How to Use Claude Prompt Caching and Read Cache Usage
Turn on Claude prompt caching with one field, place breakpoints, know the 5-minute TTL, and read cache usage in responses to confirm you get hits.
Claude prompt caching lets the API reuse a prompt prefix it has already processed, so repeated requests with the same beginning run faster and cost less. You turn it on by adding cache_control to your request. You confirm it worked by reading two fields in the response usage object: cache_creation_input_tokens and cache_read_input_tokens.
The default cache lifetime is 5 minutes, refreshed at no extra cost each time the cached content is used. Below: the smallest working request, how breakpoints and invalidation work, what it costs, and why a cache that "doesn't work" usually comes down to a handful of causes.
How to enable Claude prompt caching#
There are two ways. Automatic caching puts a single cache_control field at the top level of the request. The system applies the breakpoint to the last cacheable block and moves it forward as the conversation grows. Explicit breakpoints put cache_control on individual content blocks when you want fine-grained control.
I'd start with automatic caching. This is the smallest example from the docs:
import anthropic
client = anthropic.Anthropic()
response = client.messages.create(
model="claude-opus-5-5",
max_tokens=1024,
cache_control={"type": "ephemeral"},
system="You are an AI assistant tasked with analyzing literary works. Your goal is to provide insightful commentary on themes, characters, and writing style.",
messages=[
{
"role": "user",
"content": "Analyze the major themes in 'Pride and Prejudice'.",
}
],
)
print(response.usage.model_dump_json())
With automatic caching, everything up to and including the last cacheable block is cached. Later requests with the same prefix reuse it.
One gotcha: this no longer needs a beta prefix. If you see AttributeError: 'Beta' object has no attribute 'prompt_caching', you're running old example code. Replace client.beta.prompt_caching.messages.create(**params) with client.messages.create(**params).
That example won't actually cache anything, though. The system prompt is far below the minimum length (more on that below). It shows the shape of the request, not a hit.
How Claude prompt caching works#
When a request comes in with caching enabled:
- The system checks whether the prompt prefix, up to the cache breakpoint, is already cached from a recent query.
- If it is, the cached version is used, which cuts processing time and cost.
- If not, the full prompt is processed and the prefix is cached once the response begins.
That last point matters for parallel work. A cache entry only becomes available after the first response begins. If you fire ten identical requests at once, none of them benefit from the others. Send one, wait for the response to start, then send the rest.
Caching pays off most for prompts with many examples, large context or background material, repetitive tasks with consistent instructions, and long multi-turn conversations.
Cache keys come from a cryptographic hash of the prompt up to the cache control point, so only requests with identical prompts can hit a given entry.
Cache breakpoints and what gets cached#
The cache follows a fixed hierarchy: tools, then system, then messages. A breakpoint caches everything before it in that order. So a stable tool list and system prompt sit at the front, and the changing conversation sits at the end.
When you need control, put cache_control on a content block instead of the top level:
response = client.messages.create(
model="claude-sonnet-5-5",
max_tokens=1024,
messages=[
{
"role": "user",
"content": [
{
"type": "text",
"text": LONG_REFERENCE_DOCUMENT,
"cache_control": {"type": "ephemeral"},
},
{
"type": "text",
"text": "Summarize section 3.",
},
],
}
],
)
LONG_REFERENCE_DOCUMENT is a placeholder for your own large, stable text. The rule from the docs: put the breakpoint on the last block that stays identical across requests. For a static prefix followed by a varying suffix (timestamps, per-request context, the incoming user message), that's the end of the prefix, not the varying block.
If you put the breakpoint on something that changes every request, it writes a fresh entry each time and never hits. It's safe to use cache_control anywhere, but it only produces reads when the content before it is stable.
Use explicit breakpoints when different sections change at different rates. For example, a rarely changing instruction block and a document that changes daily can each get their own breakpoint.
Thinking blocks can't be marked with cache_control directly. They get cached as part of the request content on later calls, for example when you pass them back along with tool results. When they're read from cache, they count as input tokens in your usage.
Claude prompt caching TTL and timing#
The default lifetime is 5 minutes, and "ephemeral" is currently the only supported cache type. Every time the cached content is used, the cache is refreshed at no additional cost.
The timing detail people miss: the lifetime is measured from the start of the request that writes or reads the cache entry, not from the end of its response. Generation time counts against it. If a response takes 4 minutes to stream, a follow-up that reuses the same prefix has to start within about 1 minute of that response finishing.
The pricing table also lists a 1-hour write rate, but the pages I'm working from don't show the request syntax for it, so check the current docs before relying on a longer lifetime.
Minimum length: why short prompts don't cache#
Prompts shorter than the model's minimum are processed without caching, and no error is returned. This is the most common reason people think caching is broken. The minimums on the Claude API, Claude Platform on AWS, Google Cloud, and Microsoft Foundry (and they apply on every platform where the model is available, Bedrock included):
| Minimum cacheable tokens | Models |
|---|---|
| 512 | Claude Fable 5.1, Mythos 5.1, Opus 5.5, Opus 5, Sonnet 5.5, Fable 5, Mythos 5, Haiku 5.5 |
| 1,024 | Opus 4.8, Sonnet 5, Sonnet 4.6, Sonnet 4.5, Opus 4.1, Opus 4, Sonnet 4 |
| 2,048 | Mythos Preview, Opus 4.7, Haiku 3.5 |
| 4,096 | Opus 4.6, Opus 4.5, Haiku 4.5 |
If your prompt falls just short, padding the cached content up to the threshold is often worthwhile, because cache reads cost much less than uncached input tokens.
How to read cache usage in responses#
Print response.usage and look at two fields:
cache_creation_input_tokens: tokens written to the cache on this request.cache_read_input_tokens: tokens served from the cache on this request.
What the combinations mean:
| Creation | Read | Meaning |
|---|---|---|
| greater than 0 | 0 | Cache miss, new entry written. Normal on the first request. |
| 0 | greater than 0 | Cache hit. |
| 0 | 0 | Not cached. Likely the prompt is under the minimum length. |
The docs state the last row directly: if both are 0, the prompt was not cached, likely because it didn't meet the minimum. A good test is to send the same long request twice, in sequence, and compare the two usage objects. The second should show reads where the first showed creation.
Here's a small helper you can run to classify a usage dict and estimate cost. The prices are Claude Sonnet 5.5 from the pricing table: $2 / MTok input, $2.50 / MTok for 5-minute writes, and $0.10 / MTok for hits.
INPUT, WRITE_5M, HIT = 2.00, 2.50, 0.10 # $ per million tokens, Sonnet 5.5
def classify(usage):
created = usage.get("cache_creation_input_tokens", 0)
read = usage.get("cache_read_input_tokens", 0)
if created == 0 and read == 0:
return "not cached (check the minimum prompt length)"
if read > 0:
return "cache hit"
return "cache write (first request or expired entry)"
def cost(tokens, rate):
return tokens / 1_000_000 * rate
prefix = 20_000 # tokens in the stable prefix
requests = 10
uncached = requests * cost(prefix, INPUT)
cached = cost(prefix, WRITE_5M) + (requests - 1) * cost(prefix, HIT)
print(classify({"cache_creation_input_tokens": 0, "cache_read_input_tokens": 0}))
print(classify({"cache_creation_input_tokens": prefix, "cache_read_input_tokens": 0}))
print(classify({"cache_creation_input_tokens": 0, "cache_read_input_tokens": prefix}))
print(f"prefix cost, {requests} requests uncached: ${uncached:.4f}")
print(f"prefix cost, {requests} requests cached: ${cached:.4f}")
The numbers only cover the stable prefix. Output tokens and the varying part of each prompt are billed separately at normal rates.
Claude prompt caching pricing#
Caching adds a write price and a much lower read price. Per the FAQ, 5-minute cache writes cost 25% more than base input tokens, 1-hour writes cost 2x base input, and hits cost a fraction of base input.
Per million tokens, for the current main models:
| Model | Input | 5m writes | 1h writes | Hits and refreshes |
|---|---|---|---|---|
| Claude Fable 5.1 | $10 / MTok | $12.50 / MTok | $20 / MTok | $0.25 / MTok |
| Claude Opus 5.5 | $4 / MTok | $5 / MTok | $8 / MTok | $0.20 / MTok |
| Claude Sonnet 5.5 | $2 / MTok | $2.50 / MTok | $4 / MTok | $0.10 / MTok |
Hits and refreshes on Fable 5.1 and Mythos 5.1 are priced at 0.025x the base input price, and on Opus 5.5 and Sonnet 5.5 at 0.05x. All other models use the standard 0.1x multiplier. Claude Haiku 5.5 has tiered pricing by prompt size, so check the table for it. The full list, including older models, is on the official pricing section.
The break-even is simple: you pay slightly more on the write, then save on every hit. A prefix that's reused once or twice within the lifetime may barely matter. One reused many times is where it pays.
Why prompt caching isn't working: common causes#
When reads stay at 0, work through this list.
- Prompt under the minimum. Covered above. No error, just no caching.
- Breakpoint on changing content. A timestamp, a user message, or per-request data before or at the breakpoint changes the prefix every time. Move the breakpoint to the end of the stable part.
- Changed tool definitions. Editing tool names, descriptions, or parameters invalidates the entire cache: tools, system, and messages.
- Toggled web search or citations. Both change the system prompt, which invalidates the system and messages caches. Tools stay valid.
- Changed
tool_choice. Only the messages cache is affected. - Added or removed images. Anywhere in the prompt, this affects the messages cache.
- Changed thinking settings. Switching modes or changing the budget invalidates cached message prefixes, and can invalidate system and tools on some models. The
output_config.effortvalue behaves the same way. Setting effort explicitly to the model's default is equivalent to omitting it and doesn't invalidate. - Speed setting. Switching between
speed: "fast"and standard speed invalidates the system and message caches. - Parallel first requests. The entry doesn't exist until the first response begins.
- Waiting too long. The default lifetime is 5 minutes, measured from the start of the request.
Because of the hierarchy, a change at one level invalidates that level and everything after it. So keep the things that rarely change (tools, then system) at the front, and don't edit them mid-session.
For Claude Code and SillyTavern, I'm not covering their settings here because the official caching docs I used don't describe them. The mechanics above still apply to whatever requests those tools send: stable prefix first, check the usage fields. Bedrock follows the same minimums, and caches there are isolated per organization. On the Claude API, Claude Platform on AWS, and Microsoft Foundry, they're isolated per workspace.
Quick reference checklist#
- Start with top-level
cache_control={"type": "ephemeral"}for multi-turn chats. - Use block-level
cache_controlwhen sections change at different rates. - Put stable content first: tools, then system, then messages.
- Put the breakpoint on the last block that stays identical across requests.
- Check your model's minimum (512 to 4,096 tokens).
- Read
cache_creation_input_tokensandcache_read_input_tokenson every response while tuning. - Both at 0 means nothing was cached.
- Don't edit tool definitions mid-conversation.
- Send the next request within 5 minutes of the start of the previous one.
- Wait for the first response to begin before sending parallel requests.
- Track your hit rate over time and adjust.
For more patterns, see the prompt caching cookbook linked from the prompt caching docs.
Quick answers
How long does Claude prompt caching last?
By default the cache has a 5-minute lifetime, refreshed at no extra cost each time the cached content is used. The lifetime is measured from the start of the request, so streaming time counts against it.
Why are both cache usage fields 0?
The prompt was not cached, most likely because it's shorter than the minimum for your model (512, 1,024, 2,048, or 4,096 tokens depending on the model). No error is returned in that case.
Is Claude prompt caching still a beta feature?
No. It no longer needs the beta prefix. Use client.messages.create(params) instead of client.beta.prompt_caching.messages.create(params).
Does prompt caching work on Bedrock?
The same minimum prompt lengths apply on every platform where a model is available. On Bedrock and Google Cloud, caches are isolated per organization.
How much does a cache hit cost?
It depends on the model. For Claude Sonnet 5.5, hits and refreshes are $0.10 / MTok against $2 / MTok for base input. Most other models use the standard 0.1x multiplier on the base input price.
Did this solve it?
One tap. No account.
Sorry about that. What was missing?
from 0 readers
Saved you an hour? Send a tip.
USDC or ETH on Base. Keeps the notes free and the examples running.

0x9dFD2Ee86182f92464b50809C6702B999a0Cd54f
On a computer? Scan the code with your phone wallet, or copy the address.
USDC or ETH on Base works best. Other tokens on Base, Ethereum or Arbitrum also reach this address. Tips are optional and unlock nothing; every guide stays free.
Discussion
Start the discussion
Next note