How to Cache Prompts With Claude API: Full Guide
Prompt caching lets you reuse large, unchanging chunks of context — system prompts, documents, tool definitions — across multiple API calls without paying full price or full latency every time. You enable it by marking specific blocks of your request with cache_control, and Claude stores that processed context server-side for a short window so subsequent calls skip re-processing it.
This matters anywhere you send the same long context repeatedly: a coding assistant that reloads a 20,000-token codebase on every turn, a support bot that always injects the same product docs, or an agent with a big static tool schema. Below is exactly how to set it up, what counts as cacheable, and the mistakes that silently break caching.
How prompt caching actually works
When you mark content with cache_control, Claude breaks your request into segments. Cacheable segments get hashed and stored after the first request. On the next request, if the same segment appears in the same position with identical content, Claude reuses the cached version instead of reprocessing it from scratch.
Two things make this different from just "sending the same prompt twice":
- Order matters. Cached content must appear in the same place in the request structure every time. If you reorder blocks, the cache misses.
- Caches expire. They live for a few minutes of inactivity. If your traffic is bursty with long gaps, you won't get consistent cache hits.
You're not caching the response — you're caching the processed input. The model still generates a fresh completion each time; it just skips re-encoding the parts you've marked.
Setting up cache_control in a request
Here's a minimal example using a long system prompt as the cached block:
curl https://api.anthropic.com/v1/messages \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "content-type: application/json" \
-d '{
"model": "claude-3-5-sonnet-20241022",
"max_tokens": 1024,
"system": [
{
"type": "text",
"text": "You are a support agent. Here is the full product manual: ...",
"cache_control": {"type": "ephemeral"}
}
],
"messages": [
{"role": "user", "content": "How do I reset my password?"}
]
}'
The cache_control object goes on the specific block you want cached, not on the whole request. On the first call this block is written to the cache; on the next call within the cache window, Claude reads it instead of reprocessing, and the response comes back faster with lower input-token billing for that segment.
The same pattern works for:
- Long documents in user messages — wrap the document block in its own content object with
cache_control. - Tool definitions — if you register many tools with detailed schemas, cache the tools array so it isn't re-parsed every turn.
- Few-shot examples — a fixed set of example conversations used as context for every request.
What you should (and shouldn't) cache
Good candidates:
- System prompts over a few hundred tokens that don't change between requests
- Reference documents, API specs, or codebases injected as context
- Tool/function definitions that stay fixed across a session
- Static few-shot examples
Bad candidates:
- Content that changes every single call (user questions, live data, timestamps)
- Very short blocks — the overhead of managing the cache isn't worth it below roughly 1,000 tokens
- Content you expect to call once and never again in the cache window
A common mistake is caching the entire request including the user's message. Since the user's question changes every time, that invalidates the whole cache. Split your request so static content is isolated into its own cached block, and dynamic content (the actual question) stays uncached and separate.
Structuring multi-turn conversations with caching
For a chat-style agent where you re-send conversation history each turn, cache the stable prefix (system prompt + early turns) and leave the newest message uncached:
{
"system": [
{"type": "text", "text": "You are a coding assistant...", "cache_control": {"type": "ephemeral"}}
],
"messages": [
{"role": "user", "content": "Here's my codebase: ... (large)"},
{"role": "assistant", "content": "Got it, I've reviewed the code."},
{"role": "user", "content": "Now fix the bug in auth.js"}
]
}
As the conversation grows, you can mark the content block containing the codebase with cache_control too, so each new turn only pays full price for the newest message, not the accumulated history.
Monitoring whether caching is actually working
The API response includes usage fields that tell you how many tokens were cache writes versus cache reads. If you're not seeing cache-read tokens on repeat calls, check:
- Whether the cached block's content is byte-for-byte identical between calls (whitespace changes break the match)
- Whether enough time has passed that the cache expired
- Whether the block is large enough to be eligible for caching at all
If you're running this behind SubToAPI, the same request structure applies — you send cache_control blocks in your /v1/messages call exactly as shown above, authenticated with your sub_live_... key instead of a raw Anthropic key. The response usage metadata in the dashboard breaks down cache reads versus writes per request, which makes it easier to confirm caching is actually reducing cost across your team rather than guessing from latency alone. See the messages docs for the full request format and the quickstart if you're setting up your first key.
Practical checklist before shipping
- Isolate static context (docs, schemas, system instructions) into its own content block
- Add
cache_control: {"type": "ephemeral"}to that block only - Keep block order identical across requests
- Keep traffic frequent enough that caches don't expire between calls
- Check usage metadata to confirm cache hits, not just assumed savings
Questions
Does prompt caching change the model's output? No. Caching only affects how input is processed and billed — it doesn't change what the model generates. The same input produces the same quality of response whether cached or not.
How long does a cached prompt stay valid? Caches expire after a short period of inactivity, typically a few minutes. If your application has long gaps between requests to the same user or session, you'll see cache misses even on identical content.
Can I cache prompts if I'm not calling Anthropic's API directly? Yes — if you're using a proxy layer like SubToAPI, the same cache_control fields work in your requests to /v1/messages. Check the docs for the exact request shape and confirm cache usage in your dashboard's per-request metadata.