AI — Claude API Development
Claude API Prompt Caching for Cost Reduction
Direct answer
Claude API prompt caching lets you mark a stable prefix of your request — typically the system prompt, tool definitions, and long documents — with cache_control breakpoints so repeat requests are billed at a small fraction of the base input rate. It is a strict prefix match: any byte change before the breakpoint invalidates the cache. Structure requests as stable-content-first, volatile-content-last, then verify hits via the usage fields on every response.
Prompt caching is the single biggest cost lever on most Claude workloads I audit, and also the most commonly misconfigured. Here is how the mechanism actually works and the checklist I use to make caches hit.
Key facts, with sources
- Anthropic's published API pricing lists Claude Sonnet 4.6 at $3 per million input tokens and $15 per million output tokens, with Claude Haiku 4.5 at $1 and $5 for lighter workloads. (Claude Platform Docs)
- The Claude API offers a 50 percent discount on both input and output tokens via the Batch API and up to 90 percent savings on repeated input through prompt caching. (Claude Platform Docs)
- Current Claude Opus and Sonnet models support a 1 million token context window at flat per-token rates with no long-context surcharge. (CloudZero)
- Anthropic raised a $30 billion Series G at a $380 billion post-money valuation in 2026. (Anthropic)
- Anthropic said it hit a $30 billion revenue run rate after roughly 80x growth in about two years, driven primarily by enterprise and developer API consumption. (VentureBeat)
It is a prefix match — internalize that first
The cache key is derived from the exact bytes of the rendered request up to each breakpoint, in the order tools, then system, then messages. One changed byte anywhere in that prefix — a timestamp, a reordered JSON key, a renamed tool — invalidates everything after it. This is the mental model that explains every caching mystery I get called in to debug.
The design consequence: sort your request by stability. Frozen system prompt and deterministic tool list first, per-session context next, the per-request question last, after the final breakpoint. There is also a minimum cacheable size — short prefixes silently do not cache at all, with no error to tell you — so tiny prompts are not worth instrumenting. For chat-style products, placing a breakpoint at the end of the conversation so far lets each turn reuse the whole prior history.
Adding cache_control correctly
The marker is a cache_control field of type ephemeral on a content block. Placed on the last system block, it caches the tool definitions and system prompt together, since tools render before system. You get a small number of breakpoints per request — I typically use one after the static system prompt and, in document workloads, one after the document block, so different questions against the same document share the expensive prefix.
The most important rule is about what does not get a marker: anything that varies per request must come after the last breakpoint. Marking a block that contains a formatted date is the classic self-inflicted wound — the request looks cached, bills like it is not, and nobody notices until the invoice.
import anthropic
MODEL = "claude-..." # use the latest Claude model id
client = anthropic.Anthropic()
response = client.messages.create(
model=MODEL,
max_tokens=1024,
system=[
{
"type": "text",
"text": STABLE_SYSTEM_PROMPT, # frozen: no dates, IDs, or flags
"cache_control": {"type": "ephemeral"},
}
],
messages=[
# volatile content lives AFTER the breakpoint
{"role": "user", "content": user_question}
],
)The silent invalidators I hunt for in audits
When a client says caching is not working, the cause is almost always one of a short list. A current-date line interpolated into the system prompt — new prefix every request. JSON serialized without sorted keys, or iteration over a set, feeding the prompt — non-deterministic bytes. A per-user or per-session ID embedded early in the system text — no cross-user sharing. Conditional prompt sections toggled by feature flags — every flag combination is its own cold cache. A tool list built per user — tools render first, so nothing downstream ever hits.
The fix is always the same shape: make the prefix deterministic, move the dynamic piece after the last breakpoint, or delete it if it is not load-bearing. Most current dates in system prompts are not load-bearing.
Verify hits with the usage fields — never assume
Every response reports cache activity in usage: cache_creation_input_tokens is what you wrote to cache this request, cache_read_input_tokens is what was served from cache, and input_tokens is the uncached remainder billed at full price. Total prompt size is the sum of all three — a common misreading is panicking that input_tokens looks tiny, when that is exactly what success looks like.
If cache_read_input_tokens stays at zero across requests you believe share a prefix, diff the rendered request bytes between two calls; the invalidator will be staring at you. I export these three fields to the metrics dashboard on every deployment I ship, because cache hit rate regressions are otherwise invisible until the bill arrives.
usage = response.usage
print("written to cache:", usage.cache_creation_input_tokens)
print("served from cache:", usage.cache_read_input_tokens)
print("full-price input:", usage.input_tokens)
# Healthy steady state: large cache_read, small input.
# cache_read stuck at 0 across identical-prefix requests means
# something is mutating your prefix - diff the rendered prompts.The economics: when caching pays and when it does not
Cache reads are billed at roughly a tenth of the base input price, while writes carry a modest premium over a normal uncached request — which means a cached prefix pays for itself after very few reuses within the cache lifetime. For a support bot, an agent loop resending its history every turn, or a document Q&A product, the prefix is reused constantly and the savings are dramatic. Agent workloads are the sneaky big winner: each loop iteration resends everything, so caching turns quadratic-feeling costs into something sane.
Where it does not pay: prompts that differ from the first byte every request, prefixes below the cacheable minimum, and one-shot jobs with no reuse inside the cache window. Adding markers there just pays the write premium for nothing. Caching is an architecture decision, not a checkbox — decide what is stable, keep it stable, and measure.
When to hire senior help
Senior help matters most when you go beyond simple completions into agentic systems on the Claude API, where tool design, caching architecture, and eval harnesses decide reliability and cost. A few days of experienced review typically cuts token bills materially and prevents expensive rewrites later. If your stack includes React Native + Python + AI, a senior engineer who owns the full product beats coordinating multiple juniors.
Bottom line
Dhairya Senjaliya ships AI — Claude API Development projects worldwide — book a scoping call to discuss your specific situation.
Common pitfalls to avoid
- ✕Skipping prompt caching in agent loops that resend the same system prompt and tool definitions every turn, paying full input price for tokens that could cost 90 percent less
- ✕Running latency-insensitive workloads like evals, backfills, and bulk classification through the live API instead of the Batch API's 50 percent discount
- ✕Migrating model versions by swapping the ID string without checking for removed parameters like temperature or thinking budgets, which now return 400 errors on newer Claude models
- ✕Treating refusal and max-token stop reasons as generic errors instead of branching on stop_reason, which surfaces as silent empty responses in production
Frequently asked questions
How much does prompt caching reduce Claude API costs?
Cached input is billed at roughly a tenth of the base input rate, so workloads that resend a large stable prefix — support bots, agent loops, document Q&A — often see input costs drop by well over half. The exact saving depends on what share of each request is a stable, reused prefix versus per-request content.
Why is my Claude prompt cache never hitting?
Almost always a silent prefix invalidator: a timestamp or user ID interpolated into the system prompt, JSON serialized with unsorted keys, feature-flagged prompt sections, or a tool list that varies per request. Check usage.cache_read_input_tokens on responses; if it is zero, diff the rendered bytes of two supposedly identical requests to find the mutation.
What should I cache in a Claude API request?
Cache the content that is identical across many requests: the system prompt, tool definitions, and long reference documents. Place a cache_control breakpoint at the end of that stable prefix and keep everything volatile — the user's question, timestamps, session context — after it. Requests render tools first, then system, then messages, and caching matches on that exact byte order.
Is Claude cheaper or more expensive than GPT for production workloads?
List prices are comparable tier for tier, so real cost differences come from token efficiency, caching hit rates, and how many loop iterations each model needs to finish a task. The only reliable answer is to run both on your own eval set and compare cost per completed task, not per token.
When do we actually need the 1 million token context window?
Most applications work fine well under 200K tokens, and input cost scales with everything you send. The 1M window matters for whole-codebase analysis, large document sets, and long-running agent sessions, and pairing it with prompt caching keeps repeated long contexts affordable.
How do we keep Claude API costs under control?
The three biggest levers are prompt caching (up to 90 percent off repeated input), the Batch API (50 percent off asynchronous work), and routing simple tasks to Haiku-class models. Instrument the usage fields on every response so you can see cache hit rates and catch cost regressions early.
Bottom line: Dhairya Senjaliya ships AI — Claude API Development projects worldwide. Book a scoping call at https://dhairyasenjaliya.com/#book-call.