What you need to know before you read on
There is a difference between reducing your LLM bill and understanding it, and most teams have never done the second. This guide is about the second. Related guides on this site cover how to cut spend with caching, routing and compression; this one is about how to measure and attribute it — so that when the invoice arrives you can say exactly which feature, which team and which customer generated every rupee and every pound of it. Here is the shape of what follows.
- Total-bill blindness is the real problem — you cannot optimise or price what you cannot attribute, and one big provider invoice tells you nothing about where the money went.
- Tag every request across six dimensions — feature, team, customer/tenant, environment, model and user-tier — and price the tokens per class, because cached reads and reasoning tokens are billed differently from fresh input and output.
- Capture it in one of three places — provider request metadata, an LLM gateway, or OpenTelemetry spans feeding a warehouse — and enforce a required tag schema wherever you choose.
- Turn cost into unit economics — cost per active user, per resolved ticket, per successful task — and use gross margin per feature to set prices, caps and regression alerts.
- Mind the dual-market details — the bill is in USD but you may charge INR or GBP, and where you store usage logs matters for DPDP and UK GDPR.
Before writing any code, agree on the tag schema as a team and make it mandatory. A cost dimension you did not tag from day one is one you can never backfill — the tokens are already spent and the provider does not remember which feature sent them. Attribution is a write-time decision, not a read-time one.
Why total-bill blindness is the problem
The default state of an AI product is a single, undifferentiated invoice. You wired an SDK into a prototype, shipped a few features on top of it, and every one of them now bills to the same account. At the end of the month a number arrives from OpenAI or Anthropic — say it doubled since the quarter before — and nobody in the room can explain the increase, because the invoice is just a total. That is total-bill blindness, and it quietly caps how well you can run the product.
It caps optimisation first. If your bill went up 40% you cannot tell whether a single runaway feature caused it, a new customer onboarded a heavy workload, or a background job started retrying in a loop. Without attribution every investigation is archaeology. It caps pricing next: a SaaS product cannot set a sensible per-seat or per-tenant price when it has no idea what a tenant costs to serve, and it certainly cannot spot the whale customer whose usage is eating the margin of the other hundred. And it caps prioritisation, because "which feature should we make cheaper?" has no answer when every feature looks identical on the bill.
The one-big-invoice trap is seductive because the provider console gives you just enough to feel informed — a spend graph, maybe a per-model breakdown — while hiding the dimension that actually matters, which is your product structure. The provider does not know what a "feature" or a "tenant" is in your application. Only you can attach that meaning, and only at the moment you make the request. Everything in this guide follows from that single fact.
The attribution model: dimensions and token classes
Attribution has two halves. The first is which dimensions you tag — the labels that let you slice the bill. The second is how you price the tokens — because a token is not a token, and a naive rate will misstate the cost. Get both right and every request becomes a row you can group, filter and sum.
Tag every request with at least these six dimensions:
- Feature / endpoint — the product surface that made the call:
summarise_ticket,chat_assistant,doc_extract. This is the dimension product managers care about most. - Team — the internal owner, so engineering leadership can run showback across squads.
- Customer / tenant — the account the request was serving, which is the basis of any per-tenant price, cap or chargeback.
- Environment —
prod,staging,dev. Untagged non-prod spend is a classic source of mystery cost. - Model — the exact model id, because pricing is per model and a routing change silently shifts the mix.
- User-tier — free, pro, enterprise. This is what turns raw cost into margin, because it pairs spend with the plan that is meant to cover it.
Now the pricing. The cost of a single request is not tokens times one rate; it is the sum over token classes, each at its own price. As of July 2026 a frontier request can carry four distinct classes that bill differently: fresh input, cached-read input (billed at a steep discount, often a fraction of the fresh rate), output, and reasoning or thinking tokens on the models that expose them. Reasoning tokens are the sharp edge: the user never sees them, but they are billed as output, so a feature that leans on extended thinking can cost several times what its visible output suggests. The formula you want, per request, is:
cost = (fresh_input_tokens * rate.input)
+ (cached_read_tokens * rate.cached_input)
+ (output_tokens * rate.output)
+ (reasoning_tokens * rate.output) # billed as output on most models
Every provider returns these counts on the usage object of each response — read them from there rather than estimating from string length, which is always wrong. The rates belong in a per-model price table you keep in configuration, never hardcoded, because provider prices change and you do not want a deploy to be the thing standing between a price cut and a corrected bill. If you are already thinking about how caching changes these numbers, our companion guide on caching, routing and compression covers the reduction side; here the point is simply to account for each class honestly.
Counting only prompt (input) tokens is the single most common attribution bug. Output is usually the more expensive class, reasoning tokens are billed as output while being invisible, and cached reads must be priced at the discounted rate or you will overstate cheap traffic and understate the expensive kind. If your cost model uses one blended rate, it is wrong in both directions at once.
How to capture it: metadata, gateway or OpenTelemetry
You have three places to attach tags and record token usage, and they are not mutually exclusive — mature setups combine them. The right starting point depends on how your traffic is shaped and how much plumbing you already have.
The first option is provider request metadata. Both Anthropic and OpenAI accept a per-request metadata field, so you can stamp a user_id or a small set of tags onto each call and have them appear in the provider's own usage reporting and exports. It is the lowest-effort start, but the tag vocabulary is limited and you are reading your cost back out of the provider's console rather than your own warehouse.
The second, and the one most teams settle on, is an LLM gateway that every call passes through — LiteLLM, Portkey, OpenRouter or Helicone. The gateway sees all traffic, so it can attach tags, compute cost per request from its own price table and aggregate spend by any dimension you send it. Because it is a single chokepoint, it is also where you enforce the tag schema. If you are choosing between them, our guide to LLM gateways compares the options in depth.
The third is OpenTelemetry spans plus a warehouse. You emit a span for every model call with the tags and token counts as span attributes, ship them through your existing observability pipeline, and query cost in the same warehouse you already use for everything else. It is the most work to set up but the most flexible, and it unifies cost with latency and error data — the wider practice we cover in agent observability with OpenTelemetry.
| Where to attribute | Pros | Cons | Effort |
|---|---|---|---|
| Provider request metadata | Zero new infrastructure; shows up in the provider's own usage export | Limited tag vocabulary; data lives in the provider console, not your warehouse; hard to join with product data | Low |
| LLM gateway (LiteLLM / Portkey / OpenRouter / Helicone) | Single chokepoint tags, prices and aggregates every call; enforce the tag schema in one place; provider-agnostic | Everything must route through it; a component to run or a vendor to trust; async/batch jobs can bypass it if you let them | Medium |
| OpenTelemetry spans + warehouse | Most flexible; cost joins latency, errors and product data; owns your data end to end | Most plumbing; you build the price model and rollups yourself | High |
Whichever you choose, the capture logic is small. Here is a thin Python middleware that wraps a provider call, stamps the tags onto the request metadata, prices the response by token class from a config-driven table, and logs one usage row to a warehouse. It is deliberately provider-neutral in shape so the same pattern works behind a gateway or standalone.
import time
import uuid
from anthropic import Anthropic
client = Anthropic()
# Prices live in CONFIG, never hardcoded — providers change them. USD per token.
# Keep one entry per model, per token class. As of July 2026 these are illustrative
# placeholders; load the real numbers from a config store you can edit without a deploy.
PRICES = {
"claude-model-x": {
"input": 5.00 / 1_000_000,
"cached_input": 0.50 / 1_000_000, # cached reads are much cheaper
"output": 25.00 / 1_000_000, # reasoning tokens bill as output
},
}
REQUIRED_TAGS = {"feature", "team", "tenant", "environment", "user_tier"}
def priced_call(model, system, messages, tags, warehouse):
# Enforce the tag schema at the boundary — reject untagged traffic.
missing = REQUIRED_TAGS - tags.keys()
if missing:
raise ValueError(f"blocked: request missing required tags {missing}")
started = time.time()
resp = client.messages.create(
model=model,
max_tokens=1024,
system=system,
messages=messages,
# Provider metadata: Anthropic & OpenAI both accept a per-request field.
metadata={"user_id": tags["tenant"]},
)
# Read exact counts from the usage object — never estimate from length.
u = resp.usage
fresh = getattr(u, "input_tokens", 0)
cached = getattr(u, "cache_read_input_tokens", 0)
output = getattr(u, "output_tokens", 0)
# reasoning_tokens, when the model exposes them, are already inside output.
p = PRICES[model]
cost = (fresh * p["input"]
+ cached * p["cached_input"]
+ output * p["output"])
# One immutable usage row per call -> the grain of all showback queries.
warehouse.insert("llm_usage", {
"id": str(uuid.uuid4()),
"ts": time.time(),
"latency_ms": int((time.time() - started) * 1000),
"model": model,
"feature": tags["feature"],
"team": tags["team"],
"tenant": tags["tenant"],
"environment": tags["environment"],
"user_tier": tags["user_tier"],
"input_tokens": fresh,
"cached_tokens": cached,
"output_tokens": output,
"cost_usd": cost,
})
return resp
With rows landing in a llm_usage table at the grain of one per call, showback is just SQL. This query rolls up a month's spend by feature and by tenant, splits input, cached and output tokens so you can see where the cost sits, and orders the biggest spenders first — the report you send to each team.
SELECT
feature,
tenant,
COUNT(*) AS calls,
SUM(input_tokens) AS input_tokens,
SUM(cached_tokens) AS cached_tokens,
SUM(output_tokens) AS output_tokens,
ROUND(SUM(cost_usd), 2) AS cost_usd
FROM llm_usage
WHERE environment = 'prod'
AND ts >= EXTRACT(EPOCH FROM DATE_TRUNC('month', NOW()))
GROUP BY feature, tenant
ORDER BY cost_usd DESC;
Every article here is written by a Verified Builder. Want your name on the next one?
AI Tech Connect lists AI engineers, founders and researchers across India and the UK — and the people hiring browse it to find them. Adding your profile is free.
Become a Verified Builder →Turning cost into unit economics
Attribution is the plumbing; unit economics is the point. A per-feature cost total is useful, but the number that changes decisions is cost per outcome — and choosing the right outcome is most of the work. Cost per token is an engineering metric; cost per resolved ticket is a business one, and only the second tells you whether a feature makes money.
Pick the denominator that matches the product surface. For a chat assistant it is cost per active user: the feature's monthly spend divided by the users who touched it. For a support-deflection bot it is cost per resolved ticket — spend divided by the tickets it actually closed without a human, which is the outcome the business is buying. For an agent it is cost per successful task or per completed workflow, and here the accounting must include failed and retried runs, because an agent that costs a little per step but retries three times to succeed has a very different unit cost from one that succeeds first time. Multi-agent systems make this especially slippery, since one user request can fan out into dozens of model calls; we go deeper on that in cost-optimising multi-agent systems.
Once you have cost per outcome and you already tag user-tier, you have gross margin per feature: revenue attributable to the tier minus the LLM cost of serving it. That single figure drives three decisions. It tells you whether to set a per-tenant price or a usage cap — if a handful of enterprise tenants generate most of the cost, a metered price or a fair-use cap protects the margin of everyone else. It tells you which feature is worth an optimisation sprint, because you can see which one has the thinnest margin. And it gives you the baseline for cost-per-outcome regression alerts: when a prompt tweak or a model swap doubles the cost per resolved ticket while the ticket count stays flat, you want a page, not a surprise at month-end. For the broader FinOps framing of these numbers, our news explainer on AI inference cost economics in 2026 is a useful companion.
Showback, chargeback and tagging governance
Attribution unlocks two operating models, and the distinction matters. Showback reports cost: each team, feature or customer sees what its LLM usage generated, and that visibility alone changes behaviour — engineers optimise what they can see. Chargeback goes further and moves money, billing the cost back to a team's budget or a customer's invoice. The sensible path is to start with showback everywhere, because it needs only accurate attribution, and graduate to chargeback on the dimensions where the numbers are trusted and a billing relationship already exists — most naturally per-tenant usage in a metered product.
Both depend on the same foundation: FinOps discipline applied to AI, which in practice means the same tagging governance cloud teams learned years ago. The core rule is that a required tag schema is enforced at the gateway, so a request that arrives without its feature, team, tenant, environment and user-tier tags is rejected or flagged rather than quietly logged into an "unattributed" bucket. That bucket is where cost discipline goes to die: once it exists and is tolerated, it grows, and every untagged rupee or pound is spend nobody owns. Enforcement at write time — the REQUIRED_TAGS check in the middleware above — is what keeps it empty.
Make "unattributed" a monitored metric, not a silent default. Chart the share of spend that fails the tag schema each day and alert when it rises above a threshold you can live with, say 2%. A creeping unattributed line is the earliest signal that a new code path is bypassing your gateway.
Dual-market notes: FX, margin and where logs live
Two details bite teams serving both India and the UK. The first is currency. Your provider bill is almost always in USD, but you may charge customers in INR or GBP, which means your margin moves with the exchange rate even when nothing about the product changes. Store cost in USD as the source of truth in your usage table, and convert to the charging currency at reporting time using a dated rate — never bake a single conversion into the logged rows, or a month of FX drift will quietly distort every margin number. A tenant that looked profitable in April can slip underwater by July on FX alone if you priced in INR against a USD cost and the rate moved.
The second is where the logs live. Cost attribution needs only metadata — feature, tenant id, model, token counts, timestamp — and that metadata rarely contains personal data, so it can sit centrally in your warehouse for analysis without much concern. Raw prompt and completion text is a different matter: it may contain personal data, and under India's DPDP Act and UK GDPR the residency and retention rules for that content depend on the customer it belongs to. The pragmatic pattern is to separate the two streams — cheap metadata metering everywhere, sensitive prompt logs treated with their own residency and retention policy — rather than dumping everything into one table. This is a pointer, not legal advice; confirm your obligations with counsel and see any data-residency guidance specific to your regions.
Common pitfalls that break attribution
Attribution fails in predictable ways. Each of these turns a clean cost model into a misleading one, and all of them are avoidable if you know to look.
- Untagged background jobs. The interactive path gets tagged carefully; the nightly batch summariser, the cron re-indexer and the async webhook handler get forgotten, and their spend lands in the unattributed bucket. Route every code path — including jobs — through the same tagged gateway.
- Ignoring cached-token accounting. Pricing cached reads at the fresh input rate overstates the cost of your most efficient traffic and hides the value of caching from anyone reading the report. Price each token class at its own rate.
- Attributing only prompt tokens. Output is usually the dominant cost and reasoning tokens are invisible output; a model that counts only input will understate expensive features and misrank your optimisation targets.
- Double-counting retries. When a call fails and you retry, both attempts spend tokens and both must be logged — but make sure your outcome denominator counts the task once, or cost per task will look artificially high or low depending on how you net them.
- Batch and async jobs escaping the gateway. Bulk and offline pipelines are exactly the workloads that call the provider SDK directly for speed, and they are often the biggest spenders. If they bypass the chokepoint, your biggest costs are the least attributed.
The failure mode that hurts most is the one you cannot see: a background job that skips the gateway does not error, it just silently spends untagged tokens. That is why the unattributed-spend metric matters — it is the only signal that catches a bypass before the finance team does.
Where to start
You do not need the full stack on day one. Agree the six-dimension tag schema, put it behind a gateway or the thin middleware above, and start with showback — a weekly report of cost by feature and by tenant is enough to change conversations immediately. Add token-class pricing so cached and reasoning tokens are honest, then layer cost per outcome once the raw attribution is trusted. Chargeback, per-tenant caps and regression alerts follow naturally once the numbers are ones people believe. The whole thing rests on one habit: tag every request, at the moment you make it, without exception. Everything else is arithmetic.