Cost calculation
Learn how telemetry.dev calculates model cost from token usage and model prices.
telemetry.dev sets each span’s cost when the span is ingested:
- If the span carries a valid client cost, that value is stored as sent.
- Otherwise, the server multiplies the span’s token counts by the model’s prices from a pricing snapshot.
- If neither is possible, the cost is null, which means unknown. A known cost of zero is stored as
0.
Costs are in US dollars.
Server calculation
Snapshot prices are US dollars per 1,000,000 tokens. The server splits the span’s input into three parts and prices each part with its own rate:
| Token part | Tokens | Price field | If the price is missing |
|---|---|---|---|
| Uncached input | Input minus cache read minus cache creation | input |
— |
| Cache read | Cache-read input tokens | cachedInput |
input |
| Cache creation | Cache-creation input tokens | cacheWrite |
input |
| Output | Output tokens | output |
— |
uncached = max(0, input - cacheRead - cacheCreation)
cost = ( uncached * price.input
+ cacheRead * (price.cachedInput ?? price.input)
+ cacheCreation * (price.cacheWrite ?? price.input)
+ output * price.output
) / 1,000,000
input is the total input, including cache reads and cache creation. output includes reasoning tokens, so reasoning has no separate charge. For which attributes supply each count, see Token usage.
Worked example
This span used 12,000 input tokens, of which 8,000 were read from cache and 1,000 were written to cache, and 1,500 output tokens:
{
"gen_ai.provider.name": "anthropic",
"gen_ai.request.model": "claude-sonnet-4-5",
"gen_ai.usage.input_tokens": 12000,
"gen_ai.usage.cache_read.input_tokens": 8000,
"gen_ai.usage.cache_creation.input_tokens": 1000,
"gen_ai.usage.output_tokens": 1500
}
The prices below are illustrative. They match the anthropic:claude-sonnet-4-5 entry in the snapshot of 2026-09-24, but each new snapshot can change them.
Where the cost of this span comes from
| Token part | Tokens | Price per 1M | Cost |
|---|---|---|---|
| Uncached input | 3,000 | $3.00 | $0.009 |
| Cache read | 8,000 | $0.30 | $0.0024 |
| Cache creation | 1,000 | $3.75 | $0.00375 |
| Output | 1,500 | $15.00 | $0.0225 |
| Total | 13,500 | $0.03765 |
The stored cost is 0.03765. Adding reasoning tokens to this span would not change it, because they are already counted in the 1,500 output tokens.
Rounding and precision
The server works with exact decimal values of the prices, not floating-point arithmetic. It rounds the final cost once, to the nearest 0.00000001 dollars (eight decimal places), and drops trailing zeros. For example, 7 input tokens at $3.00 per million is stored as 0.000021.
A stored cost can have at most ten digits before the decimal point. A calculated cost above $9,999,999,999.99999999 is null.
Client cost
A client cost replaces the server calculation. Use it for a custom or private model, for negotiated prices, or when the snapshot does not know your model. It does not need a model or token counts.
The ingest API checks these attributes in order and uses the first valid one:
gen_ai.usage.costai.usage.costtd.cost_usdtelemetry.dev.cost_usd
A valid client cost is one of:
- A finite, nonnegative number. The server rounds it to eight decimal places.
- A string of digits with an optional decimal part of one to eight digits, such as
"0.5"or"12.00000001". Strings with a sign, an exponent, or more than eight decimal places are invalid.
Either way, the value can have at most ten digits before the decimal point. An invalid value is skipped, and the next attribute in the list is tried.
The first-party SDKs send costUsd (TypeScript) and cost_usd (Python) as gen_ai.usage.cost.
When cost is null
The server stores a null cost, and adds nothing to the trace total, when there is no valid client cost and any of these is true:
- The span has no model attribute.
- No snapshot entry matches the provider and model; see Model matching.
- The span has no token counts at all.
- The span has an input count, and cache read plus cache creation is larger than it. The ingest API also records the diagnostic
inconsistent_usage_counts. - The model has context tiers and the span reports no input count of its own.
- The modality counts contradict the aggregate counts; see Modality pricing.
- A one-hour cache-creation count or a compaction count is larger than the total it is part of; see Provider-specific adjustments.
- The result is larger than $9,999,999,999.99999999.
If a model is missing from the snapshot or priced wrong for you, send a client cost.
Modality pricing
Some providers price image and audio tokens differently from text. When a span carries modality usage attributes, the server prices the reported image and audio parts with their own rates, and the rest of each aggregate at the text rate:
| Part | Price field | If the price is missing |
|---|---|---|
| Image input, uncached | imageInput |
input |
| Audio input, uncached | audioInput |
input |
| Image cache read | imageCachedInput |
cachedInput, then input |
| Audio cache read | audioCachedInput |
cachedInput, then input |
| Image output | imageOutput |
output |
| Audio output | audioOutput |
output |
| Text and any remainder | input, cachedInput, output |
As in the main table |
A modality’s uncached input is its input count minus its own cache-read count. Two cases keep a modality’s uncached input at the base input rate instead:
- The span has a cache-read count above zero but no cache-read count for that modality. Send the modality’s cache-read count, even
0, to avoid this. - The span has any cache creation. The server cannot tell which modality the cache writes belong to, so it does not split them.
Reported image and audio cache reads still use their own cache-read rates in both cases, and cache creation still uses cacheWrite.
When a span has no aggregate count for a kind, the server uses the sum of its modality counts for that kind.
The cost is null when the modality counts contradict the aggregates:
- Image plus audio uncached input, at their modality rates, is larger than the total uncached input.
- The modality cache reads add up to more than the cache-read total, or a modality’s cache read is larger than its own input.
- Image plus audio output is larger than the output total.
These are token-cost estimates. Snapshot entries without image or audio rates price every modality at the text rate. The calculation does not include per-image generation fees, service-tier surcharges, or other charges that a provider bills outside token counts.
Context tiers
Some models charge more for every token once a request’s input is larger than a threshold. For these models, the total input count, including cache reads and cache creation, selects the rates for the whole request. The server does not charge only the tokens above the threshold at the higher rate.
A tiered model has a null cost when the span reports no input count of its own, because the server cannot pick a tier. An input attribute or modality input counts are enough, and so is the total on an embeddings or rerank span. An input that the ingest API filled in from cache counts, or from the total on another kind of span, is not.
Provider-specific adjustments
These adjustments come from Claude Platform and Claude Code telemetry:
| Adjustment | Applies when | Effect |
|---|---|---|
| One-hour cache writes | The span has anthropic.usage.cache_creation.ephemeral_1h_input_tokens |
That part of cache creation is priced at cacheWrite1h, falling back to cacheWrite and then input. The rest of cache creation uses cacheWrite. |
| Fast mode | A Claude Code claude_code.llm_request span has speed set to fast, and the snapshot entry has a fast-mode multiplier |
Every rate is multiplied by the entry’s multiplier. |
| Compaction | An Anthropic SDK request span has anthropic.usage.compaction.input_tokens or anthropic.usage.compaction.output_tokens |
The compaction tokens are added to the span’s input and output, and priced as a separate request with all input uncached. They do not change the main request’s context tier. |
Pricing snapshot
The server reads prices from a pricing snapshot that telemetry.dev updates from the four catalogs below. It does not call any catalog while it ingests a span, so a price change reaches telemetry.dev with the next snapshot, not immediately. The set of priced models can change between snapshots.
| Source | Role |
|---|---|
| models.dev | Primary prices for direct and hosted providers |
| LiteLLM | Fills provider and model entries that models.dev lacks. For entries models.dev has, fills only image and audio rates, one-hour cache-write rates, and fast-mode multipliers. |
| Vercel AI Gateway | Prices for Vercel AI Gateway routes only |
| OpenRouter | Prices for OpenRouter routes only |
Gateway routes are priced only from their own catalog. A Vercel AI Gateway or OpenRouter route missing from that catalog has no price, even if another provider prices the same model.
Model matching
Before the lookup, the server trims and lowercases the model, and removes these Google Vertex prefixes:
projects/<project>/locations/<location>/publishers/google/models/publishers/google/models/models/
It then trims and lowercases the provider and maps these names:
| Received provider | Lookup provider |
|---|---|
gcp.vertex_ai, vertex, vertex_ai |
google-vertex |
gcp |
google |
aws, bedrock, bedrock_converse |
amazon-bedrock |
azure.ai.inference |
azure_ai |
gateway, vercel_ai_gateway, vercel ai gateway |
vercel |
fireworks_ai |
fireworks-ai |
ibm.watsonx.ai |
watsonx |
mistral_ai |
mistral |
together_ai |
togetherai |
x_ai |
xai |
Any other name that contains . |
The part before the first ., then the mappings above |
The lookup tries these model forms, in order:
- The model as received.
- The model without a version or date suffix: a
-or@followed byYYYY-MM-DD,YYYYMMDD,vN,latest, or three digits at the end. - The Anthropic form, which changes a leading
claude-N-M-toclaude-N.M-. - The Anthropic form without a version or date suffix.
For example, claude-3-5-sonnet-20241022 is tried as claude-3-5-sonnet-20241022, claude-3-5-sonnet, claude-3.5-sonnet-20241022, and claude-3.5-sonnet.
The server first looks up provider:model for each form. If none matches, or the span has no provider, it looks up each form without a provider. That lookup uses the entry from the first provider in this order that prices the model: openai, anthropic, google, google-vertex, xai, deepseek, mistral, meta, meta-llama, cohere, groq, amazon-bedrock, azure, then all other providers in alphabetical order.
The vercel and openrouter providers skip the lookup without a provider, because gateway routes are priced only from their own catalog.
The snapshot also has an entry for each undated model family, such as gpt-4o, that points to the family’s -latest entry or, without one, its newest dated version, unless the catalog already lists the family name.
Trace totals
A trace’s cost is the sum of its spans’ costs. It stays null until at least one span has a cost, so a trace with no priced spans shows no cost rather than $0.
For integrations that report the same usage on more than one span, such as OpenClaw, Eve, and Claude Platform, the server counts that usage once in trace totals.