Prompt Caching in the Claude API: A Practical Guide

By Carlos Montiel | Enterprise AI Specialist
Leer en español →
Published: 2026-07-28 | By: Carlos Montiel | Reading time: ~5 minutes

Prompt caching can cut input token cost by up to 90% and latency proportionally, but only if you understand it's an exact prefix match — a single byte out of place invalidates everything that follows.

The invariant that explains everything

The rule you need to internalize before touching code: prompt caching is a prefix match. The cache key is derived from the exact bytes of the rendered prompt up to each cut point (`cache_control`). A single-byte change at position N invalidates the cache for every breakpoint after that position.

The rendering order on every request is fixed: `tools` → `system` → `messages`. A breakpoint on the last `system` block caches both the tools and the system prompt together. Understanding this order is half the work of designing caching well — the other half is making sure volatile content (timestamps, session IDs, the user's specific question) always comes after the last breakpoint.

Basic syntax and verification

The simplest usage is automatic request-level caching, which caches the last cacheable block without you having to annotate individual blocks:

response = client.messages.create( model="claude-opus-4-8", max_tokens=1024, cache_control={"type": "ephemeral"}, system="You are an expert in the company's internal regulations...", messages=[{"role": "user", "content": "Summarize the key points"}], ) print(response.usage.cache_read_input_tokens) # tokens served from cache (~0.1x cost) print(response.usage.cache_creation_input_tokens) # tokens written to cache (~1.25x cost)

If `cache_read_input_tokens` is zero on repeated requests with the same prefix, there's a silent invalidator at work — the most common cause is a timestamp or a UUID interpolated into the system prompt, or a `JSON.stringify` with non-deterministic key ordering.

Where to place the breakpoints

For a large system prompt shared across many requests, the breakpoint goes on the last text block of `system`. For multi-turn conversations, it goes on the last content block of the most recent turn — every subsequent request reuses the entire prefix of the prior conversation. For the "shared prefix, variable suffix" pattern (same few-shot context or retrieved documents, different question each time), the breakpoint must go at the end of the **shared** part, never at the end of the full prompt — otherwise every request writes a different cache entry and none of them ever get read.

"system": [ {"type": "text", "text": "large system prompt", "cache_control": {"type": "ephemeral"}} ]

The minimum number of cacheable tokens depends on the model: 4096 tokens for the Opus 4.x and Haiku 4.5 family, 2048 for Sonnet 4.6 and earlier Haiku models. A 3000-token prompt gets cached on Sonnet but not on Opus — no error, just `cache_creation_input_tokens: 0`.

Architecture decisions that silently break the cache

The most common mistake isn't syntax but design: interpolating "current date: X" or the user's name directly into the system prompt. Since that content sits at the start of the prefix, it invalidates everything that follows on every single request. The fix is to inject that dynamic context later in `messages`, not at the start of `system`.

Changing the tool set or the model mid-conversation also invalidates the whole cache — tools get rendered at position zero of the prompt. If you need distinct behavior "modes," don't change the tool set; give Claude a tool that records the mode transition, or pass it as message content instead.

Economics: when it's worth it

A cache read costs roughly 0.1x the base input price; a write costs 1.25x with a 5-minute TTL, or 2x with a 1-hour TTL. The break-even point with a 5-minute TTL is reached at two requests; with a 1-hour TTL, you need at least three. The one-hour TTL makes sense when traffic is intermittent but recurring (for example, a workflow that runs every 20-30 minutes); the 5-minute one is enough for most conversational applications with continuous traffic.

Pre-warming the cache in latency-sensitive applications

To eliminate the latency of the first cache miss in applications where first-response time is visible to the user (live chat, voice assistants), you can send a request with `max_tokens: 0` when the application or worker starts up. This runs the prompt prefill, writes the cache at the indicated breakpoint, and returns immediately without generating output — no output token cost, just the normal cache-write cost.

This is only worth it when real traffic has gaps longer than the TTL; if requests arrive more often than every 5 minutes, the cache already stays warm on its own and additional pre-warming is an unnecessary extra write.

Carlos Montiel
Enterprise AI Solutions Architect
Specialist in LLMs, Agents, and Orchestration
guatemalia.com/en/#contact · info@guatemalia.com

Need to implement AI at your company?

Carlos Montiel is an enterprise AI solutions architect. He implements LLMs, Agents, RAG, and orchestrators for companies across Guatemala and Latin America. Reach out for a consultation.

Contact Carlos Montiel

info@guatemalia.com