Skip to content

Model catalog and pricing

The SDK never computes a price. It reports token counts, a provider, a model identifier and a timestamp, and the server turns those into a figure using an effective-dated catalog.

Pricing runs once, in the worker, as the batch is processed. The rate is selected using the call’s own timestamp, not the time the batch happened to be processed, so a delayed batch is still priced at the rate that was in force when the call was made. The result is written to the row’s cost_usd column along with the catalog’s own figure, the price row that produced it, the reasons the result is partial, and a status.

Every currency figure in the dashboard is then a sum of those stored values. The read path does no arithmetic against the catalog.

The reported provider is lowercased, then aliased.

Reported Becomes
amazon-bedrock, aws, aws-bedrock bedrock
gateway, vercel, vercel-ai-gateway The creator half of the creator/model identifier
Anything else Itself

The pair (provider, model) is looked up in the alias table. The model identifier is matched verbatim, lowercased, which is why the raw model string your code sent is kept untruncated on the row.

When the provider is unknown or missing ("", unknown, litellm, proxy, none, null) the lookup falls back to matching on the model identifier alone. That fallback succeeds only when every candidate points at the same canonical model. If several candidates disagree, the result is unpriced with the reason ambiguous_model rather than a guess.

Where one canonical model has several pricing channels, a first-party channel wins over a reseller. The order is anthropic-api, openai-api, google-ai-studio, google-api, xai-api, deepseek-api, mistral-api, amazon-api, meta-api, moonshot-api, minimax-api, zai-api, alibaba-api, aws-bedrock, aws-bedrock-geo.

When the reported provider cannot price the model, for example claude-sonnet-4-6 sent with provider: "google", the model’s direct channel is inferred from the catalog and the call is priced there. This happens only when the model maps to one canonical model with one direct channel. The row keeps its computed status and records channel_inferred, so an inferred cost is never mistaken for one priced on the channel you reported. A model that is ambiguous stays unpriced.

Prices are intervals, not values. Each row has an effective_from and an optional effective_to, and the call’s own timestamp selects the row. Two calls to the same model on different days can legitimately price differently.

Region resolution tries your deployment’s region, then *, then global.

No interval covering the timestamp means unpriced with the reason no_effective_price. The canonical model is still recorded, so you can see that the model was recognized and only the rate was missing.

Providers differ in whether the headline input count already includes cached tokens. The catalog carries that as a per-model rule rather than assuming.

Rule Effect
input_includes_cache_read Cache-read tokens are subtracted from billable input before the input rate is applied
input_includes_cache_write Cache-write tokens are subtracted as well

If the subtraction would go negative, billable input becomes zero and the reason cache_read_exceeds_input or cache_write_exceeds_input is recorded. The row is priced, but it is flagged partial so you know the arithmetic was clamped.

Rate Applied to
input_per_mtok Billable input tokens
output_per_mtok Output tokens
cache_read_per_mtok Cache-read tokens
cache_write_5m_per_mtok Cache writes explicitly attributed to a 5 minute TTL
cache_write_1h_per_mtok Cache writes explicitly attributed to a 1 hour TTL
batch_input_per_mtok Input tokens, when the call was a Batch API call
batch_output_per_mtok Output tokens, when the call was a Batch API call

All rates are USD per million tokens.

Batch rates are separate rows, not a discount factor. A call whose batch flag is true uses batch_input_per_mtok and batch_output_per_mtok if the catalog has both. If either is missing, the standard rates are used and the reason batch_rate_unavailable is recorded, so the figure is an over-estimate and says so. Cache rates are not swapped for a batch call.

Cache writes are priced by TTL. Anthropic reports 5 minute and 1 hour cache writes separately, and they have different rates. Where a provider reports only a total cache-write count, the remainder after subtracting the TTL-attributed counts is charged at the cheaper of the two available rates, and the reason cache_write_ttl_ambiguous is recorded when those rates differ. A total smaller than the sum of its parts records cache_write_details_exceed_total.

Some models charge more once the prompt passes a threshold. Where the catalog carries a long_context rule and the total input count exceeds its threshold, the input and output multipliers are applied. The multiplier also applies to cache-read and cache-write rates, because those are input-side.

The result is quantized to eight decimal places, half up.

One case overrides the catalog: an OpenRouter chat.completions call whose response carried usage.cost, with the SDK’s own fixed provenance string. That amount becomes the effective cost and the status is priced. The catalog’s own figure is kept alongside it in catalog_cost_usd.

No other reported cost overrides the catalog. An upstream inference cost from OpenRouter is retained as provenance and never combined with anything.

Every row carries one.

