# Claude prompt caching: add cache_control and read the usage fields

> Prompt caching lets Claude reuse a prompt prefix instead of processing it again. You add a cache_control marker to the last block that stays the same, and later requests that begin with an identical prefix read it from the cache. The first call writes the entry, and the usage fields show whether a later call read it.

Mindket editors · Published 2026-11-04 · Updated 2026-11-05 · https://mindket.com/learn/claude-api/claude-prompt-caching

## In short

- Put stable content first and mark the last block that never changes.
- Check the minimum length for your model, because a short prefix is skipped silently.
- Read cache_creation_input_tokens and cache_read_input_tokens on every response.
- Use cache diagnostics to name the change that caused a miss.

## Before you start

Caching pays off when many requests begin with the same large block: a system prompt, tool definitions, a reference document or a long history. The first request writes that prefix to the cache once its response starts. A later request with an identical prefix reads it back. The docs state that caching has no effect on the output you receive.

You need the setup from [lesson 1](https://mindket.com/learn/claude-api/claude-api-quickstart), and the tool list from [lesson 2](https://mindket.com/learn/claude-api/claude-tool-use) is a typical thing to cache. Caching needs no beta header any more. The [prompt caching page](https://platform.claude.com/docs/en/build-with-claude/prompt-caching) covers the one trap: an old tutorial that calls `client.beta.prompt_caching` fails in current SDKs with an AttributeError. Call `client.messages.create` instead.

## Add a cache breakpoint and prove it worked

The sample reviews text against a style guide kept in a file. The guide is the same on every request, so it carries the breakpoint, and the question that follows it changes. Use a real guide of at least 512 tokens, or the cache is skipped.

````python
import anthropic

client = anthropic.Anthropic()

# Your stable text. It must reach the model's minimum, 512 tokens on Opus 5.5.
STYLE_GUIDE = open("style_guide.md", encoding="utf-8").read()


def ask(question: str):
    return client.messages.create(
        model="claude-opus-5-5",
        max_tokens=500,
        system=[
            {"type": "text", "text": "You review text against the style guide below."},
            {
                "type": "text",
                "text": STYLE_GUIDE,
                "cache_control": {"type": "ephemeral"},
            },
        ],
        messages=[{"role": "user", "content": question}],
    )


for question in ["Is the word 'utilize' allowed?", "How should I write a date?"]:
    usage = ask(question).usage
    print(
        "written:", usage.cache_creation_input_tokens,
        "read:", usage.cache_read_input_tokens,
        "uncached:", usage.input_tokens,
    )
````

1. Put the stable content first. The cache prefix is built in the order `tools`, `system`, then `messages`.
2. Add `"cache_control": {"type": "ephemeral"}` to the last block that is identical in every request.
3. Run the script. The second request starts within five minutes of the first.
4. Read the printed numbers. The first request reports tokens written to the cache, and the second reports the same count as tokens read.
5. If written and read both stay at 0, the prefix is under the model minimum or the marked block changes between calls.

## What a breakpoint covers

A breakpoint caches everything before it, from the first tool down to the marked block. Writes happen only at breakpoints. A read goes the other way: the API looks for an entry at your breakpoint, then walks back through earlier blocks, up to 20 positions, for one that an earlier request wrote. It never finds stable text that was not a breakpoint when it was written.

That rule explains the classic failure. Put a timestamp or the user message inside the marked block and every request writes a new entry and none reads one. Mark the end of the static prefix and let the changing part follow it.

You can place up to four breakpoints, and they cost nothing by themselves. Billing covers only writes, reads and uncached input. A second breakpoint matters in a long conversation: when a turn adds 20 or more blocks past the last write, the walk misses the old entry unless a nearer breakpoint has been writing.

| Aspect | Automatic caching | Explicit breakpoints |
| --- | --- | --- |
| Where it goes | One `cache_control` at the top level of the request | `cache_control` on a tool, system or message block |
| As the chat grows | Moves to the last cacheable block on each request | Stays where you put it |
| Good for | Multi-turn conversations and agent loops | Sections that change at different rates, such as tools, system and documents |
| Slots | Uses one of the four | Up to four |
| Fails when | The last block has an explicit marker with a different TTL, or all four slots are taken | The marked block changes from request to request |

## Minimum lengths by model

A prompt shorter than the minimum is processed without caching, and the API returns no error. You find out only from the usage fields: if both cache counts are 0, the prefix was too short or did not match.

If your prompt falls just under the line, the docs say padding the cached content to the threshold is often worthwhile, because a read costs less than uncached input.

| Minimum cacheable prompt | Models |
| --- | --- |
| 512 tokens | Fable 5.1, Opus 5.5, Sonnet 5.5, Haiku 5.5, Opus 5, Fable 5 |
| 1,024 tokens | Opus 4.8, Sonnet 5, Sonnet 4.6, Sonnet 4.5 |
| 2,048 tokens | Opus 4.7 |
| 4,096 tokens | Opus 4.6, Opus 4.5, Haiku 4.5 |

## Lifetime and cost

The default lifetime is five minutes, counted from the start of the request that wrote or read the entry. Every read refreshes it at no extra charge, so a busy agent keeps its own cache alive. A response that streams for four minutes leaves its follow-up about one minute to start.

For longer gaps, set `"ttl": "1h"` inside `cache_control`. A 1-hour write costs more, and the docs suggest it for side agents that run past five minutes or for chats where the user may not reply soon. When one request mixes both lifetimes, the 1-hour entries must come first. The usage object then splits writes into `ephemeral_5m_input_tokens` and `ephemeral_1h_input_tokens`.

The usage fields add up: total input is `cache_read_input_tokens` plus `cache_creation_input_tokens` plus `input_tokens`. The last one counts only what comes after your final breakpoint, which is why it shrinks once caching works.

**Multipliers on the base input price**

- **1.25x** for a 5-minute cache write (Source: [Anthropic Docs: Prompt caching](https://platform.claude.com/docs/en/build-with-claude/prompt-caching), read 2026-11-05)
- **2x** for a 1-hour cache write (Source: [Anthropic Docs: Prompt caching](https://platform.claude.com/docs/en/build-with-claude/prompt-caching), read 2026-11-05)
- **0.1x** for a cache read on most models (Source: [Anthropic Docs: Prompt caching](https://platform.claude.com/docs/en/build-with-claude/prompt-caching), read 2026-11-05)
- **0.05x** for a cache read on Opus 5.5 and Sonnet 5.5 (Source: [Anthropic Docs: Prompt caching](https://platform.claude.com/docs/en/build-with-claude/prompt-caching), read 2026-11-05)

## What resets the cache

A change at one level of the tools, system, messages order resets that level and every level after it. Tool definitions come first, so touching a name, a description or a parameter throws everything away.

The [cache diagnostics page](https://platform.claude.com/docs/en/build-with-claude/cache-diagnostics) names a cause that surprises people: the cache is per model, so a router or fallback that picks another model starts from nothing. Another comes from JSON writers. Swift and Go, among others, randomize key order, so the keys inside a `tool_use` block must come out in the same order every time.

> **Workspaces do not share a cache**
>
> On the Claude API, caches are isolated by workspace, and organizations never share one even with identical prompts. A hit needs a 100 percent identical prefix, text and images included, inside the same workspace.

| What changes | Tools | System | Messages |
| --- | --- | --- | --- |
| Tool definitions | Reset | Reset | Reset |
| Web search or citations toggle | Kept | Reset | Reset |
| Speed setting, fast or standard | Kept | Reset | Reset |
| `tool_choice` | Kept | Kept | Reset |
| Images added or removed | Kept | Kept | Reset |
| Thinking parameters, `output_config.effort` | Model-specific | Model-specific | Reset |

## Find out why a request missed

Reading usage tells you that the cache missed, not why. Cache diagnostics compares two requests and names the first place they diverged. Pass a `diagnostics` object with `previous_message_id` set to null on the first turn, then pass the previous response id on the next one. It works on the Claude API only. The beta header it once needed is no longer required, though the docs' Python example still goes through `client.beta.messages`.

A reply with `cache_miss_reason` of `model_changed`, `system_changed`, `tools_changed` or `messages_changed` points at the culprit, with `cache_missed_input_tokens` as a rough size of the lost prefix. A `messages_changed` result usually means the history was truncated, edited or re-serialized instead of appended to ([cache diagnostics page](https://platform.claude.com/docs/en/build-with-claude/cache-diagnostics)).

````python
r1 = client.beta.messages.create(
    model="claude-opus-5-5",
    max_tokens=1024,
    cache_control={"type": "ephemeral"},
    system=SYSTEM,
    messages=[{"role": "user", "content": "Summarize section 1."}],
    diagnostics={"previous_message_id": None},  # opt in on the first turn
)

r2 = client.beta.messages.create(
    model="claude-opus-5-5",
    max_tokens=1024,
    cache_control={"type": "ephemeral"},
    system=SYSTEM,
    messages=[
        {"role": "user", "content": "Summarize section 1."},
        {"role": "assistant", "content": r1.content},
        {"role": "user", "content": "Now summarize section 2."},
    ],
    diagnostics={"previous_message_id": r1.id},
)

if r2.diagnostics and r2.diagnostics.cache_miss_reason:
    print(r2.diagnostics.cache_miss_reason.type)
````

- A null result with high cache reads means the prefix held and the cache hit.
- A null result with zero reads means the requests matched but the entry had expired, so shorten the gap or use the 1-hour lifetime.
- A changed type with zero reads means the request changed. Fix what the type names, starting with the first one reported.

## Warm the cache before traffic arrives

An entry exists only after the first response starts. Requests fired at the same moment cannot read it, so send one, wait for its response to begin, then send the rest in parallel.

To fill the cache before a user asks anything, send a pre-warm request. Set `max_tokens` to 0 and put an explicit breakpoint on the last block the real requests share, usually the system prompt or the tool list. It returns an empty `content` array, a `stop_reason` of `max_tokens` and a full `usage` block. No output tokens are billed, but a cache write still is.

The API rejects a zero-token request that sets `stream`, extended thinking, structured outputs or a forced `tool_choice`, and it rejects one inside a batch. Match the real requests' thinking and effort settings, because the cache keys on them.

## Thariq Shihipar and ClaudeDevs on building around the cache

Thariq Shihipar, who worked on Claude Code at Anthropic when he posted it, [wrote on X](https://x.com/trq212/status/2024638793719177291) on 20 February 2026 that agents have to be designed for prompt caching first. Almost every feature touches the cache somehow, in his words. For your own agent, decide where the breakpoints go before you add features that rewrite the system prompt or swap tools mid-session.

ClaudeDevs, Anthropic's account for developers, [shared a tip on 14 May 2026](https://x.com/ClaudeDevs/status/2055069548672631218): send the system prompt before the user's request. Claude writes it to the cache without generating output, so the real request lands on a warm cache. That is the zero-token pre-warm request in this lesson. The docs add a rule the post leaves out: on the default lifetime, re-warm at least every five minutes.

> you fundamentally have to design agents for prompt caching first, almost every feature touches on it somehow
>
> Thariq Shihipar, Formerly on Claude Code at Anthropic, [X](https://x.com/trq212/status/2024638793719177291) (read 2026-11-05)

[![Thariq Shihipar (@trq212) on X, 20 February 2026. One of the biggest realizations I have had working on Claude Code is that you fundamentally have to design agents for prompt caching first, almost every feature touches on it somehow. I wrote this in a day but it is the culmination of months of learnings, hope you enjoy it.](https://mindket.com/blog/shots/x-trq212-prompt-caching-first.webp?v=0cac74185170)](https://x.com/trq212/status/2024638793719177291)

Thariq Shihipar on X, 20 February 2026

[![ClaudeDevs (@ClaudeDevs) on X, 14 May 2026: Useful tip to cut time-to-first-token on longer prompts in the API: pre-warm the prompt cache. Send your system prompt before the user prompt. Claude writes it to the cache, but skips generating any output. When the real user request lands, it will hit a warm cache.](https://mindket.com/blog/shots/x-claudedevs-pre-warm-cache.webp?v=2a74144f28b7)](https://x.com/ClaudeDevs/status/2055069548672631218)

ClaudeDevs on X, 14 May 2026

> Screenshots of X's own embeds of the posts, taken on 5 November 2026, with the time in UTC. Select one to open the post on X.

## Common mistakes

- Marking a block that holds a timestamp, a request id or the user's message.
- Expecting a hit under the model minimum and getting neither a hit nor an error.
- Reading `input_tokens` as the whole input once caching is on.
- Editing, reordering or re-serializing the tool list between requests.
- Switching `tool_choice`, effort or images mid-conversation and wondering why the messages stopped hitting.
- Counting on the cache after a pause longer than its lifetime.
- Expecting a second workspace to share the cache.

## Try it yourself

- Run the sample twice and print the three usage numbers. Then change one character in the style guide and run again. Which number moves?
- Add a timestamp to the first system block, above the breakpoint. Run twice and watch the read count stay at 0.
- Set `"ttl": "1h"` and print `usage.cache_creation` to see the two lifetimes counted apart.
- Wait six minutes between two calls and see what the usage fields say.

## Next

[Lesson 4](https://mindket.com/learn/claude-api/build-ai-agent-claude-api) writes the agent loop with a top-level `cache_control` on every call, so the history before each new turn is read from the cache. The cache only saves work on input, and [the context window lesson](https://mindket.com/learn/prompt-engineering/context-window) explains what still has to fit.

## Check yourself

1. Where should the cache_control breakpoint go?

   A. On the user message, since it changes every time
   B. On the first tool only
   C. On every block in the request
   D. On the last block that stays identical across requests

   Answer: D. Writes happen only at breakpoints, so a breakpoint on a changing block never produces a read.

2. Both cache usage fields are 0 on a call that carries cache_control. What is the likely cause?

   A. The prefix was shorter than the model minimum
   B. The API key lacks access
   C. The response was streamed
   D. max_tokens was set too high

   Answer: A. A prefix below the minimum is processed without caching and no error is returned.

3. With caching on, what does input_tokens in usage count?

   A. Every token in the request
   B. Only the tokens after the last breakpoint
   C. Only the tokens Claude generated
   D. The tokens read from the cache

   Answer: B. Total input is cache reads plus cache writes plus input_tokens, and the last one covers only what follows the final breakpoint.

4. Which change resets the tools, system and messages caches together?

   A. Changing tool_choice
   B. Adding an image
   C. Editing a tool definition
   D. Switching web search on

   Answer: C. Tools come first in the cache order, so a changed tool definition resets every level after it.

## Frequently asked questions

### Does caching change what Claude answers?

No. The docs say prompt caching has no effect on output token generation, so you get the same response you would get without it.

### Can I use caching with the Batch API?

Yes, but hits are best effort, because batch requests can run concurrently and in any order. The docs suggest sending one request with the shared prefix and a 1-hour cache block first, then the rest of the batch.

### Can I clear the cache by hand?

No. There is no way to clear it. An entry expires after a minimum of five minutes without use, or after its 1-hour lifetime.

### Are thinking blocks cached?

They cannot carry cache_control themselves. When they sit in earlier assistant turns they are cached along with the other content, and they count as input tokens when read from the cache.

### What does a cache read cost compared with normal input?

On 5 November 2026 the docs list 0.1 times the base input price for most models, 0.05 times for Opus and Sonnet 5.5, and 0.025 times for Fable 5.1. Writes cost more than normal input, so a prefix used only once never pays back.

## Sources

- [Anthropic Docs: Prompt caching](https://platform.claude.com/docs/en/build-with-claude/prompt-caching) (read 2026-11-05)
- [Anthropic Docs: Cache diagnostics](https://platform.claude.com/docs/en/build-with-claude/cache-diagnostics) (read 2026-11-05)
- [Anthropic Docs: Models overview](https://platform.claude.com/docs/en/models/overview) (read 2026-11-05)
- [X: Thariq Shihipar on designing agents for prompt caching first](https://x.com/trq212/status/2024638793719177291) (read 2026-11-05)
- [X: ClaudeDevs on pre-warming the prompt cache](https://x.com/ClaudeDevs/status/2055069548672631218) (read 2026-11-05)

[Browse AI agents](https://mindket.com/agents)
