Prompt caching

How Claude's prompt cache keys a request, what the cache read and cache write token fields count, and what a cache miss costs.

Verified 2026-09-04: three independent investigations (/investigate, run wf_6d36f04f-246) merged adversarially against primary sources and jq-mined local transcripts, recomputing a review-skill run whose tokens exploded. FACT: primary source or reproduced. HYPOTHESIS: unproven; the runbook measures it. Session-level numbers: Token usage.

Method and sources

SourceUsed for
Prompt-caching docsBreakpoints, keys, TTL, invalidation table.
Messages API referenceusage field semantics.
Claude Code costs, Claude Code prompt cachingHow plan usage draws on cache reads.
API rate limitsCache-aware ITPM (input tokens per minute).
Local transcripts~/.claude/projects/**/*.jsonl, mined with jq.
The review-skill run~/Downloads/d3337a3f-a576-4040-a271-29ef4474e801.jsonl plus its subagents/.

The API has no memory

The Messages API is stateless: Anthropic keeps no session. Every call resends the whole conversation in order (tool definitions, system prompt, all messages), and the model processes all of it before generating a token. A 60-turn Claude Code session processes turn 1's content ~60 times, turn 2's ~59; uncached, each pass pays full input price.

Prompt caching stores the processed state of a request's beginning, the prefix; the next request computes only the new end, the suffix.

request N:   [tools][system][msg1][msg2]...[msgK]
             └────────── prefix ─────────┘
request N+1: [tools][system][msg1][msg2]...[msgK][asst reply][new msg]
             └── identical to request N, cached ──┘└─ new suffix ──┘

How cache keys are created

FACT From the prompt-caching docs.

  1. Mark breakpoints

    cache_control marks a breakpoint, at most 4 per request. Claude Code sets them; you never do.

  2. Hash the prefix

    The server hashes everything from the request's start to each breakpoint: tools, system, messages, in order, byte for byte. The hash is the cache key; it stores key → processed model state.

  3. Match the longest key

    The next request matches the longest stored key equal to its beginning, checking breakpoint positions backward within a ~20-position lookback window. Everything up to the match comes from cache.

RuleDetail
Matches only from the startA change at byte 3,000 of the system prompt breaks every key over positions ≥ 3,000, though 99% of the content is unchanged.
Not session-scopedSame starting bytes from your account hit the same entries. Observed: a new session's first call read cache_read = 45,383 that a parallel session in the same directory (same system prompt and CLAUDE.md) wrote minutes earlier.
Minimum size512 tokens; shorter prompts are silently uncached.
TTL5 minutes on API keys; 1 hour on subscriptions (Pro/Max/Team), 5m once on usage credits.
RefreshA hit refreshes the TTL for free; an active session never expires its own cache.

The four usage fields

FACT Each response's usage splits the request into three input buckets plus output (docs semantics, verbatim).

FieldCountsPrice (× base input)
cache_read_input_tokensThe matched prefix, served from cache.0.1×
cache_creation_input_tokensProcessed fresh and written to cache, up to the last breakpoint.1.25× (5m TTL) / 2× (1h TTL)
input_tokensLeftover after the last breakpoint: fresh, uncached.1×
output_tokensGenerated tokens; thinking counts as output.5×
Trap

input_tokens is not the prompt size. Full context per call: context = input_tokens + cache_creation_input_tokens + cache_read_input_tokens.

Why input_tokens is about 2 in Claude Code

Claude Code puts a breakpoint at essentially every request's end, so the whole conversation lands in the two cache buckets; ~2 tokens of structural wrapper remain. FACT input_tokens ≤ 2 on all ~470 sampled Claude Code calls.

A raw API call without a breakpoint at the end (no cache_control) reports the whole prompt as input_tokens, both cache fields 0. In Claude Code the new suffix (new message, previous assistant reply, new tool results) bills once as cache_creation, then as cache_read on every later call.

Worked example: cache hits

