AI agents

How to Attribute Token Spend to Features and Teams

Antonio Evans8 min read

A clean isometric diagram of a request path: a user action box on the left flows into an API gateway, then to a model provider box on the right.
Key points

Key points

  1. 1Token spend is attributable only if every model call carries tags (feature, team, customer, environment, model, tokens, timestamp) at request time, not reconstructed later from provider invoices.
  2. 2Log one row per call to a cost table and multiply input and output tokens by the provider's per-token rate for that model to get a dollar figure per request.
  3. 3Three dashboards cover most decisions: cost by feature per day, cost per successful request (unit cost), and cost by team for chargeback.
  4. 4Read the numbers before you cut: separate retries, retry storms and context bloat from genuine usage growth, or you will optimize the wrong line item.

Build the metadata schema before you touch a model

The provider invoice tells you that spend went up. It cannot tell you whether the increase came from one customer's agent loop, a retry pattern during an outage, or a prompt change that added hundreds of tokens to every chat session. That gap is the whole reason to instrument, and it is the first failure mode teams hit when they try to attribute cost from the invoice alone (Braintrust).

The fix is a fixed schema applied at the call site. A practical minimum set is: feature_id, team, customer_id (or tenant), environment, model, input_tokens, output_tokens, cached_tokens, prompt_version, timestamp, latency_ms, and a success flag. Each field earns its place:

  • feature_id makes spend comparable across product surfaces and is what your primary dashboard groups by.
  • team is the chargeback key and should map to an existing cost center, not a Slack channel name.
  • customer_id covers multi-tenant B2B products where billing rolls up to organizations rather than seats (Braintrust).
  • environment keeps staging traffic out of production totals; a load test in dev should never appear in a team's bill.
  • prompt_version pins the template so a prompt change that quietly adds 500 tokens shows up as a cost move, not a mystery.
  • success is what lets you compute cost per resolved task rather than cost per call.

Keep cardinality in mind. Low-cardinality fields such as feature, environment, model and prompt version are safe to chart as separate series. High-cardinality fields such as customer_id and agent_run_id are fine to store and filter on but will make a chart unusable if you draw a line per value (Braintrust). The working rule: alert and chart on the cross-product of feature and model; filter by customer only when investigating.

Where to capture the metadata

There are three capture points and they are not mutually exclusive.

SDK wrapper (start here). One function wraps your model client, injects the tags from the calling context and writes the row. It is the smallest change and the most portable: the tagging happens at the call site, where the feature label is still known, rather than in raw logs where the call appears as an opaque provider request with only model and token count (Braintrust). The downside is discipline: any code path that bypasses the wrapper is invisible.

API gateway or proxy. Every request passes through one control point, so tagging and budget controls apply across all providers and all services without reimplementing logic per application (Maxim AI). Gateways built on the OpenAI-compatible pattern accept per-request metadata objects and project a curated subset to metric labels, keeping high-cardinality fields on the trace only (TrueFoundry). The cost is that the application must still send the feature label; the gateway cannot invent it.

Provider usage APIs. Provider-side usage and cost endpoints aggregate by API key or project and support grouping by model, project and API key (OpenAI cookbook). Use these for reconciliation, not attribution. They are the source of truth for what you were charged; your cost table is the source of truth for why.

A sensible split: gateway for enforcement and cross-provider normalization, SDK wrapper for business tags, provider API for a nightly reconciliation job that compares your computed total against the invoice and flags drift above a tolerance you set (a few percent is a common starting point).

Store it as one row per call

Do not parse text logs after the fact. Write a structured row per call to a table designed for aggregation. The pipeline pattern that works at startup-to-mid scale is streaming pre-aggregation: traces to minute counters to hourly rollups to daily rows keyed by team, app and model, with ClickHouse or TimescaleDB comfortably handling it on a single VM (TrueFoundry).

A minimal schema in SQL:

CREATE TABLE llm_calls (
  ts            TIMESTAMP NOT NULL,
  request_id    TEXT,
  feature_id    TEXT NOT NULL,
  team          TEXT NOT NULL,
  customer_id   TEXT,
  environment   TEXT NOT NULL,
  model         TEXT NOT NULL,
  prompt_version TEXT,
  input_tokens  INT NOT NULL,
  output_tokens INT NOT NULL,
  cached_tokens INT DEFAULT 0,
  latency_ms    INT,
  success       BOOLEAN NOT NULL,
  cost_usd      NUMERIC(12,6) NOT NULL
);

