Prompt Caching
Prompt caching lets you mark a point in a request after which Claude's API can reuse previously processed content instead of reprocessing it from scratch — cutting cost by up to 90% and latency substantially on any request that repeats a large, unchanging prefix.
Imagine a study buddy who rereads the entire textbook from page one before answering every single question you ask — even if you've asked five questions in a row about the same chapter. Prompt caching is like handing that buddy a bookmark: once they've read up to the bookmark, they remember everything before it, so each new question after it is fast and cheap. Move the bookmark or change a word on an earlier page, though, and they have to start over from there.
The problem: paying full price for the same context, over and over
☺ Like you're 10: It's like re-reading the whole instruction manual from page one every time a friend asks a two-word question, instead of just picking up where you left off.
Once your prompts grow past a "hello world" call, a pattern shows up fast: most of what you send on every request is identical to what you sent last time. A support bot resends its entire product manual as a system prompt on every question. A coding agent resends the same twelve tool definitions on every turn. A document-analysis app resends the same 40-page PDF on every follow-up question about it.
Without caching, the API reprocesses that entire prefix from the first token every single time — you pay full input-token price and full processing latency for content that hasn't changed since the last call. If your system prompt plus tool definitions plus reference document adds up to 20,000 tokens and the actual user question is 15 tokens, you're paying for 20,000 tokens of redundant work on every turn of the conversation.
Prompt caching fixes this by letting the API remember the processed state of a prefix and reuse it on a subsequent request, as long as that prefix is byte-for-byte identical to what was cached.
How cache_control breakpoints work
☺ Like you're 10: It's a bookmark, not a highlighter — everything from the start of the book up to where you stick the bookmark gets remembered; nothing after it does.
You mark a cache breakpoint by adding a cache_control field to a content block: {"type": "ephemeral"}. That marker tells the API "cache everything in this request up through this block." On the next request, if the API finds a prior cache entry whose content matches that same prefix exactly, it reads the cached version instead of reprocessing it — you're billed at a fraction of the normal input price for those tokens, and the response starts faster.
The request body is rendered in a fixed order: tools, then system, then messages. A breakpoint on the last block of your system prompt caches the tool definitions and the system prompt together, since both come before it in render order. You can place up to 4 cache breakpoints in a single request, and each breakpoint looks back at most 20 content blocks to find a matching prior entry — long agentic turns with many tool calls may need an intermediate breakpoint to stay within that lookback window.
The mechanism is a strict prefix match: the cache key is derived from the exact bytes of the prompt up to the breakpoint. Change one character anywhere before a breakpoint — reorder a JSON key, swap a tool, interpolate a timestamp — and every breakpoint at or after that position misses.
What goes before the breakpoint, what stays dynamic
The candidates worth caching are the parts of a prompt that are large and don't change from request to request: long system instructions, a fixed set of tool definitions, a reference document or codebase the model needs on every turn, or a block of few-shot examples. Everything that genuinely varies per request — the user's actual question, retrieved search results, a timestamp — has to stay after the last breakpoint, or it will invalidate the cache on every single call.
Keep the system prompt byte-identical across requests and mark its last block with cache_control. The user's question goes in messages, after the breakpoint, where it's free to change every time without touching the cached prefix.
Interpolating f"Current date: {datetime.now()}" or a session ID into the system prompt. That string sits at the very front of the prefix, so it changes on every request and invalidates the entire cache — tools, system prompt, everything downstream of it — even though the rest of the prompt was static.
Foxy: I stamped today's date at the top of my system prompt so Claude always knows what day it is. Why did my cache savings vanish?
Professor Owl: Because the cache is a strict prefix match, not a "close enough" match. That timestamp sits before your breakpoint, and it changes on every request — so everything after it, tools and instructions included, gets reprocessed from scratch.
Foxy: So where should the date actually live?
Ellie: After the breakpoint, in the user's turn — that's the part I never bother memorizing anyway. Keep the reusable stuff first and byte-identical, and I'll hold onto it for you every time.
The cost and latency tradeoff
☺ Like you're 10: Storing something in memory takes a little extra effort the first time; recalling it later is nearly free. That's the whole trade.
Caching isn't free — writing an entry costs more than a normal input token, because the API has to reprocess and store it. Reading a cache hit is cheap. The economics depend on the TTL (time-to-live) you choose:
| Cache TTL | Write cost (vs. base input price) | Read cost (vs. base input price) | Requests to break even |
|---|---|---|---|
| 5 minutes (default) | 1.25× | ~0.1× (about 90% cheaper) | 2 |
1 hour (opt-in via ttl: "1h") | 2× | ~0.1× | 3 |
In other words: the first request that writes a cache entry costs slightly more than an uncached call. Every request after that, within the TTL, that reads the same prefix costs roughly a tenth of the normal input price for those tokens. For a chat session where the same large system prompt gets reused across dozens of turns, or a batch job that asks a hundred questions about the same document, the savings compound quickly.
There's also a minimum size below which a prefix won't cache at all — no error, it just silently doesn't create an entry (you'll see cache_creation_input_tokens: 0). The minimum sits somewhere in the range of roughly 512–4,096 tokens depending on which model you're calling, with larger/faster models generally tolerating a smaller minimum — check the current per-model minimums on platform.claude.com before relying on an exact number.
Caching has a side benefit beyond price: for most models, only uncached input tokens count against your per-minute input-token rate limit. A well-cached system prompt effectively raises your real throughput without needing a rate-limit increase.
Placing cache_control in a request
The marker goes directly on the content block you want the cache boundary to end at — most commonly the last block of your system prompt, or the last tool definition if you want to cache only the tools.
import anthropic
client = anthropic.Anthropic()
response = client.messages.create(
model="claude-opus-5",
max_tokens=1024,
system=[
{
"type": "text",
"text": (
"You are a support agent for Acme Cloud. Use the product "
"manual below to answer questions.\n\n" + PRODUCT_MANUAL_TEXT
),
"cache_control": {"type": "ephemeral"}, # cache everything up to here
}
],
messages=[
{"role": "user", "content": "How do I rotate an API key?"}
],
)
for block in response.content:
if block.type == "text":
print(block.text)
# Inspect the usage block to confirm caching is actually happening
print(response.usage.cache_creation_input_tokens) # tokens written to cache (first call)
print(response.usage.cache_read_input_tokens) # tokens served from cache (later calls)
print(response.usage.input_tokens) # tokens processed at full priceimport Anthropic from "@anthropic-ai/sdk";
const client = new Anthropic();
const response = await client.messages.create({
model: "claude-opus-5",
max_tokens: 1024,
system: [
{
type: "text",
text: `You are a support agent for Acme Cloud. Use the product manual below to answer questions.\n\n${PRODUCT_MANUAL_TEXT}`,
cache_control: { type: "ephemeral" } // cache everything up to here
}
],
messages: [
{ role: "user", content: "How do I rotate an API key?" }
]
});
for (const block of response.content) {
if (block.type === "text") {
console.log(block.text);
}
}
console.log(response.usage.cache_creation_input_tokens);
console.log(response.usage.cache_read_input_tokens);
console.log(response.usage.input_tokens);The same field works on tool definitions. If your tool list is large and stable, mark the last tool instead (or in addition) to cache the tool schemas independently of the system prompt:
"tools": [
{
"name": "get_weather",
"description": "Get the current weather in a given location",
"input_schema": { "type": "object", "properties": { "location": {"type": "string"} }, "required": ["location"] }
},
{
"name": "search_docs",
"description": "Search the internal knowledge base",
"input_schema": { "type": "object", "properties": { "query": {"type": "string"} }, "required": ["query"] },
"cache_control": { "type": "ephemeral" }
}
]Run the same request twice with an unchanged prefix and you'll see the shift directly in usage: the first call reports tokens under cache_creation_input_tokens (the write), and the second call reports the same tokens under cache_read_input_tokens (the read) at roughly a tenth of the price.
Gotchas: expiry and invalidation
☺ Like you're 10: It's like your bookmark falling out if you leave the book alone too long, or someone sneaking in and changing a word on page one — either way, you're starting over from the top.
Cache entries expire. Each cache entry has a TTL — 5 minutes by default, or 1 hour if you set "cache_control": {"type": "ephemeral", "ttl": "1h"}. If no request reads that entry within the TTL window, it's gone, and the next request pays the full write cost again. For bursty traffic with long idle gaps, either keep requests flowing more often than the TTL, or move to the 1-hour TTL and accept its higher write cost in exchange for surviving longer gaps.
Any edit invalidates the cache from that point forward. Caching is a strict prefix match — there's no partial credit. If you interpolate a per-request value (a date, a UUID, a session ID) anywhere before your breakpoint, or reorder a JSON object's keys non-deterministically, or add or remove a tool, every request misses the cache from that byte onward. This is the single most common reason caching "doesn't work" — the fix is almost always to move the dynamic value after the last breakpoint, or make the serialization deterministic.
Build a system prompt with at least 1,200–2,000 words of static reference text (paste in a long README or license file if you don't have one handy) and mark its last block with cache_control: {"type": "ephemeral"}. Send the same request twice in a row with two different one-line user questions in messages. Print response.usage.cache_creation_input_tokens and response.usage.cache_read_input_tokens after each call — you should see the first call write the cache and the second call read it. Then change one word inside the system prompt and send a third request: watch the read count drop back to zero as the prefix match breaks.
You should now be able to explain why prompt caching exists (paying full price and full latency for a repeated prefix on every request), where a cache_control breakpoint has to sit so tools and system prompt get cached together, and why a single byte changed before that breakpoint — a timestamp, a reordered key, a swapped tool — quietly wipes out the whole cache. Next up: see how extended thinking changes the shape of a response entirely.
Check your answers
- What does a cache_control breakpoint actually cache, and in what order? Everything from the start of the request up through the marked content block, following the request's fixed render order of tools, then system, then messages — so a breakpoint on the last system block caches the tool definitions and the system prompt together.
- Why did Foxy's date-in-the-system-prompt trick break his caching? Because caching is a strict byte-for-byte prefix match; a value that changes on every request, like a live timestamp, placed before the breakpoint invalidates everything from that point onward, including the static content that follows it.
- Roughly how much cheaper is a cache read than a normal input token, and what's the catch? A cache read costs roughly a tenth (~0.1×) of the base input price, but a cache write costs more upfront — 1.25× for the default 5-minute TTL or 2× for a 1-hour TTL — and entries expire if nothing reads them within the TTL, so it takes 2–3 requests sharing the same prefix to break even.