A fresh Claude Code session; system prompt, CLAUDE.md and tool definitions total ~5,000 tokens.

  1. Call 1: a 100-token question

    Request [5,000 system/tools][100 user], cold, breakpoint at the end. Stores a 5,100-token key over system, tools and question.

    usage: input=2, cache_creation=5,100, cache_read=0, output=300
  2. Call 2: a tool runs

    Resent with the 300-token assistant reply (the tool call) and a 400-token tool result appended. Call 1's key matches: 5,100 served at 0.1×, the new 700 processed fresh, a new key covers 5,800.

    usage: input=2, cache_read=5,100, cache_creation=700, output=250
  3. Call 3: the next tool result, 1,000 tokens
    cache_read=5,800, cache_creation=1,250 (prev output 250 + result 1,000), input=2
ClaimEvidenceStatus
cache_read(N+1) = cache_read(N) + cache_creation(N)Exact on 268/285, 51/52, 129/130 and 32/32 consecutive calls across four transcripts.FACT
session cache_read total ≈ Σ over calls (context size at that call)Review-skill run: Σ cache_read 60.84M vs Σ per-call context 63.5M, a 95.7% match.FACT

Each token is written once, on its first turn, and read on every later call: at 200k context every call, even "say ok", pays ~200k cache-read tokens.

Worked example: cache miss

Same session, call 40, context 115,000 tokens. Between calls 39 and 40 you toggle a hook, changing the system prompt at about token 2,000. Every long key hashed the old bytes; the longest surviving match is a breakpoint before the change, perhaps the tools block.

FACT Real event from a local transcript, 2026-07-14T19:04:24:

call 39: cache_read=114,903  cache_creation=1,850   input=2   ← normal
call 40: cache_read=32,852   cache_creation=88,743  input=2   ← MISS
call 41: cache_read=121,595  cache_creation=...     input=2   ← healed (32,852+88,743 exactly)

Call 40 read the 32,852 tokens before the change at 0.1× and re-wrote the other 88,743 (everything from the changed byte on, messages unchanged) at the 2× write rate. Misses never bill at base rate or show in input_tokens, which stays 2. Call 41 resumes the recurrence on the rebuilt cache.

ClaimEvidenceStatus
A miss turns the turn's full-context 0.1× read into a partial or full 2× write: up to a ~20× cost spike for that turn.Worst observed: 791,843 tokens re-written in one call, cache_read down to 36,106: ≈ 1.58M input-token equivalents, dozens of normal turns.FACT

FACT The invalidation hierarchy is tools → system → messages: a change invalidates its level and everything after.

Miss triggerLevel
Tool set changes mid-session: an MCP server connects or disconnects, ToolSearch loads a tooltools
Hooks toggled, settings edits, CLAUDE.md reloadsystem
Compaction rewrites the whole message listmessages: full rebuild
Idle past the TTLall: entries expired, the next call re-writes the whole context at write rates. Subscription main conversation: 1h. Subagents, workflows and compaction: 5m unless subagentPromptCacheTtl is set, so a slow fan-out can quietly miss.

Switching thinking levels mid-session

FACT Per the docs' "What invalidates the cache" table (fetched 2026-09-04), the thinking configuration (mode on/off, budget_tokens) is rendered into the prompt. Changing it invalidates the messages-level cache, plus tools and system on models that render it ahead of them. Messages are almost all of a long session's context: treat a mid-session thinking or effort change as a near-full-context 2×-rate rebuild on the next call.

Same for the speed setting (system and message caches), tool_choice and image changes (message blocks).

FACT Thinking blocks cache like other content and count as input when read back. Opus 4.5+ and Sonnet 4.6+ keep previous-turn thinking blocks by default, so a normal user message keeps the cache valid. Older Opus/Sonnet and all Haiku strip prior thinking blocks on non-tool-result user content: the bytes change, invalidating from there.

Switching models mid-session

