Registry pricing and cost estimates
August 27, 2026 · View on GitHub
TrustGate prices each request in USD per token, the same unit stored in the
models.dev catalog (llmcost.CustomPrice). There is no monthly aggregator in
the gateway. Downstream rollups that sum cost.total_usd (or
trustgate.cost.total_usd) over a period are only as accurate as the rates
used on each event.
Registry pricing
Configure contract rates on an LLM registry (admin API field pricing, also
nested on llm_target in snapshots):
{
"pricing": {
"discount": 0.2,
"overrides": {
"gpt-4o": { "input": 0.0000015, "output": 0.000006 },
"gpt-4o-mini*": { "input": 0.0000001, "output": 0.0000004 }
}
}
}
overrides: absolute USD per token. Keys are exact slugs or*globs. The most specific match wins (BestMatch: exact > most literal characters).discount: optional fraction off the models.dev list price (0.2= 20% off). Applied only when no override matches that model.
discount is not a multiplier. A comment that used 0.8 for “20% off”
is the inverse of this contract.
Precedence
For a given request, input/output rates resolve as:
- LLM Budget
custom_pricingon the policy (explicit per-policy overlay) - Registry absolute override (exact slug, then best glob)
- Registry
discount× models.dev catalog price - models.dev catalog as-is
- Unpriced (no
coston the event; dollar budgets accrue zero)
The same function (llmcost.Resolve) is used for:
- per-request metrics
cost.prompt_usd/cost.completion_usd/cost.total_usd - LLM Budget dollar mode (and cost-cap evaluation)
Two registries can publish different rates for the same model slug. The served registry (stamped on the request after routing) is the one that applies.
Monthly estimates
TrustGate does not compute a monthly invoice. To estimate period spend:
sum(usage.tokens × resolved_per_token_rate) over the window, which is the
same as summing cost.total_usd on telemetry events.
Those estimates match the customer invoice only when registry pricing
matches the contract (enterprise list discount and/or committed per-model
rates). List prices from models.dev diverge from reserved/committed deals.
Cache tokens
Prompt tokens served from, or written to, a provider's cache bill at their own
rate. The catalog carries cache_read_price and cache_write_price alongside
the input and output rates, synced from models.dev, and a registry
pricing.overrides entry may set cache_read / cache_write to a negotiated
rate.
prompt_usd = (prompt - cached - cache_written) * input
+ cached * cache_read
+ cache_written * cache_write
InputTokens is the whole prompt, and the cache counts are subsets of it.
Providers that report their cache counts beside the prompt rather than inside it
— Anthropic does, where input_tokens is only the uncached remainder — are
normalised by their adapter, so this one expression is correct everywhere.
An unset cache rate bills at the plain input rate, which is what the gateway
charged for those tokens before cache rates existed. That applies to a model
models.dev publishes no cache rate for, and to a registry override that names
only input and output. Reading unset as zero would silently make most of a
cached prompt free, which is the worse way to be wrong. An explicit
"cache_read": 0 is honoured as a real "free under my contract".
Rates differ sharply by provider, so they are never derived from a ratio: on
current list prices Anthropic reads at 0.1x input, OpenAI's gpt-4o at 0.5x,
gpt-5 at 0.1x, xAI Grok at 0.25x.
Anthropic's one-hour cache TTL bills above its five-minute default, and no
catalog publishes a rate for it. The one-hour share is reported separately on the
usage view and priced from cache_write_1h when an override sets it, falling
back to the five-minute rate otherwise. Set it explicitly on any registry whose
traffic uses the long TTL — without it those writes are under-billed, and the
multiplier is deliberately not inferred, because it is an Anthropic fact rather
than a universal one.
Streaming providers report usage in pieces: Anthropic sends the prompt and both cache buckets on the first event and the completion on the last. Usage is merged field-wise across events, keeping the larger of each count, so an event that omits a field cannot erase it.
If the sub-counts ever exceed the prompt they claim to be part of — which is what a provider silently changing its wire shape looks like — the whole prompt is billed at the plain input rate and a warning is logged. That is the safe direction for money, and it is no longer silent.
Smart-routing savings
When a consumer's pool uses the smart-routing algorithm and the tier table
chose the route, the event's cost object also carries savings_usd: the same
token counts repriced at the highest configured tier, minus the actual cost.
It lives inside cost rather than in a block of its own, so the counterfactual
total is cost.total_usd + cost.savings_usd.
The full emission rules — when the field is absent rather than zero, and why — live in the OTLP metadata contract. What matters for pricing:
- The baseline resolves through the same
llmcost.Resolveladder as the actual cost, but with the baseline tier's own registry overlay, which is not necessarily the served registry's — a tier can point anywhere in the pool. discountapplies only to catalog-resolved prices, while explicitoverridesreturn before it. If one leg is priced by an override and the other by a discounted catalog rate, the two follow different rules.- "Highest tier" means the greatest
min_score, not the highest price. Nothing validates that the top tier is the most expensive model, so a ladder that puts a premium model at a low threshold produces a negativesavings_usd. That is left unclamped: it surfaces the misconfiguration instead of hiding it. - The baseline prices the whole prompt at the plain input rate, including the share the served request billed as a cache read or write. This is deliberate: the baseline is a route that was never taken, so it had no warm cache to read from. Pricing the counterfactual as if it inherited the served route's cache would credit it a discount it could not have earned.
- That assumption biases the figure upward, and how far depends on the token mix. On a prompt-dominated request that is mostly a cache hit it is worth roughly 10x the reported saving; on an output-dominated one it barely moves. The gap is exactly the cached share repriced from the plain input rate down to the baseline's cache read rate. It is the honest reading of a cold counterfactual, not a conservative one, and it should be read as such.
It is a modelled counterfactual, not a measurement: it assumes the premium model would have consumed the same tokens. It will not reconcile against a provider invoice, and it does not cover cost-cap model downgrades, which are a different mechanism.