Every cost figure in every AI dashboard is the same product: the prompt tokens the provider reported, times an input rate, plus the completion tokens, times an output rate. Two terms. When the figure turns out to be wrong, the rate is the term that gets investigated — somebody checks the pricing page, finds a number that moved, and updates a table.
The count is the term nobody audits. It arrives from the provider, it looks like a measurement, and a measurement feels like the solid part of the arithmetic. It is not. It is a reported quantity whose definition varies between protocols, whose relationship to your text changes between model versions of the same family, and which several real charges do not appear in at all.
The count means different things on different protocols
Anthropic and OpenAI both return a prompt-token number. They do not mean the same thing by it.
Anthropic's input_tokens counts only the tokens after the last cache breakpoint. Cache reads and cache writes are reported beside it, in cache_read_input_tokens and cache_creation_input_tokens, and the documentation gives the total as the sum of all three1. OpenAI's cache figures sit inside the total: cached_tokens and, on current models, cache_write_tokens under input_tokens_details tell you how many of the input tokens were read from or written to the cache, not how many to add to them2.
// Anthropic: the three fields are disjoint.
// The total is 400 + 11500 + 100.
{
"input_tokens": 400,
"cache_read_input_tokens": 11500,
"cache_creation_input_tokens": 100
}
// OpenAI: both cache figures are subsets of input_tokens.
// The total is 12000, of which 11500 were read and 100 written.
{
"input_tokens": 12000,
"input_tokens_details": { "cached_tokens": 11500, "cache_write_tokens": 100 }
}Take the headline field from each without normalizing and the Anthropic request reports 400 against the OpenAI request's 12,000 for identical work. Normalize in the wrong direction — adding the cached count to a total that already contains it — and you charge those tokens twice.
This is not an exotic failure. It is the ordinary consequence of a gateway, an observability tool or an internal wrapper presenting one shape over several upstreams — which is the entire point of such a layer — and it produces a number that is plausible, stable and wrong in one provider's favour.
The same text is not the same number of tokens
A token is a unit of a specific tokenizer, and tokenizers change.
Anthropic's pricing page states that Claude 4.7 and later models use a newer tokenizer that "produces approximately 30% more tokens for the same text", and that the exact increase depends on the content and workload shape3. Claude Sonnet 4.6 and earlier use the previous one.
Two consequences follow, and both are easy to miss because the arithmetic keeps working.
The first is that a per-token rate is not comparable across that boundary. Two models whose published input rates are identical are not the same price per unit of your text if one of them cuts your text into more units. Any comparison that ends in dollars per million tokens has to say which tokenizer produced the million, and almost none of them do.
The second is that quantities you set against the old tokenizer silently stop meaning what they meant. A max_tokens chosen to bound an answer's length, a chunk size chosen to fit a retrieval window, a per-request token budget in an agent loop: all of them are expressed in units that just got smaller. Nothing errors. The bill moves, and the answers get truncated somewhere they did not used to be.
Cache writes are dearer than the tokens they replace
Prompt caching is marketed as a discount, and the read is one. The write is not.
| Operation | Anthropic | OpenAI (GPT-5.6 and later) |
|---|---|---|
| Cache write, short entry | 1.25x base input, five-minute entry3 | 1.25x base input; a 30-minute TTL is the only one offered2 |
| Cache write, one-hour entry | 2x base input3 | No one-hour tier published |
| Cache read | 0.1x base input; 0.05x on Claude Opus 5.5 and Sonnet 5.5; 0.025x on Claude Fable 5.1 and Mythos 5.13 | 0.1x base input; 0.05x on GPT-6.1 Sol2 |
Anthropic states the break-even plainly: at the standard read rate, caching pays for itself after one cache read at the five-minute rate, and after two at the one-hour rate3. That is a good deal, and it is not a free one. Note also that the read multiplier now varies by model inside one vendor's catalog, so "cache reads are a tenth of input" is no longer a rule a rate table can apply across a family.
A rate table that models the read discount but not the write premium is very easy to write, because the discount is the half that gets published in a headline. Such a table prices every cached token correctly on the way out and 25% too cheap on the way in. The resulting error is not noise: it is concentrated precisely on the large-stable-prefix workloads that cache hardest, which are the agent and retrieval workloads that dominate a modern bill.
We wrote that table. INFRO folded cache-creation tokens into the prompt total and estimated all of them at the plain input rate, so the figure was systematically low on exactly the traffic that used caching most — low being the direction a customer plans against and then gets a larger invoice than the console predicted. The catalog gained the write multipliers in September, and the same class of error arrived again from the other side: the entry that recorded OpenAI as having no cache-write charge was true of the generation it was written for and had become a stale reading of a vendor that now publishes one.
Some charges arrive with no tokens attached
A per-token estimate cannot produce a charge that is not denominated in tokens. Several are not.
- Web search is $10 per 1,000 searches on Anthropic3 and $10 per 1,000 calls on OpenAI, with one variant at $254. The content the search returns is billed as tokens as well, separately.
- Container sessions for code execution are billed by time. On Anthropic it is $0.05 per hour per container after 1,550 free hours a month, and free when the request also carries the current web search or web fetch tool3. On OpenAI it is between $0.03 and $1.92 per twenty-minute session depending on memory, with a footnote that eligible sessions are billed by the minute with a five-minute minimum4.
- File search is $2.50 per 1,000 calls plus $0.10 per gigabyte per day of storage beyond the first gigabyte4.
- Gemini's explicit caching bills storage per token-hour — $0.50 per million tokens per hour on Gemini 3.8 Flash until 31 December 2026 and $1.00 from 1 January 2027, and $4.50 on Gemini 3.1 Pro Preview5. There is no request at the moment that charge accrues.
That last one is the structural version of the problem. A per-request estimator can, in principle, learn about per-call surcharges: they happen during a request and the response reports them. It cannot ever see a charge that accrues while nothing is happening. There is no request to attach it to, so there is no project, no customer and no feature to attribute it to either.
The rate is a function of the request, not only of the model
A table keyed by model, holding an input rate and an output rate, assumes those two numbers are properties of the model. Increasingly they are properties of the request.
| What moves the rate | Set by | Effect |
|---|---|---|
| Service tier | service_tier: "flex" in the request body | Tokens priced at Batch API rates6 |
| Speed | speed: "fast" on Claude Opus 5.5, Opus 5 and Opus 4.8 | $8 and $40 per million on Opus 5.5 against $4 and $20; $10 and $50 on Opus 5 and 4.8 against $5 and $253 |
| Inference geography | inference_geo: "us" on Claude 4.6 and later | 1.1x on every token category, cache writes and reads included3 |
| Data residency endpoint | An OpenAI data-residency endpoint | 10% uplift on eligible models released on or after 5 March 20264 |
| Endpoint type | A regional or multi-region endpoint on Bedrock or Google Cloud | 10% premium over global3 |
| Batch submission | The batch endpoint | 50% off input and output3 |
| Prompt length | The size of the prompt | Claude Haiku 5.5 is $0.10 and $0.50 per million up to 100,000 prompt tokens and $0.50 and $2.50 above3; OpenAI's GPT-6 and GPT-5.6 models carry a second column above 272K input tokens4; Gemini 3.1 Pro Preview is $2.00 and $12.00 at or below 200K and $4.00 and $18.00 above5 |
| Time of day | When the request runs | DeepSeek's peak rate, twice its off-peak rate, applies 01:00–04:00 and 06:00–10:00 UTC, Monday to Friday, excluding Chinese public holidays7 |
The first four are the awkward ones, because they are chosen per request by the application. The same row of the same table is right for one call and wrong for the next, and nothing in the response necessarily tells you which happened. Length tiers are at least derivable — you know how long the prompt was — but only if the estimator knows the threshold exists and counts the prompt the way the vendor does. Time-of-day pricing is derivable too, from a clock, but DeepSeek's page does not say which moment of a long request decides the window7, so an estimate near the boundary is a guess either way.
A prompt-length tier prices the whole request
Anthropic's description of Haiku 5.5's tier has two details that break the obvious implementation. The prompt length counts every input token, cache reads and cache writes included. And a request over the threshold pays the higher prices on all of it, even the part that was a cache hit3. The tier is not a marginal rate on the tokens above 100,000; it is a different price list for the whole request.
It is worth noting what is not tiered. Claude 4.6 and later models other than Haiku 5.5 include the full one-million-token context window at standard rates: a 900,000-token request is billed at the same per-token rate as a 9,000-token one3. "Long context costs more" is true of some models and false of others — within one vendor, in the same month — which is exactly why it has to be a property of the rate table rather than a rule of thumb applied across it.
Tool definitions are billable input your application never wrote
The tools parameter is input. So is the tool-use system prompt the provider adds on your behalf, and its size is published per model: 286 tokens on Claude Opus 5 and Haiku 5.5 with tool_choice of auto or none, 406 with any or tool, and 675 and 804 respectively on Opus 4.73. Individual tools add more on Claude 4.7 and later — 325 tokens for the bash tool, 974 for the text editor, around 4,500 for the computer toolset and around 6,600 for the browser toolset3.
A request whose message content is forty tokens is not a forty-token request if it carries a toolset. Estimators that measure the prompt and forget the envelope are low by a fixed amount on every single call, which is the error shape hardest to notice in aggregate and easiest to notice on a small model, where the envelope can cost more than the message.
What to record instead
None of this makes estimation pointless. It makes an unqualified estimate pointless. The difference is bookkeeping:
- Record the counts as each field reports them — fresh input, cached reads, cache writes by lifetime, output — not as a single prompt total. You cannot re-derive the split later, and the split is where the rates differ.
- Record the full prompt length and the time in UTC. Prompt-length tiers and peak windows are functions of both, and a log without them cannot be re-priced when you learn a tier existed.
- Record which rate you applied and where it came from: a contract you were given, a rate card for that specific provider account, or the vendor's public list. These are different claims and they fail differently.
- Store the arithmetic with the request. Re-pricing a month-old call against today's table means a vendor's price change silently rewrites what somebody was already charged, and the customer is right in that argument every time.
- Count what you could not price. A model with no rate contributes zero to an estimate and a real number to the invoice. The share of spend you could not price is the confidence interval on all the rest of it.
- Watch direction, not magnitude. One day's gap is a day boundary. Five days leaning the same way is a rate you do not model.
- Compare against what you were actually charged. Both OpenAI and Anthropic publish organization-level cost reporting; reconciling the estimate against it is the only thing that converts all of the above from a worry into a number.
The same bookkeeping applies before a request as after it. An estimate made ahead of the call — to choose a model, size a batch or refuse a job that would not fit — has every limit above plus one more: it has to guess the output length. Estimating cost before the request covers that half.
How INFRO estimates a request
INFRO's estimate is what the customer's own provider is expected to bill them — INFRO takes no part in that money, and the subscription is a separate charge that is never added to it. The arithmetic runs in one place, and everything downstream reads its output, so there is exactly one calculation to be wrong about.
Three things follow from the sections above. Cache reads and both cache-write lifetimes are subtracted out of the prompt total and priced on their own rates, so the buckets a provider reports separately stay separate. Every request stores the lines that produced its charge — the quantities, the rate each was charged at as it stood that day, and the amount each contributed — with rounding as its own line rather than absorbed into another, so the document adds up exactly to the figure it explains. And every priced request records in price_source which rung of the rate ladder answered: a rate entered for that provider, a rate entered for the model generally, or the catalog's list rate. Where a provider reports the cost of a request in its own response, INFRO records that figure instead of computing one, and price_source is provider.
POST /v1/estimates asks the same question before a request: given a model, optionally a provider, and the quantities — input and output tokens for text, resolution and quality for images, seconds of video, characters of speech — it returns estimated_cost, its price_source and the rate used, or null with an unpriced_reason where no rate is known. For text it applies one input and one output rate, so it inherits every limit in the next paragraph.
What the rate model does not express today, stated rather than implied: prompt-length tiers, service tiers and speed modes, server-side tool surcharges, storage billed per token-hour, peak and off-peak pricing, and provider accounts billed in a currency other than US dollars. A rate you enter for a model holds one input and one output figure, so for a tiered model such as Claude Haiku 5.5 it is right on one side of the 100,000-token line and wrong on the other. A workload leaning on any of those should be governed with margin, and the reconciliation report is where the gap shows up.
That margin matters because INFRO's budgets count the estimate. A blocking cap refuses a request whose worst-case estimated cost does not fit, and a model with no known price is refused while a blocking cap applies (402 unpriced_model_under_budget) unless the organization allows unpriced traffic. The cap governs INFRO's estimate of provider cost, not the provider's invoice; a rate the table does not model moves the invoice without moving the cap.
The catalog carries the date it was last reconciled against published provider pricing, on every model page, because a price sheet without one is a price sheet nobody can weigh. That date moved to 2026-09-20 while this article was first being researched, and what moved with it is the point of the article: two cached-input multipliers that had drifted as vendors changed them, a cache-write premium that a vendor had introduced since the entry was written, and two base rates — one of which recorded a price rise that was announced, scheduled, and then cancelled. When this article was reviewed on 2026-10-13 the date read 2026-10-05, and the published price lists by then included a prompt-length tier on a Claude model and a 0.05x cache-read rate on three models from two vendors.
An estimate with a direction
The useful thing is not a more accurate number. It is a number that states which way it is likely to be wrong, how much of the traffic it covers, and when it was last checked against the only figure that settles the argument.
Everything above is a reason a competent estimate still drifts. None of them is a reason not to compute one — a per-request figure is the only way to answer which customer, which feature or which prompt change moved the bill, and an invoice will never answer that. Compute it, qualify it, and check it against the invoice often enough that you find out from your own reporting rather than from finance.
