kvleak · notes

Prompt caching, from the KV cache up

Tokens
are not dollars.

Your agent's dashboard counts tokens. Your bill counts what kind of tokens they were. In 936 public Claude Code runs, the gap between the two is wide enough to flip which habits save money.

Share of tokens

cache reads 0.1× input price89.7%
cache writes 1.25–2×9.3%
output 5×1.0%

All 936 parsable runs of Kensei's public benchmark (114M tokens, $79.14 at list price). Uncached input rounds to 0%.

Every coding agent re-sends its whole conversation on every call: system prompt, tool definitions, your instructions file, every file it has read, every command it has run. A normal question takes six or so model calls, and the same hundred thousand tokens go over the wire each time.

That would be ruinous without prompt caching, and with it the dashboards look great: hit rates of 90, 95, 99%. But a hit rate is a share of tokens, and the three kinds of token on your bill differ in price by a factor of 20 to 200. Count them by money and the picture changes: the reads that make up 90% of the volume are a quarter of the cost.

This piece builds the pricing up from what the cache physically stores, then re-prices a public benchmark that was, quite reasonably, scored in tokens. Two of its conclusions reverse.

1 · What is actually cached

The cache stores attention, so it only matches exact prefixes

Before a model writes its first token, it runs a prefill pass over your prompt. For every token, in every layer, it computes a key and a value vector that later tokens attend to. That pile of vectors is the KV cache, and computing it is most of what you pay for as input.

Prompt caching keeps that pile around after the request. The next request that starts with exactly the same tokens can skip prefill for that part and read the stored vectors instead.

The catch is in the word exactly. Attention is causal: the vectors for token 5,000 depend on tokens 1 to 4,999. Change one token anywhere and every vector after it is wrong, even if the rest of the text is identical. A cache can only match a prefix. Try it below: click the part of the prompt that changes.

A 200k-token Claude Code request, Opus 5.5, 1-hour cache

read from cacherebuilt and re-writtennew this turn
read from cache200k
rebuilt (written at 2×)0
cost of this call's input$0.048

Nothing before the new message changed, so everything else is a cache read.

Segment sizes are illustrative of a long session. Prices from kvleak models: $4/MTok input, reads 0.05×, 1-hour writes 2×.

This is why a timestamp in a system prompt costs a full rebuild on every call, and a hook that injects today's date costs one every morning. A tool list that comes back in a different order does the same, silently. It is also why a model switch is a miss even when the text is identical: the vectors belong to the weights that computed them. And it is why compaction always rebuilds. It rewrites history, so the old prefix no longer exists.

2 · The price sheet

A read is nearly free. A write costs more than not caching

Providers price the two sides of the cache very differently. Reading stored vectors costs 0.1× the normal input price on most models, and less on newer ones. Storing them costs a premium on top of the prefill: 1.25× for a 5-minute lifetime, 2× for an hour. Output, the tokens the model writes, costs 5×.

modelinput $/MTokcache readwrite, 1hone miss costs
Fable 5.1$10.000.025×2×79 hits
Opus 5.5$4.000.05×2×39 hits
Sonnet 5.5$2.000.1×2×19 hits
Haiku 5.5$0.100.1×2×19 hits

The last column is the avoidable part of a miss, (write − read) / read: what you pay for rebuilding a prefix that could have been read, measured in reads. Cheaper reads make each miss look worse, not better. On Fable 5.1 one rebuild is worth 79 hits.

A 95% hit rate tells you how many tokens were cheap. It says nothing about how expensive the other 5% were.

The same ratio answers a second question: how long is it worth keeping a cache alive? Each keep-alive ping is one read of the whole prefix. A rebuild costs write − read. So the most pings worth sending is

K = floor((write − read) / read) 1h cache, Opus 5.5: floor((2 − 0.05) / 0.05) = 39 pings

Thirty-nine pings, one per hour, is more than a day and a half of idle time. On cost alone, almost any pause you plan to come back from is worth bridging. The catch is the come back: a ping before a pause you never return from is wasted, which is why kvleak's keepalive learns from your own history how often you actually return.

3 · The clock

Every read resets a timer. Lunch doesn't

A cached prefix lives for its TTL after its last use, and each read restarts the clock for free. An agent working in a loop calls every few seconds and never lets it expire. You walking away is what kills it.

On my own 30 days of Claude Code, 95.4% of input tokens were cache hits, and 20.7% of input spend still went to rebuilds. Most of that leak, 69%, came from idle gaps that outlived the TTL: lunch, meetings, overnight. Play with the numbers:

200k
84 min
Model
Cache lifetime
next message if the cache died$1.60
keep-alive pings needed1
cost of those pings$0.040

Rebuild = context × write price. Pings fire just before each expiry, each one a read of the context. Same arithmetic as kvleak breakeven.

4 · A benchmark, re-priced

The same 945 runs, counted two ways

In October, Kensei published one of the most careful Claude Code experiments I've seen: 12 popular tips, 945 runs, every answer graded, paired comparisons and confidence intervals, and all the raw logs on GitHub under MIT.

