Anthropic’s minimum cacheable prefix is model-dependent, and the ordering is the opposite of the obvious guess: the newest and most expensive models have the lowest floor. A prefix below the floor is not cached, no error is raised, and cache_creation_input_tokens stays at 0.
| Minimum prefix | Models |
|---|---|
| 512 tokens | Opus 5, Fable 5, Mythos 5 |
| 1,024 tokens | Opus 4.8, Sonnet 5, Sonnet 4.6, Sonnet 4.5, Opus 4.1, Opus 4, Sonnet 4 |
| 2,048 tokens | Opus 4.7, Mythos Preview, Haiku 3.5 |
| 4,096 tokens | Opus 4.6, Opus 4.5, Haiku 4.5 |
These floors apply on every platform where the model is available. The old Amazon Bedrock override for Fable 5 was removed and no per-platform exception remains.
The routing trap
The non-monotonic ordering has one practical consequence worth more than the table itself.
A 3,000-token policy prefix caches on Opus 5 and silently never caches on Haiku 4.5. So the moment you do the sensible thing and fan high-volume work out to the cheap tier, that tier starts paying full input price on every call. The cost model you validated on the expensive model does not transfer down.
Check the floor for the model you route to, not the one you developed against. If you are tiering work across models, this belongs in the same review as the price table in Stop paying full price for repeat LLM calls.
Opus 5 moved the other way: it halved the Opus 4.8 floor from 1,024 to 512, so prompts previously too short to cache now create entries with no code change. Worth re-checking anything you wrote off as uncacheable.
Verify, don’t assume
Three fields in usage tell you what actually happened:
| Field | Meaning |
|---|---|
cache_creation_input_tokens | Written to cache this request (paid the write premium) |
cache_read_input_tokens | Served from cache this request (paid ~0.1x) |
input_tokens | Processed at full price - the uncached remainder only |
Total prompt size is input_tokens + cache_creation_input_tokens + cache_read_input_tokens. That last point catches people out: if a long-running agent reports 4,000 input_tokens, the rest was served from cache. Check the sum, not the single field.
The one-line diagnosis: if cache_read_input_tokens is 0 across repeated identical requests, either the prefix is changing or it is under the floor. Both are silent.
u = client.messages.create(**build_request()).usage
print(u.cache_creation_input_tokens, u.cache_read_input_tokens, u.input_tokens)
# call 1: write>0, read=0 -> healthy, cache written
# call 2: write=0, read>0 -> healthy, cache read
# call 2: write>0, read=0 -> prefix changing every call
# both 0 across calls -> prefix is under the model's floor
To tell “changing” from “too short”, build the request twice and diff it - the byte that moved is rarely the suspected one. Full procedure in Don’t break the prompt cache; the one-line rule is cache-stable prefix.
Breakpoints are a budget of four
Maximum 4 cache_control breakpoints per request, counted across tools + system + messages combined. Top-level automatic caching consumes one slot.
Exceeding the limit returns a 400, which is the good outcome. The bad outcome is an agent loop that appends a breakpoint per turn as history grows, exhausts the budget mid-session, and starts rewriting the prefix instead of reading it.
Render order is tools -> system -> messages, and caching matches everything up to and including a marked block. So spend the budget from the front: one breakpoint after tool schemas, one after the system prompt, and give a rolling message window whatever remains.
system=[{
"type": "text",
"text": SYSTEM_PROMPT,
"cache_control": {"type": "ephemeral"}, # 5-minute TTL (default)
# "cache_control": {"type": "ephemeral", "ttl": "1h"},
}]
What invalidates what
Not every parameter change costs you the whole cache. There are three tiers, and a change only invalidates its own tier and below.
| Change | Tools cache | System cache | Messages cache |
|---|---|---|---|
| Tool definitions added, removed, reordered | lost | lost | lost |
| Model switch | lost | lost | lost |
| System prompt content | kept | lost | lost |
tool_choice, images, thinking on/off | kept | kept | lost |
| Message content | kept | kept | lost |
The useful read: you can flip tool_choice or toggle thinking per request without losing the tools-and-system cache. Only tool-definition and model changes force a full rebuild.
Two of those rows have an escape hatch, and they are gated separately:
- System prompt content - append a
{"role": "system", "content": "..."}message tomessagesinstead of editing top-levelsystem. Available today with no beta header on Opus 5, Opus 4.8, Fable 5, and Mythos 5. Not on Sonnet 5, which returns a 400. - Tool definitions -
tool_addition/tool_removalblocks, Opus 5 onward behind themid-conversation-tool-changes-2026-07-01beta. The tool must already be declared withdefer_loading: true.
Model switching has no escape hatch: caches are model-scoped. Keep the main loop on one model and push cheaper sub-tasks to a subagent.
Two silent failures that are not the floor
The 20-block lookback. Each breakpoint walks back at most 20 content blocks looking for a prior cache entry. A single turn that appends more than 20 blocks - routine in an agent loop with many tool-use and tool-result pairs - pushes the previous entry out of range, and the next request silently misses. Fix: place an intermediate breakpoint roughly every 15 blocks in long turns.
Concurrent requests all pay. A cache entry becomes readable only once the first response begins streaming. Fire N identical-prefix requests in parallel and every one of them pays full price, because none can read what the others are still writing. For fan-out, send one request, wait for its first streamed token, then release the rest.
Economics, and when caching is a loss
Cache reads cost ~0.1x base input. Writes cost 1.25x for the 5-minute TTL and 2x for the 1-hour TTL, which sets different break-even points:
| TTL | Write cost | Break-even |
|---|---|---|
| 5 minutes | 1.25x | 2 requests (1.25 + 0.1 = 1.35x vs 2x uncached) |
| 1 hour | 2x | 3 requests (2 + 0.2 = 2.2x vs 3x uncached) |
So caching a prefix you call exactly once is a pure loss: you pay the write premium and never read it. The 1-hour TTL keeps entries alive across gaps in bursty traffic, but the doubled write means it needs more reads to pay for itself.
Pre-warming. To remove the cold-write latency from the first real request, send a max_tokens: 0 request at startup. It runs prefill, writes the cache, returns immediately with empty content, and bills no output tokens. Put cache_control on the last block shared with the real request - the system prompt or tool schemas - never on the placeholder user message. It is rejected with stream: true, with thinking explicitly enabled, with output_config.format, with a forced tool_choice, and inside a Batches request.
Only pre-warm when first-request latency is user-visible and there is a quiet moment before traffic. If requests already arrive more often than the TTL, they keep the cache warm themselves and a separate warm call is pure extra write.
Next move
Look up the floor for whatever model your highest-volume endpoint routes to, then log cache_read_input_tokens on it for a day. If it reads 0, you now have two candidate causes and a way to tell them apart. The placement discipline is in Don’t break the prompt cache, the output side of the bill is in Trim output, not the cache, and the full lever map is on the Optimization hub.