← Back to DevBytes

Prompt Caching for Cost Reduction with Claude Code: Complete Guide

Prompt Caching for Cost Reduction with Claude Code: Complete Guide

As developers build increasingly sophisticated applications on top of large language models, two challenges consistently emerge: rising API costs and unacceptable latency. Anthropic's prompt caching feature, available through Claude Code and the broader Claude API, directly addresses both problems by allowing you to reuse large portions of prompts across multiple requests without paying full price each time. In this guide, we'll explore what prompt caching is, why it matters, how to implement it effectively, and the best practices that will keep your bills low and your responses fast.

What Is Prompt Caching?

Prompt caching is a feature that lets Claude store a prefix of your prompt on Anthropic's servers for a limited time. When you send a subsequent request that begins with the same prefix, Claude reuses the cached content instead of reprocessing it from scratch. This reduces both the input token cost and the time-to-first-token for that portion of the prompt.

The cache works on a prefix basis. This means the beginning of your prompt — up to the point where you mark a cache breakpoint — is stored. Any content after the breakpoint is processed normally on each request. You can define up to four cache breakpoints in a single request, allowing you to cache different sections of a complex prompt independently.

Cached content persists for five minutes by default, and each time you use the cache, that TTL refreshes. This makes it ideal for conversational applications, document Q&A systems, and agentic workflows where the same context is referenced repeatedly.

Why Prompt Caching Matters

The economics of prompt caching are compelling. When you read from the cache, you pay only 10% of the standard input token price. Writing to the cache costs 25% more than standard input tokens, but that surcharge is paid only once per cache write. The math favors caching whenever you expect to reuse the same prefix multiple times within the TTL window.

How Prompt Caching Works with Claude Code

Claude Code is Anthropic's command-line agentic coding tool. It leverages the Claude API under the hood, and prompt caching is applied automatically to reduce costs during extended coding sessions. However, if you're building your own integrations on top of the Claude API — or extending Claude Code's behavior — understanding how to control caching directly is essential.

The core mechanism is the cache_control parameter. You add it to any content block in your messages or system prompt to mark where the cache should end. Everything from the start of the prompt up to that breakpoint becomes the cached prefix.

Setting Up Your Environment

Before writing caching code, make sure you have the Anthropic Python SDK installed and your API key configured.

pip install anthropic

export ANTHROPIC_API_KEY="your-api-key-here"

Then initialize the client in your script:

import anthropic

client = anthropic.Anthropic()

Basic Prompt Caching Example

Let's start with a simple example. Suppose you have a large system prompt that defines your assistant's behavior, and you want to cache it across multiple user queries.

import anthropic

client = anthropic.Anthropic()

SYSTEM_PROMPT = """
You are an expert software engineer assistant. You help developers
write clean, maintainable, well-tested code. You always explain your
reasoning before providing code. You follow these principles:
1. Prefer composition over inheritance.
2. Write functions that do one thing well.
3. Include type hints in all Python code.
4. Add docstrings to every public function.
5. Suggest tests for any code you write.
... (imagine several thousand more tokens of detailed instructions)
"""

def ask_claude(question: str) -> str:
    response = client.messages.create(
        model="claude-sonnet-4-5-20250929",
        max_tokens=1024,
        system=[
            {
                "type": "text",
                "text": SYSTEM_PROMPT,
                "cache_control": {"type": "ephemeral"}
            }
        ],
        messages=[
            {"role": "user", "content": question}
        ]
    )
    return response.content[0].text

# First call: cache is written (you pay a 25% write surcharge)
print(ask_claude("How do I structure a Python package?"))

# Second call within 5 minutes: cache is read (you pay only 10% of input cost)
print(ask_claude("What testing framework should I use?"))

Notice the cache_control key with {"type": "ephemeral"}. This tells Claude to cache everything up to that point. The first call writes the cache; subsequent calls within the TTL read from it.

Caching Large Documents

One of the most powerful use cases is caching large reference documents. Imagine building a documentation Q&A bot where users ask many questions about the same codebase or manual.

def ask_about_document(document_text: str, question: str) -> str:
    response = client.messages.create(
        model="claude-sonnet-4-5-20250929",
        max_tokens=2048,
        system=[
            {
                "type": "text",
                "text": "You are a helpful documentation assistant. "
                        "Answer questions based only on the provided document."
            },
            {
                "type": "text",
                "text": f"<document>\n{document_text}\n</document>",
                "cache_control": {"type": "ephemeral"}
            }
        ],
        messages=[
            {"role": "user", "content": question}
        ]
    )
    return response.content[0].text

# Load a large document once
with open("api_reference.md", "r") as f:
    doc = f.read()