Status Meaning Contributes to a dashboard total?
priced The catalog produced a figure with no caveats, or a qualified gateway amount was used Yes
partial A figure was produced, but at least one reason was recorded Yes, and it may be low or high
unpriced No figure. cost_usd is null Contributes zero
reported Legacy rows only. No new row gets this status Yes

The reason list is the audit trail for a partial or unpriced row. A priced row carries a reason only when its channel was inferred.

Reason What happened
channel_inferred The reported provider could not price the model, so it was priced on the model’s sole direct channel
unknown_model No alias matched the provider and model pair
ambiguous_model The provider was unknown and the model name matched more than one canonical model
no_effective_price The model is known, but no price interval covers the call’s timestamp
catalog_unavailable The catalog could not be loaded when the batch was processed
missing_input_tokens The provider reported no input count. Treated as zero
missing_output_tokens The provider reported no output count. Treated as zero
input_rate_unavailable There are billable input tokens but no input rate
output_rate_unavailable There are output tokens but no output rate
cache_read_rate_unavailable Cache reads occurred but no cache-read rate exists
cache_write_5m_rate_unavailable 5 minute cache writes occurred but no rate exists
cache_write_1h_rate_unavailable 1 hour cache writes occurred but no rate exists
cache_write_rate_unavailable Cache writes occurred and neither TTL rate exists
cache_write_ttl_ambiguous Untagged cache writes were charged at the cheaper of two differing rates
cache_write_details_exceed_total The per-TTL counts add up to more than the reported total
cache_read_exceeds_input Cache reads exceeded the headline input count. Billable input clamped to zero
cache_write_exceeds_input Cache writes exceeded billable input. Clamped to zero
batch_rate_unavailable A batch call was priced at standard rates because no batch rate exists
uncaptured_fees The model carries charges this catalog does not model, so the figure is a floor

An unrecognized model produces no cost and lands in the unpriced coverage bucket. It is still a captured call: it counts toward the call total, toward the monthly allowance, toward your error rate, and it appears in Traces with its tokens and latency. It just contributes nothing to spend.

This is the single most common reason a spend figure looks too low, and it is why the dashboard reports coverage rather than presenting a floor as a fact.

  1. Read the coverage line under Spend by model. On Performance, when coverage is incomplete the table says A lower bound. N calls carry no price. If that line is absent, every call in the window is priced and the total is not the problem.

  2. Find the model. Set Group by to Model on the timeline, or read the Spend by model table. A model with a healthy call count and a spend of $0.00 is unpriced. The identifier shown is exactly the string your code sent.

  3. Check whether it is the model or the provider. Open a trace on that model and read the span inspector. Cost reading Unpriced with a sensible Model and Provider points at a catalog gap. A Provider that is blank, unknown or a proxy name points at an ambiguous-model resolution instead, and is fixed on your side by naming the provider when you wrap the client.

  4. Check tokens, not just cost. A model priced correctly but reporting no token counts also produces a low total. The most common cause is a streaming OpenAI chat call with METERGRAPH_PATCH_STREAM_USAGE=0, which leaves the provider reporting no usage at all. See Errors.

  5. Check whether it is a batch call. A Batch API call priced at standard rates because the catalog has no batch rate is an over-estimate, not an under-estimate. Batch API’s expected relative cost says Unavailable: the catalog has no batch price for a contributing model when this applies to a probed model.

If a model you use is missing from the catalog, that is a catalog gap on the Metergraph side rather than something you can configure. Report it with the exact model identifier from step 2.

The catalog is a database table, edited by platform administrators against provider pricing pages, with each row carrying its own source URL and the date it was last checked. It changes between releases, and a printed list here would be stale within weeks.

Check coverage in your own dashboard instead, using the four steps above. The question that matters is not “which models does Metergraph know” but “is every model my traffic uses priced”, and the coverage line answers exactly that for your workspace.

Coverage is broad across the first-party APIs of Anthropic, OpenAI, Google, xAI, DeepSeek, Mistral, Amazon, Meta, Moonshot, MiniMax, Z.ai and Alibaba, plus AWS Bedrock and the Vercel AI Gateway. That is the set of pricing channels the resolver knows how to prefer, not a guarantee that every model on each is present.

For completeness, because it explains the vocabulary above.

Table Holds
model_catalog One row per canonical model: publisher, display name, family, lifecycle (preview, active, limited, legacy, retired), release date, context window, max output, capabilities, source URL, last-checked date
model_aliases The many identifiers that resolve to one canonical model, each with its pricing channel and its per-model rules
model_prices Effective-dated rates per model, channel, region and service tier. USD only. A row must carry at least one of an input, output or cache-read rate

Editing any of these requires the platform-administrator role. There is no per-workspace price override.