Cost is computed at span close from usage attributes and a versioned pricing table, because cache reads, cache writes and image or audio inputs each carry a separate price line (TrueFoundry). Store cost_usd rather than computing it in every query: pricing changes, and you want historical rows to keep the price that applied when they were written. Keep the pricing table itself in version control with an effective date, so a re-run is reproducible.

The formula per call is simple:

cost = input_tokens   * input_rate
     + output_tokens  * output_rate
     + cached_tokens  * input_rate * cache_multiplier

where cache_multiplier reflects the provider's cache read discount, which is substantial: Anthropic bills cache reads at 0.1x the input rate and OpenAI at roughly 0.5x on cached input (Morph, Worklytics). If you ignore the cached and uncached split you will systematically overstate spend on cache-heavy workloads and understate the value of adding caching.

Three dashboards, and what each one is for

Build these in this order. Each answers a different question and each has a specific way of misleading you.

DashboardGrainQuestion it answersRead it like thisCommon trap
Cost by feature, dailyfeature x dayWhere is the money going?Look at the top 5 features and the day-over-day delta, not the totalA new feature's launch week looks like a spike; baseline after 7 days
Unit costcost per successful request, feature x dayAre we getting more expensive per outcome?Trend, not level; compare against a fixed reference weekAveraging over retries hides them; exclude success = false and count separately
Team chargebackteam x monthWho pays, and how much?Compare to the budget each team agreed toShared platform cost with no owner silently lands on the largest team

Cost by feature per day. Sort descending, show the top 10 and roll the rest into "other." The insight is rarely the number; it is the slope. A feature whose cost grows 20 percent week over week while its request count is flat is getting more expensive per request, which points at context growth, a prompt change, or a model swap.

Cost per successful request. This is the metric to optimize, because an agent that solves a problem in one expensive call can be cheaper than one that grinds through ten cheap ones (BigData Boutique). Track it per feature, per prompt version, and per model. A fall in unit cost after a routing change is the evidence you need to keep the change.

Cost by team for chargeback. Monthly grain, tied to agreed budgets. Split shared platform spend (evaluation runs, shared retrieval, gateway overhead) explicitly and in writing, or it defaults to whoever has the most traffic.

A worked example makes the unit economics concrete. Suppose a support classification feature handles 1,000,000 requests a month, averaging 800 input tokens and 120 output tokens per call on a mid-tier model priced at $1.00 per million input and $5.00 per million output, figures read from the pricing table in Worklytics. Input is 800,000,000 tokens, or $800. Output is 120,000,000 tokens, or $600. Total $1,400 a month, or $0.0014 per request. If a prompt change pushes input to 1,200 tokens, input cost becomes $1,200 and the feature costs $1,800: a 29 percent rise with zero change in request volume. Without per-request tagging that $400 would surface only as a slightly larger invoice line.

Read the numbers without fooling yourself

Attribution dashboards fail in predictable ways. These are the guardrails that keep the analysis honest.

Count retries as their own line. Transient provider errors trigger SDK retries with exponential backoff; the application logs the successful response, so retry tokens may never surface in application logs (Braintrust). Token spend hides in four places: retries, long context, verbose output and over-large models. Track each as its own line item so a retry storm during an upstream incident appears as a distinguishable spike rather than a gradual trend (BigData Boutique).

Separate input from output. Output tokens cost more on most models, so a prompt that triggers a 3,000-token response is a different problem from one that produces 50 tokens (NeuralTrust). A blended "cost per call" hides the difference between a verbosity problem and a context problem.

Watch compounding, not just totals. Every unnecessary token in an agent conversation is paid for on every subsequent turn: 100 wasted tokens in turn one of a 30-turn session costs 3,000 tokens overall, which at $5 per million input tokens is $0.015 of waste per session, or $450 a month across 1,000 sessions a day (Morph). Feature-level totals will show this as steady growth; unit cost per request is where you catch it.

Reconcile against the invoice monthly. Your computed total should land within a few percent of the provider bill. Larger gaps mean untagged calls (a script using a raw key, an evaluation job, a notebook someone left running) and those gaps are themselves findings.

Do not attribute shared infrastructure to the loudest feature. Caching, routing and evaluation runs are shared costs. The gateway's own budget and usage controls belong at the platform layer, not folded into a product feature's unit cost, because doing so makes every optimization decision look better or worse than it is (Maxim AI).