# Many questions reuse the cached document
questions = [
    "How do I authenticate requests?",
    "What is the rate limit for the free tier?",
    "How do I paginate results?",
    "What error codes can the API return?",
]

for q in questions:
    answer = ask_about_document(doc, q)
    print(f"Q: {q}\nA: {answer}\n")

In this pattern, the document is cached on the first call. Each subsequent question pays only 10% of the input cost for that large document block, while the small system instruction and user question are processed at standard rates.

Multi-Turn Conversations with Caching

For chat applications, you want to cache the growing conversation history so that each new turn doesn't reprocess everything from scratch. Place the cache breakpoint at the end of the conversation history, right before the new user message.

conversation_history = [
    {"role": "user", "content": "What is dependency injection?"},
    {"role": "assistant", "content": "Dependency injection is a design pattern..."},
    {"role": "user", "content": "Can you show me a Python example?"},
    {"role": "assistant", "content": "Certainly! Here's a simple example..."},
]

new_question = "How does this compare to the service locator pattern?"

# Build messages with cache breakpoint at the end of history
messages = []
for msg in conversation_history:
    messages.append(msg)

# Add the new user message with a cache_control on the last history item
messages[-1]["content"] = [
    {
        "type": "text",
        "text": messages[-1]["content"],
        "cache_control": {"type": "ephemeral"}
    }
]

messages.append({"role": "user", "content": new_question})

response = client.messages.create(
    model="claude-sonnet-4-5-20250929",
    max_tokens=1024,
    system="You are a helpful programming tutor.",
    messages=messages
)

print(response.content[0].text)

Each turn, the entire prior conversation is cached. As the conversation grows, the savings compound because you only pay full price for the newest message.

Using Multiple Cache Breakpoints

The API supports up to four cache breakpoints per request. This is useful when your prompt has distinct sections that change at different rates — for example, a static system prompt, a semi-static document, and a growing conversation history.

response = client.messages.create(
    model="claude-sonnet-4-5-20250929",
    max_tokens=1024,
    system=[
        {
            "type": "text",
            "text": STATIC_SYSTEM_PROMPT,
            "cache_control": {"type": "ephemeral"}
        },
        {
            "type": "text",
            "text": LARGE_REFERENCE_DOC,
            "cache_control": {"type": "ephemeral"}
        }
    ],
    messages=[
        {"role": "user", "content": "Summarize the key points."},
        {"role": "assistant", "content": "Here are the key points..."},
        {
            "role": "user",
            "content": [
                {
                    "type": "text",
                    "text": "Now explain section 3 in detail.",
                    "cache_control": {"type": "ephemeral"}
                }
            ]
        }
    ]
)

Here we use three breakpoints: one for the system prompt, one for the reference document, and one for the conversation prefix. If the document changes but the system prompt stays the same, only the document cache is invalidated — the system prompt cache remains valid.

Monitoring Cache Usage

The API response includes token usage details that tell you exactly how caching performed. Always inspect these fields to verify your caching strategy is working.

response = client.messages.create(
    model="claude-sonnet-4-5-20250929",
    max_tokens=1024,
    system=[
        {
            "type": "text",
            "text": SYSTEM_PROMPT,
            "cache_control": {"type": "ephemeral"}
        }
    ],
    messages=[{"role": "user", "content": "Hello!"}]
)

usage = response.usage
print(f"Input tokens: {usage.input_tokens}")
print(f"Output tokens: {usage.output_tokens}")
print(f"Cache creation tokens: {usage.cache_creation_input_tokens}")
print(f"Cache read tokens: {usage.cache_read_input_tokens}")

On the first call, you'll see cache_creation_input_tokens populated with the cached prefix size. On subsequent calls within the TTL, you'll see cache_read_input_tokens populated instead. If both are zero, your cache isn't being used — check that your prefix is truly identical between calls.

Best Practices for Prompt Caching

To get the most out of prompt caching, follow these guidelines:

Common Pitfalls to Avoid

Even experienced developers run into caching issues. Here are the most frequent mistakes:

Conclusion

Prompt caching is one of the highest-leverage optimizations available to developers building on Claude. By structuring your prompts to separate stable, reusable content from dynamic per-request content, you can reduce input token costs by up to 90% and dramatically improve response latency. Whether you're building a document Q&A system, a multi-turn chatbot, or an agentic coding workflow with Claude Code, the principles are the same: cache the largest stable prefix you can, monitor your cache hit metrics, and let the TTL work in your favor. Start by instrumenting your existing API calls to measure current costs, then introduce cache breakpoints incrementally and observe the savings. With thoughtful implementation, prompt caching transforms expensive, slow LLM applications into efficient, responsive ones — without sacrificing capability or context depth.

— Ad —

Google AdSense will appear here after approval

← Back to all articles