The docs are silent on whether entries are model-specific. Measured: claude-fable-5 and claude-opus-4-8 share them, on two transcripts with mid-session /model-style switches (8d6cc0a1 in the smine worktree, 5f9a7ba3 in remove-agent-worktree-bug).

fable-5:    in=2  cw=687      cr=187,489    ← last fable call, context ~188k
--- switch ---
opus-4-8:   in=2  cw=0        cr=162,961    ← first opus call READ 163k from
opus-4-8:   in=2  cw=18,659   cr=162,961      cache written by fable, wrote 0
--- switch back ---
fable-5:    in=2  cw=23,317   cr=190,576    ← fable read opus's writes back

The read prefix holds the session's own unique messages, so only the other model can have written it: cross-model reads in both directions.

ClaimEvidenceStatus
For this model pair a switch is a partial miss, not a full rebuild.~87–96% of the context survived; the ~13–25k rebuilt at write rates fits messages-level invalidation (stripped thinking blocks, re-rendered config in recent turns).FACT
This generalizes to other model pairs.Unmeasured. Across model families or tokenizers a switch may be a full-context rebuild; budget for one.HYPOTHESIS

The full rebuild 2.4h after a switch in 8d6cc0a1 was TTL expiry, not the model change.

Pricing reference

FACT Opus 5 prices.

BucketPrice per MTok
Base input$5
5m cache write$6.25
1h cache write$10
Cache read$0.50
Output$25

FACT Fable/Mythos 5.1 reads cost 0.025× ($0.125), 4× cheaper than the 0.1× of non-.1 models.

HYPOTHESIS

Staleness: cost conclusions frozen at 0.1× reads go stale on 5.1-generation models, where 1h writes clearly dominate weighted cost.

Rate limits vs plan usage windows

Two systems meter cache tokens differently.

ClaimEvidenceStatus
API rate limits: input_tokens and cache_creation count toward ITPM, cache_read does not (except Haiku 3.5). Cache-read growth is rate-limit free; a miss costs twice: 2×-rate billing and full ITPM.Rate-limits page, "Cache-aware ITPM".FACT
Subscription 5h and weekly windows: cache reads draw plan usage, at the cached token rate.Claude Code costs doc: a one-line question in a long-open session "still draws usage for the whole conversation". /usage flags long-context and cache-miss behavior at ≥10% of recent usage.FACT
The formula mapping the four fields to window drain.Unpublished; pricing-multiplier weighting is a proxy, not documented arithmetic. The runbook measures it.HYPOTHESIS

Mining usage from transcripts

RuleEvidenceStatus
Dedup by message.idThe JSONL repeats a response's usage on each content-block row; per-row sums inflate 1.84×–3.2×.FACT
Take the per-id max of output_tokensSubagent transcripts stream output: an id's early rows carry partial values (1 → … → 548). First-row dedup undercounted the review-skill run's subagent output 4.7×.FACT
Ignore the iterations arrayDuplicates the top-level usage numbers.FACT
jq -s '[group_by(.message.id)[] | map(.message.usage) | {
    in: .[0].input_tokens, cw: .[0].cache_creation_input_tokens,
    cr: .[0].cache_read_input_tokens, out: (map(.output_tokens) | max)}] |
  {calls: length, input: (map(.in)|add), cache_write: (map(.cw)|add),
   cache_read: (map(.cr)|add), output: (map(.out)|add)}' \
  <(jq -c 'select(.message.usage != null and .message.id != null)' SESSION.jsonl)

Verdicts on the review-skill diagnosis

The peeked diagnosis plan's claims, checked against the transcripts.

