← All posts
Provider notes5 min read

The same model ID is not the same price everywhere. A field guide.

A per-token price is a function of platform, model id, endpoint class and billing mode, plus context tier and time resolved per request. Every shortcut produces a confident wrong number.

Ask what a model costs and you will get a two-number answer: dollars per million input, dollars per million output. Building a pricing engine that has to match real invoices taught us that those two numbers are the output of a function with at least four arguments — and that every shortcut through the argument list produces a figure that looks right and is not.

The four components a pricing key needs

Platform. The same model ships on multiple platforms at independently set rates, under independently shaped ids. Today's cross-platform Claude parity is a fact someone verified dimension by dimension, not a rule — and where a platform publishes no number at all, the honest state is "unpriceable there", not "same as elsewhere". AWS's own pricing page renders no table for several current Llama models; we keep that as a recorded checked negative, because "we looked and there is no number" and "nobody looked" must never collapse into the same absence.

The platform's own model id. Not a normalised family name. us.anthropic.claude-sonnet-4-5-20250929-v1:0 and claude-sonnet-4-5@20250929 are the same weights, but the id grammar carries platform-specific information — Bedrock's geo prefix encodes the endpoint class — and flattening ids is how customers' fine-tunes get priced as base models.

Endpoint class. Global versus regional versus multi-region, at +10% on the clouds for the regional forms — and on Bedrock the us./eu. inference-profile ids are what most production traffic actually uses, which makes "default to global" the single highest-frequency error available to a pricing engine. This component must be required with no default. It also has an asymmetric edge: a class can be unserved (Vertex publishes no regional rate for Opus 5 or Sonnet 5 because it does not serve them regionally), and an unserved class must refuse to price, not borrow the global figure.

Billing mode. On-demand is the baseline; batch is −50% where published (and only where published); premium "fast" tiers exist as real published prices on some models (a flat 2x on Opus — and only input/output, so the fast tier's cache economics are unpublished and a cached fast call must fail closed rather than get four invented numbers). Provisioned capacity — PTUs, GSUs, provisioned throughput — is not token-billed at all: per-token arithmetic there does not approximate the number, it fabricates one, and the only correct output is a refusal that says so.

The two components that resolve per request

Two more dimensions cannot live in the key, because they are properties of the individual call.

Context tier. Several models re-price above a threshold — and re-price the entire request, not the overage. OpenAI's threshold is 272,000 input tokens, not the marketing page's rounded 270K; a rounded threshold re-prices requests the provider billed at standard. The threshold compares against the full prompt — uncached input plus cache reads plus writes — and where a long-context rate was never published for some dimension (cache writes, usually), a long-context call touching that dimension is unpriceable, honestly.

Time. Rates are validity windows, not constants. Promotions start and lapse on dates; Gemini's Flash line has a published 2x step on a calendar day; DeepSeek's rate varies by UTC wall clock, with peak weekday hours at double the off-peak price — so the same call costs different amounts at 09:00 and 11:00 UTC. A call must be priced at the rate in effect when it happened: resolving "now" against history silently re-prices the back catalogue every time a boundary passes. And a scheduled future rate is a claim about a world that has not happened yet — ours each carry a re-verify-by date that fails the build if it lapses before the boundary fires, because we once let a guessed price change fire unreviewed and charged three days of traffic 50% high.

The rules that keep the engine honest

Four rules fall out, and they are the whole design. Exact key or nothing — an unknown combination prices to insufficient data, never $0, never a sibling's rate. No defaultable components — every shortcut above is someone defaulting an argument. Carry provenance — a rate read off a vendor page, one designated by contract, and one derived from a published rule ("cached input is 10% of input") are three different strengths of claim; an aggregate is labelled by its weakest member, and anything irreversible gates on the strong ones. Published-or-refuse for derived dimensions — where a discount is unpublished or zero (one fast host charges cached tokens at the full input rate), there is no saving to display, and rendering "$0.00 saved" would still be a claim about a discount that does not exist.

Every one of these rules exists because the shortcut it forbids produced, or nearly produced, a wrong number on a real dashboard. The rendered face of all of them is the dash, and the full keyed rate table — with each row's source and retrieval date — is what Vigil prices against.

What to do

Take your top three models by spend and write down the full key for each — platform, exact platform id, endpoint class, billing mode — then check your cost tracking against the components rather than the headline rate: whether regional traffic gets the +10%, whether batch gets −50% only where published, whether anything can cross a long-context threshold, and what rate a call from last month resolves against today. If your pricing lives in a single {model: rate} map, each of those questions is a place it is quietly wrong, and the regional default is the one to check first.