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.

ReportedBecomes
amazon-bedrock, aws, aws-bedrockbedrock
gateway, vercel, vercel-ai-gatewayThe creator half of the creator/model identifier
Anything elseItself

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.

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.

RuleEffect
input_includes_cache_readCache-read tokens are subtracted from billable input before the input rate is applied
input_includes_cache_writeCache-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.

RateApplied to
input_per_mtokBillable input tokens
output_per_mtokOutput tokens
cache_read_per_mtokCache-read tokens
cache_write_5m_per_mtokCache writes explicitly attributed to a 5 minute TTL
cache_write_1h_per_mtokCache writes explicitly attributed to a 1 hour TTL
batch_input_per_mtokInput tokens, when the call was a Batch API call
batch_output_per_mtokOutput 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.

StatusMeaningContributes to a dashboard total?
pricedThe catalog produced a figure with no caveats, or a qualified gateway amount was usedYes
partialA figure was produced, but at least one reason was recordedYes, and it may be low or high
unpricedNo figure. cost_usd is nullContributes zero
reportedLegacy rows only. No new row gets this statusYes

The reason list is the audit trail for a partial or unpriced row.

ReasonWhat happened
unknown_modelNo alias matched the provider and model pair
ambiguous_modelThe provider was unknown and the model name matched more than one canonical model
no_effective_priceThe model is known, but no price interval covers the call’s timestamp
catalog_unavailableThe catalog could not be loaded when the batch was processed
missing_input_tokensThe provider reported no input count. Treated as zero
missing_output_tokensThe provider reported no output count. Treated as zero
input_rate_unavailableThere are billable input tokens but no input rate
output_rate_unavailableThere are output tokens but no output rate
cache_read_rate_unavailableCache reads occurred but no cache-read rate exists
cache_write_5m_rate_unavailable5 minute cache writes occurred but no rate exists
cache_write_1h_rate_unavailable1 hour cache writes occurred but no rate exists
cache_write_rate_unavailableCache writes occurred and neither TTL rate exists
cache_write_ttl_ambiguousUntagged cache writes were charged at the cheaper of two differing rates
cache_write_details_exceed_totalThe per-TTL counts add up to more than the reported total
cache_read_exceeds_inputCache reads exceeded the headline input count. Billable input clamped to zero
cache_write_exceeds_inputCache writes exceeded billable input. Clamped to zero
batch_rate_unavailableA batch call was priced at standard rates because no batch rate exists
uncaptured_feesThe 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.

TableHolds
model_catalogOne 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_aliasesThe many identifiers that resolve to one canonical model, each with its pricing channel and its per-model rules
model_pricesEffective-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.