He scored everything in raw tokens, and said why: Anthropic doesn't publish how cache reads count against a subscription's limits, so tokens were the honest unit. But every log also records costUSD, Claude Code's own price at API list rates. I re-scored his session experiment with it.

The experiment builds a chat of 5, 15 or 30 turns, then forks it three ways (keep going, /clear, or /compact) and asks the same three new questions in each fork.

Three follow-up questions after the fork, totals

keep going/clear/compact (includes the /compact call)
Means over 3 chains per history length. Data: kenseii1/claude-code-limits-tests, runs/session. Percentages are against "keep going" at the same length.

Flip the toggle. In tokens, /compact saves 39–60% everywhere. In dollars, it costs 14–36% more at every history length. /clear still wins at 15 and 30 turns, but by less, and at 5 turns it is slightly more expensive than doing nothing.

Nothing about the experiment is wrong. The two units simply weigh different things, and section 2 says how. "Keep going" sends a lot of tokens, but nearly all of them are reads of a warm prefix at 0.1×. /compact pays three expensive things to make that prefix smaller:

  • The summary call itself reads the whole chat and writes the summary as output at 5×: $0.035 to $0.071 per compaction here.
  • A new prefix that has to be written to the cache at 2×, because the old one no longer matches.
  • Files read again. The summary keeps conclusions, not the files the agent opened, so the follow-ups re-open them and write them to the cache again. Over three questions the compacted forks wrote roughly twice as many cache tokens as the ones that kept going.

/clear avoids the first cost, and at 5 turns the other two are enough to cancel its savings: there wasn't much history to drop.

A one-time cost can still pay off if the session runs long enough. Here is where the lines cross:

Cumulative cost of follow-up questions

Cumulative cost lines for keep going, /clear and /compact

Solid: the three measured questions. Dashed: the per-question mean extended linearly, which flatters "keep going" a little, since its context keeps growing. /compact starts at the cost of the /compact call.

Measured in tokens, /compact pays for itself before the first question finishes. Measured in dollars, it breaks even after about 7 questions on a 15-turn history and 8 on a 30-turn one, and on a short history it never does. That's still a fine trade for a session with a long way to go. It's a bad one before a quick question.

5 · Subagents

A fresh context is a cold cache

The same benchmark ran a task (run the tests, diagnose the failures) with three follow-up questions, once in the main chat and once delegated to a subagent. In tokens the subagent was a wash. In dollars it cost almost a third more:

raw tokens, subagent vs main chat

−3.4%

246k vs 255k tokens over 4 calls

costUSD, subagent vs main chat

+29.7%

$0.152 vs $0.117 over 4 calls

A subagent starts with an empty cache, so its system prompt, tools and every file it opens are written to the cache for the first time, at 2×. The task call cost $0.093 delegated against $0.052 in the main chat. The lighter main chat then saved about $0.002 per follow-up, which would take some twenty questions to repay.

Subagents still earn their keep: they hold verbose output away from your main context, which matters in long sessions. Kensei found the same and said so. They just aren't a way to save money on a short task.

6 · What to do with this

Count what you pay for

  • Keep the prefix byte-stable. No timestamps or random IDs in system prompts, hooks or instruction files; stable tool order; sorted JSON. A change at token 1 costs the whole context.
  • Pick the model at the start of a session. Switching mid-session is a full rebuild on the new model.
  • Use /clear between unrelated tasks once the history is large. Both units agree on that, as long as there is something big to drop.
  • Compact when there is a long way to go, not before a quick question. On these runs it took 7–8 further questions to pay back.
  • Mind the clock, not the hit rate. Reply before a big context expires, or let a keep-alive bridge pauses you'll come back from.
  • For one-shot claude -p batches, use a 5-minute TTL. A prompt that's written and never read back pays the 1-hour premium for nothing; in this benchmark that was $14 of the $79.

All of this depends on your own work, which is the point of measuring it. kvleak reads the session files Claude Code already keeps (and Codex CLI, Gemini CLI, OpenCode, and now claude -p --output-format json results) and prices every miss by cause. It has no dependencies and makes no network calls.

uvx kvleak
$ uvx kvleak --share
My prompt-cache audit (kvleak):
• Cache hit rate: 90.6% (936 calls across 621 sessions)
• Input spend leaked to cache rebuilds: 3.7% ($2.47 of $66.41)
• #1 leak cause: New session cold start (46.6%, $1.15 across 24 misses)

# that's Kensei's benchmark; this is yours:
$ uvx kvleak            # the full audit
$ uvx kvleak tune       # replay your history under each Claude Code setting (an estimate)
$ uvx kvleak probe      # a week after changing them: measured before vs after

If yours looks nothing like mine, that's useful too. Paste the --share output in issue #1; it contains no paths or session IDs.

Limits: dollars here are list API prices as recorded by Claude Code. How caching counts against subscription limits isn't published, so if you're on a plan, read these as relative weights rather than money. Kensei's runs are short code-reading tasks on one codebase; my own figures are one heavy user's month. The full re-analysis, with every number, is in kensei-945-analysis.md.