ClaimEvidenceStatus
Every call re-reads the full accumulated context as cache_readExact recurrence identity (cache hits).HOLDS
cache_read ≈ Σ(context × calls)Accurate to ~4–5%.HOLDS
Dedup usage by message.idPlus the subagent per-id max for output (mining).HOLDS
Write-once / read-many per turn; input_tokens small~2 per call (usage fields).HOLDS
Run totals 541 calls / 60.8M read / 2.70M write / 575k outputReproduced exactly from the raw transcripts in ~/Downloads/d3337a3f-*, not ~/.claude/projects.HOLDS
"165 API calls" for the orchestratorInternally inconsistent; the transcripts show 80 main-session calls.REFUTED
TTL framing "expiry >5 min"1h on a subscription's main conversation; 5m for subagents and compaction (cache miss).CORRECTED
"Output ruled out at <1% of read volume"As a cost statement it compares categories priced 50× apart. Cost-weighted: ~42% reads ($30.4-equiv), ~37% writes ($27.0), ~20% output ($14.4). Pre-429, reads were 94.6% of raw tokens, ~49% cost-weighted.REFUTED
"Cache-read is what exhausts the 5-hour window"Reads draw plan usage (FACT). Raw-volume metering (reads dominate) vs price-weighted (reads ≈ writes ≈ half) is unmeasured; the runbook settles it.DOWNGRADED to directional
Architectural root cause: a context-accumulating orchestrator plus 17 re-grounding subagentsHolds under either weighting; attribution softens to context re-read compounding plus 2×-rate rebuilds plus output, together.HOLDS

Also refuted: "cache hits don't count against your limit" holds only for API ITPM, not plan windows; a fresh session need not start cold (cache keys); misses never bill as base-rate input_tokens.

Runbook: measure the plan-window weighting

Does the Team-plan 5-hour window drain per raw or per price-weighted token? Each cell isolates one variable in a fresh 5-hour window: read /usage before and after, compute drain per token from the transcript, deduped as in Mining.

  1. Cell 0: control

    Fresh window, zero activity, same wall clock as cell 1. Bounds /usage display drift.

  2. Cell 1: cache-read-heavy

    Build ~150k context (paste a large file), then loop ~30 one-liners ("say ok"): each re-reads ~150k with minimal write and output, ~5M cache_read in total. The /usage delta gives drain per cache_read token. Transcript:

    ls -t ~/.claude/projects/<project-dir>/*.jsonl | head -1
  3. Cell 2: fresh-input-heavy

    Fresh window, same volume as truly uncached input: unique content per turn so no prefix matches, or prompts under the 512-token floor. Wall clock and output matched; decision rule pre-registered below.

  4. Cell 3: write-heavy

    Force misses: toggle a hook (system-prompt change) several times in a large-context session, each a 2×-rate rebuild. Compare drain per write token with cell 1's per read token; pricing predicts ~20×.

  5. Cell 4: output-heavy

    Small stable prompt, long generations. Drain per output token; pricing predicts ~5× the input rate.

  6. Cell 5: TTL checks

    Subagent TTL: fan out Task subagents without subagentPromptCacheTtl, check each subagent transcript's ephemeral_5m vs ephemeral_1h split (docs predict 5m); repeat with subagentPromptCacheTtl: "1h", confirm the flip. Main-session TTL: idle 50 min → full cache_read (1h intact); idle 70 min → full-context cache_creation rebuild.

    jq -s '[.[] | select(.message.usage.cache_creation) | .message.usage.cache_creation] |
      {m5: (map(.ephemeral_5m_input_tokens)|add), h1: (map(.ephemeral_1h_input_tokens)|add)}' \
      <(jq -c 'select(.message.usage != null)' SESSION.jsonl)
Cell 1 vs cell 2 outcomeConclusion
Drain per cache_read token ≈ drain per fresh input tokenRaw-count metering: cache-read is the window killer at volume.
Cell 1 ~10× cheaper per token (~40× on a 5.1 model)Pricing-mirror weighting: writes and output matter as much as reads.
Fold back

Re-invoke the investigation with resumeFromRunId: wf_6d36f04f-246 to make results binding facts. The fan-out replays from cache; only the merge re-runs.

Practical consequences