The same 8KB file is 3,519 tokens on Opus and 2,586 on Haiku
Identical bytes do not become identical token counts across Claude models. Haiku 4.5 counts the same file 10% to 34% cheaper than Opus 5, which quietly breaks every per-token price comparison anyone has ever drawn between them.
A token is the unit everything is priced in, and it is treated as if it were a physical constant. Every model comparison published anywhere puts dollars-per-million-tokens side by side and calls that the price.
It is not, at least not between these three. The same bytes are a different number of tokens depending on which model you send them to.
The measurement
Three files of exactly 8,000 bytes, differing only in what kind of text they contain, each dropped into an otherwise empty project as CLAUDE.md and measured as a paired before-and-after on the same model in the same batch.
| 8,000 bytes of | Opus 5 | Sonnet 5 | Haiku 4.5 | Haiku ÷ Opus |
|---|---|---|---|---|
| Dense markdown table | 4,640 / 4,625 | 4,636 | 4,179 / 4,194 | 0.90 |
| Ordinary prose | 3,469 / 3,519 | 3,470 | 2,556 / 2,586 | 0.74 |
| Repetitive common words | 1,915 | 1,919 | 1,264 | 0.66 |
Opus 5 and Sonnet 5 agree on every input, to within 15 tokens. Haiku 4.5 agrees with neither, and the size of the disagreement depends on the text: it is nearly the same on a dense table and a third cheaper on repetitive prose.
That content-dependence is the whole story. A fixed policy, a truncation rule, or a summarisation step would produce a roughly constant ratio. A ratio that swings from 0.90 to 0.66 depending on how repetitive the text is, is what a different tokenizer vocabulary looks like: more of the common sequences already exist as single tokens, so text made of common sequences compresses further.
Ruling out the boring explanation
The obvious alternative is that Claude Code simply sends less to the cheaper model, and the “saving” is content that never arrived.
It is not. I put a distinct marker at the very end of the file, past all 8,000 bytes, and asked each model to read it back:
MARKER_TAIL=WALRUS877
Opus returned WALRUS877. Haiku returned WALRUS877. The whole file reaches both. Haiku is counting the same text as fewer tokens, not receiving less of it.
This is the control that had to pass before any of the above meant anything. A cost measurement of content you never confirmed arrived is not a measurement of a discount, it is not a measurement.
The startup floor differs too, by a lot
Same trivial task, Reply with exactly OK., in an empty project, three rounds each:
| Model | Round A | Round B | Round C | Spread |
|---|---|---|---|---|
| Haiku 4.5 | 45,961 | 45,961 | 45,964 | 3 |
| Opus 5 | 58,750 | 58,750 | 58,747 | 3 |
| Sonnet 5 | 70,031 | 70,031 | 70,025 | 6 |
Deterministic to within 6 tokens. Sonnet 5 starts a session 24,070 tokens heavier than Haiku 4.5 and 11,281 tokens heavier than Opus 5, before you have typed anything.
Sonnet being the heaviest is the part I did not expect. The intuition that the bigger model carries the bigger prompt is simply wrong here.
Two caveats on these absolutes, both of which matter. They were measured on a machine with a large user-level CLAUDE.md that loads into every session, so they are not Claude Code’s bare floor. And because of the finding above, that shared file is itself a different number of tokens on each model, so it does not cleanly cancel the way a shared component normally would. Treating the file as closest to the dense-markdown sample, roughly 2,700 of the 12,789-token Opus-to-Haiku gap is the config being cheaper on Haiku, and the remaining ~10,000 is a genuinely lighter floor. That split is an estimate and labelled as one; the per-model totals above are measurements.
Why this actually costs you money
Per-token pricing is only comparable between models if a token means the same thing to each. Between Opus and Sonnet it does. Between either and Haiku it does not.
So the real cost ratio for a given document is not the published price ratio. It is the published price ratio multiplied by how many tokens that model turns your document into, and for Haiku that multiplier is 0.66 to 0.90 in your favour, on top of whatever the headline price already says. Haiku is cheaper than the price list implies, and how much cheaper depends on what you are sending it.
The same effect runs the other way on anything you are measuring. Every cross-model context comparison drawn from a real machine, including the floors in the table above, is contaminated by any shared config file, because that file is not a shared number of tokens.
One more difference, which is not about cost
On the identical instruction Reply with exactly OK.:
| Model | Output tokens observed |
|---|---|
| Opus 5 | 4, 4, 4, 4, 4, 4 |
| Sonnet 5 | 4, 4, 4, 4, 4 |
| Haiku 4.5 | 38, 41, 77, 97, 112, 156 |
Opus and Sonnet emitted exactly four output tokens every single time. Haiku never once did, and ranged across a factor of four. The direction reproduces without exception; the magnitude does not reproduce at all, so there is no number here beyond “consistently more, unpredictably how much.”
What I could not pin down
I also tried to establish whether each model keeps its own prompt cache, the way each effort level does. Returning to Opus after running Sonnet was sometimes free, 0 tokens of cache creation, which does show the caches coexist rather than evicting one another. But the same return was not free on other passes, and the pattern did not reproduce between rounds.
The most likely explanation is that cache lifetime expired during the longer sequences, which would make this the same confound that stopped the TTL measurement from being publishable. I am not going to assert it. Caches coexisting is supported; anything about when a return is free is not, and it stays unpublished until it reproduces.
Update, 2026-08-13: cache lifetime has since been measured directly, and on a subscription the cache survives an eight-minute idle gap for 39 tokens. That makes “the lifetime expired” a weaker explanation for these sequences than it looked at the time, since they ran nowhere near that long. It does not establish what did cause them, and that still stays unpublished.
Method
Every figure is a paired measurement: an empty project measured, the file added, measured again, back to back in the same batch, because the reported baseline drifts within a session. File costs are two rounds per model per content type. Floors are three rounds per model. Totals are inputTokens + cacheCreationInputTokens + cacheReadInputTokens from modelUsage, which unlike usage also counts subagent tokens, and --model was confirmed to be honoured by reading back which model modelUsage actually reported.
All measurements on Claude Code 2.1.224, headless, against claude-opus-5, claude-sonnet-5 and claude-haiku-4-5-20251001.