Prompt Caching Basics
8 examples to get you started with prompt caching - 5 basic and 3 intermediate.
Search across all documentation pages
8 examples to get you started with prompt caching - 5 basic and 3 intermediate.
pip install anthropicexport ANTHROPIC_API_KEY=sk-ant-...anthropic.Anthropic(), which reads that environment variable automatically.Mark the end of a long system prompt as cacheable.
import anthropic
client = anthropic.Anthropic()
long_policy_text = "You are Acme Corp's support assistant. " * 300
response = client.messages.create(
model="claude-sonnet-5",
max_tokens=200,
system=[
{
"type": "text",
"text": long_policy_text,
"cache_control": {"type": "ephemeral"},
}
],
messages=[{"role": "user", "content": "What's your refund window?"}],
)
print(response.content[0].text)cache_control is set on the system content block, not on the request as a whole.{"type": "ephemeral"} uses the default 5-minute TTL.Related: How Prompt Caching's Prefix Match Actually Works - why this has to be byte-identical to hit.
Confirm the first request wrote a cache entry.
response = client.messages.create(
model="claude-sonnet-5",
max_tokens=200,
system=[
{
"type": "text",
"text": long_policy_text,
"cache_control": {"type": "ephemeral"},
}
],
messages=[{"role": "user", "content": "What's your refund window?"}],
)
print("cache write tokens:", response.usage.cache_creation_input_tokens)
print("cache read tokens:", response.usage.cache_read_input_tokens)cache_creation_input_tokens should be nonzero here - that's the cache write.cache_read_input_tokens should be 0 on this first call, since nothing existed to read yet.response.usage, alongside the normal input_tokens and output_tokens.Send an identical prefix again within the TTL window.
response_2 = client.messages.create(
model="claude-sonnet-5",
max_tokens=200,
system=[
{
"type": "text",
"text": long_policy_text,
"cache_control": {"type": "ephemeral"},
}
],
messages=[{"role": "user", "content": "Do you ship internationally?"}],
)
print("cache read tokens:", response_2.usage.cache_read_input_tokens)system block is byte-identical to example 1, so the prefix matches.cache_read_input_tokens should now be nonzero - this is the cache hit.Choose the 1-hour cache window instead of the 5-minute default.
response = client.messages.create(
model="claude-sonnet-5",
max_tokens=200,
system=[
{
"type": "text",
"text": long_policy_text,
"cache_control": {"type": "ephemeral", "ttl": "1h"},
}
],
messages=[{"role": "user", "content": "What's your refund window?"}],
)"ttl": "1h" extends how long the entry survives before it must be rewritten.ttl is the same as "5m".Related: Cache TTL Options and Pricing Reference - full comparison of the two windows.
Put the breakpoint on a large tool schema.
tools = [
{
"name": "search_docs",
"description": "Search the internal knowledge base." * 20,
"input_schema": {
"type": "object",
"properties": {"query": {"type": "string"}},
"required": ["query"],
},
"cache_control": {"type": "ephemeral"},
}
]
response = client.messages.create(
model="claude-sonnet-5",
max_tokens=200,
tools=tools,
messages=[{"role": "user", "content": "Find our vacation policy."}],
)cache_control can go on the last tool in a tools list, not only on system.Cache tools and the system prompt as two separate, independently-refreshable segments.
tools = [
{
"name": "search_docs",
"description": "Search the internal knowledge base." * 20,
"input_schema": {
"type": "object",
"properties": {"query": {"type": "string"}},
"required": ["query"],
},
"cache_control": {"type": "ephemeral"},
}
]
response = client.messages.create(
model="claude-sonnet-5",
max_tokens=200,
tools=tools,
system=[
{
"type": "text",
"text": long_policy_text,
"cache_control": {"type": "ephemeral"},
}
],
messages=[{"role": "user", "content": "What's your refund window?"}],
)
print(response.usage.cache_read_input_tokens, response.usage.cache_creation_input_tokens)Related: Placing cache_control Breakpoints on System Prompts and Tools - the full ordering rules.
Wrap a call so every request reports its cache hit rate.
def call_with_cache_report(client, **kwargs):
response = client.messages.create(**kwargs)
usage = response.usage
total_prefix = usage.cache_read_input_tokens + usage.cache_creation_input_tokens
hit_rate = (
usage.cache_read_input_tokens / total_prefix if total_prefix else 0.0
)
print(
f"read={usage.cache_read_input_tokens} "
f"write={usage.cache_creation_input_tokens} "
f"hit_rate={hit_rate:.0%}"
)
return response
call_with_cache_report(
client,
model="claude-sonnet-5",
max_tokens=200,
system=[
{
"type": "text",
"text": long_policy_text,
"cache_control": {"type": "ephemeral"},
}
],
messages=[{"role": "user", "content": "What's your refund window?"}],
)hit_rate that drops to 0% unexpectedly is the first signal something upstream changed the prefix.messages.create call in a larger application.Related: Verifying Cache Hits with cache_read_input_tokens - a deeper look at building this kind of check.
Keep the system prompt cached while the messages array grows turn by turn.
conversation = [{"role": "user", "content": "What's your refund window?"}]
def cached_turn(client, conversation, user_text):
if user_text:
conversation.append({"role": "user", "content": user_text})
response = client.messages.create(
model="claude-sonnet-5",
max_tokens=300,
system=[
{
"type": "text",
"text": long_policy_text,
"cache_control": {"type": "ephemeral"},
}
],
messages=conversation,
)
conversation.append({"role": "assistant", "content": response.content[0].text})
return response
r1 = cached_turn(client, conversation, None)
r2 = cached_turn(client, conversation, "And for opened items?")
print(r2.usage.cache_read_input_tokens)conversation grows.Related: Caching Strategy for Multi-Turn Agent Conversations - per-turn breakpoint placement for growing conversations.
Stack versions: Written against the Claude model lineup current as of ~June 2026 - Claude Fable 5, Claude Opus 4.8, Claude Sonnet 5 (the default), and Claude Haiku 4.5 - and the official
anthropicPython SDK (latest 0.x release). Model names, SDK versions, and pricing move quickly - verify current specifics at platform.claude.com/docs before relying on them.
Reviewed by Chris St. John·Last updated Jul 19, 2026