Measured across 3.9GB of real Claude Code transcripts: 97.7% of tokens are cache reads, 99.8% of cache writes are the one-hour kind priced at 2x input rather than 1.25x, and 52% of transcript lines are duplicates. Getting any of the three wrong moves the bill by a multiple.
the short version
Claude bills five different things at five different prices, and they are not close to each other. If you sum the token counts in your Claude Code transcripts and multiply by one rate, your answer is wrong by roughly six and a half times. We measured this across 3.9GB of real transcripts, which is 907 session files across 60 projects on one machine.
Three separate mistakes, each worth a multiple or a few hundred dollars. What follows is each one, with the arithmetic.
where the data is
Claude Code writes a JSONL transcript per session under ~/.claude/projects/, one directory per project and one file per session. Every billed call is an assistant record carrying a usage block:
{
"type": "assistant",
"requestId": "req_011Cdbv2N3HpzpJAvk2a8Jar",
"message": {
"id": "msg_011Cdbv2PjzJap8M5SrVA9r2",
"model": "claude-opus-5",
"usage": {
"input_tokens": 2,
"output_tokens": 237,
"cache_read_input_tokens": 29940,
"cache_creation_input_tokens": 35181,
"cache_creation": {
"ephemeral_5m_input_tokens": 0,
"ephemeral_1h_input_tokens": 35181
}
}
}
}Note the shape of that block. input_tokens is 2. The turn cost almost nothing in fresh input and nearly everything in cache. That is what a Claude Code session looks like, and it is why the naive count fails so badly.
1. a cache read is a tenth of an input token
Relative to a model's base input rate, the five classes price like this. On Claude Opus 5, where input is $5 per million:
In the corpus, 97.7% of all tokens were cache reads. Because they cost a tenth of an input token, those same reads were only 63.9% of the bill. Both halves of that sentence matter. A tool that treats a cache read as an input token inflates the total about sixfold. A tool that ignores cache reads because they are "just cache" drops the majority of what you actually owe.
2. Claude Code writes one-hour cache entries, not five-minute ones
This is the one almost nobody has right, ourselves included until recently. Prompt caching has two time-to-live options and they are priced differently: a 5-minute write costs 1.25x the input rate, a 1-hour write costs 2x. The commonly cited figure is the 5-minute one, so that is what implementations reach for.
In this corpus, 99.8% of cache-creation tokens were ephemeral_1h. Pricing all of them at the 5-minute rate understated the bill by $638, which is 62.5% of the true cost of those writes.
How to see it
The breakdown is in the nested cache_creation object shown above. The flat cache_creation_input_tokens field is their sum and carries no split, so a tool that reads only the flat total cannot tell the two apart and has to guess. If you have to guess, guess one hour: it is the dearer class, so the error can only ever be in the customer's favour rather than against them.
3. more than half the lines are duplicates
A single API response is written to the transcript once per content block. An assistant turn containing a sentence and a tool call is two JSONL lines carrying the same usage object with different uuids. Resumed and forked sessions then copy lines into new files on top of that.
Of roughly 56,700 usage-bearing lines in the corpus, only about 27,100 were distinct API calls. Counting lines rather than calls roughly doubles the answer.
The identity of a billed call is the pair (message.id, requestId). uuid does not work: it is per line, so it does not collapse the split-turn case at all, and we found several hundred collisions besides. The seen-set also has to be global rather than per file, because resumed sessions duplicate across files.
counting it correctly
The method is short. Read every assistant record, drop anything whose model is <synthetic> because that is locally generated error text and was never billed, deduplicate on (message.id, requestId) globally, keep the five token classes apart, and price each class separately against the model named on that record. Never sum before pricing.
We packaged exactly that as a small open-source CLI, so you can check your own number rather than take ours:
npx claude-code-spendIt reads only your local transcripts, sends nothing anywhere, and prints spend by day, by model and by project. The pricing table and the tests that assert these multipliers are at github.com/jawadjalal/claude-code-spend, MIT licensed. If the rates drift, a pull request to one file fixes it for everyone.
what this does not say
These are list API prices. If you are on a Pro or Max subscription you are not billed per token at all, so treat the figure as what your usage would have cost on the API. It is still the most useful measure of how much work you are pushing through, and it is the number that decides whether a subscription is worth it, but it is not an invoice.
The percentages come from one machine's corpus. The 97.7% and the 99.8% are what heavy daily Claude Code use looked like there over several months. Your split will differ. The three mistakes do not: they are properties of the format and the rate card, not of the workload.