Treat attribution as continuous, not a one-off audit. Inference prices fall while consumption rises, so the cost curve bends upward unless something actively controls it; the same cost-accountability loop FinOps applies to cloud applies to tokens (Maxim AI). A monthly review of the three dashboards is the cheapest control you have.

Once the table exists, the optimization levers become measurable rather than theoretical: model routing, semantic caching, output limits, batching at the provider's discounted asynchronous rate, and context compaction each move a specific line on a specific dashboard (Morph, NeuralTrust). Attribution first, then cuts, then verification on the same unit-cost chart. If you are running several providers and would rather spend the engineering time on features than on per-provider instrumentation, Quanta Inference exposes text, image, video, audio and embedding models through one OpenAI-compatible key with prepaid USD pricing, which simplifies the pricing table your cost job has to maintain.

A rollout sequence that ships in a week

  1. Day 1: define the schema and a pricing table. Pick the fields, name the owner of each tag, and load per-model input, output and cache rates into a small table with effective dates.
  2. Day 2: wrap the client. One function that takes a context object (feature, team, customer, environment, prompt version), calls the model, and writes the row. Fail open: a logging error must never break a user request.
  3. Day 3: backfill from provider usage APIs for the current billing period so the first dashboard is not empty, grouping by model and project to reconstruct what you can (OpenAI cookbook).
  4. Day 4: build the feature-by-day chart and the unit cost chart. Keep both to lines and tables; resist the pie chart.
  5. Day 5: reconcile. Compare your total to the invoice, list and name every gap, and tag the scripts and jobs that caused them.
  6. Week 2: add the chargeback view and soft budget alerts per team at a threshold the team agrees to, then a hard cap only after a soft alert has fired at least once without a false positive.

The order matters. Chargeback before reconciliation produces arguments about numbers nobody trusts; unit cost before feature totals produces optimization of the wrong thing.

FAQ

FAQ

What is the minimum set of fields to log per model call?

Log feature ID, team, customer ID, environment, model name, input tokens, output tokens, timestamp and a success flag. Add prompt version and cached tokens as soon as you can, because they are what let you explain a cost change and price cache hits correctly (Braintrust, Worklytics).

Should I tag at the SDK, the gateway, or both?

Both, with different jobs. The SDK wrapper carries business tags (feature, team, customer) that only the calling code knows; the gateway enforces budgets, normalizes across providers and applies controls without per-app changes (Maxim AI). Provider usage APIs are a reconciliation source, not an attribution source (OpenAI cookbook).

Why is cost per call a worse metric than cost per successful request?

Cost per call rewards short, cheap interactions even when they fail and get retried, while an agent that resolves a task in one expensive call can be cheaper than one that grinds through ten cheap ones (BigData Boutique). Filtering to successful requests and dividing total cost by their count keeps the metric tied to the outcome the business pays for.

How do I stop retries from distorting the attribution?

Write a row for every attempt, not every final response, and flag retries explicitly, because SDK-level retries after provider errors can leave application logs showing only the successful call (Braintrust). Then chart retry cost as its own series so an outage-driven spike is visibly different from an organic trend (BigData Boutique).

When can I trust the numbers enough to start cutting?

When your computed total reconciles to the provider invoice within a tolerance you have set, and each of your top features has at least a full billing period of tagged data. Cutting before that is guesswork: most teams that optimize without a visibility layer find the invoice barely moves, because they never knew which part of the system was expensive (NeuralTrust).

Sources

Sources

  1. How to track LLM costs (2026): A playbook for per-user, per ...
  2. LLM Cost Optimization: A Guide to Cutting AI Spending Without ...
  3. LLM Cost Attribution and Team Budget Tracking
  4. How to use the Usage API and Cost API to monitor your ...
  5. LLM Cost Optimization: 5 Levers to Cut API Spend 70-85% | Morph
  6. How to Track LLM Token Usage and Cost | Guide - Worklytics
  7. LLM Cost Optimization Techniques: A Production Playbook
  8. Token Usage Monitoring: Track, Attribute, and Optimise AI Spend
  9. 5 Tools for Reducing LLM API Costs in Production (2026) - Maxim AI
  10. LLM Batching & Async Inference: Cut Costs on High-Volume AI
About the author

About the author

Antonio Evans
Antonio Evans

Antonio Evans is the founder of Quanta Intelligence and a fractional CTO who builds HIPAA-ready AI systems for health and life-science companies. Before that he was technical co-founder of FriendsEAT for twenty years, CEO of Socialdraft, and a data engineer at IBM. Antonio lives with his dog Santino in Lisbon, Portugal.

All articles