Production & Deployment · AI Engineer Help
Prompt caching: what breaks it, what it saves, and what we measured
Most production prompts repeat thousands of tokens on every call: instructions, tool definitions, policies, examples. Prompt caching lets the provider skip re-reading that repeated part, at up to a tenth of the price. But it only works if the repeated part comes first and stays byte-for-byte identical. A single timestamp in the wrong place switches it off without any error.
Key takeaways
- A prompt cache is a prefix match. The provider reuses work only for the leading part of your prompt that is identical to an earlier request. The first changed character ends the reusable part, and everything after it is processed at full price.
- Where the changing parts sit decides what can be cached. In our test prompt, a timestamp on the first line of the system instruction cut the reusable prefix from 5,382 tokens (99.7%) to 783 (14.4%). The same timestamp at the end of the user message left 5,403 tokens (99.5%) reusable.
- Small, invisible differences break it too. Sending the same seven tools in a different order left 11 reusable tokens. Putting the question before the handbook left 1,086. Both are below Gemini's 4,096-token minimum, so nothing at all could be cached.
- Gemini's implicit caching gave us nothing. In 97 eligible requests with byte-identical 5,400 to 13,000-token prefixes, sent seconds apart, we got zero implicit cache hits. Google documents implicit caching as having "no cost saving guarantee".
- Explicit caching delivered every time. With a cache created through Gemini's
cachedContentsAPI, 12 of 12 requests read 5,384 of about 5,400 prompt tokens from cache. That cut the cost per request, including output, by 84%, from $0.00439 to $0.00072. - We saw no latency gain at this size. Median time to first token was 2.3 s with the explicit cache and 2.1 s without one. Providers say the speed-up shows on long prompts; at 5,400 tokens, network and generation time dominate.
What prompt caching is
A model handles a request in two phases. First it reads the whole prompt (the prefill) and builds an internal representation of every token, called the key-value (KV) cache. Then it generates the answer one token at a time (the decode). Prefill is the part you pay input prices for, and for long prompts it is a large share of the time before the first token appears.
If two requests start with the same tokens, the representation of those tokens is identical too, because each token's representation depends only on the tokens before it. Prompt caching keeps that work for a few minutes and reuses it when another request starts the same way. The model still sees the full prompt, so answers don't change. Only the price and the prefill time do.
It's easy to confuse with two other kinds of caching:
| Kind | What is reused | When it applies | Does the model run? |
|---|---|---|---|
| Prompt caching | The provider's processed prompt prefix | A new request starts with exactly the same tokens | Yes, on the full prompt, so answers are as fresh as without a cache |
| Response caching | A stored answer, on your side | The whole request is identical | No. The stored answer is returned |
| Semantic caching | A stored answer, on your side | A new question means roughly the same as an old one | No. A similar but different question can get a wrong stored answer |
Table 1. Prompt caching is the only one of the three that never changes what the model says. That makes it safe to turn on everywhere.
The one rule: prefix match
Everything in this article follows from one rule: the cache matches from the first token forward, and stops at the first difference. It doesn't look for the longest matching chunk somewhere in the middle. If request 2 differs from request 1 at token 50, only the first 49 tokens can be reused, even if the next 5,000 are identical.
Figure 1. Two requests, one timestamp
Each box is a chunk of the prompt, in the order the model reads it.
Two consequences matter in practice:
- Order your prompt from most stable to least stable. Content that never changes goes first, then content that is fixed for one user or conversation, then content that changes on every request.
- Know the order the provider uses. Your API call has separate fields, but the provider joins them into one sequence. Claude's documented order is tools, then system, then messages, so a change in the tool list invalidates everything. OpenAI says the cached prefix includes "tool definitions" and Structured Outputs schemas. Gemini doesn't document its order. Treat tools and system instructions as the front of the prompt.
Figure 2. The same content in two orders
Retrieval apps often put the question first. Moving it last turns the documents into a reusable prefix.
Question first
Question last
How each provider does it
All three providers cache prefixes, but they differ in whether you have to ask, what a write costs, and how long an entry lives. Prices below are list prices per million input tokens, checked on 7 October 2026.
| Anthropic Claude | Google Gemini | OpenAI | |
|---|---|---|---|
| How to turn it on | Mark up to 4 breakpoints with cache_control, or set it once at the top level for automatic placement | Implicit: on by default, best effort. Explicit: create a cachedContents resource and reference it | Automatic on supported models. GPT-5.6 and later also accept explicit breakpoints |
| Cache read price | 0.1× input (Sonnet 5.5: $0.20 vs $2). Opus 5.5: 0.05× | 0.1× input (3.8 Flash: $0.075 vs $0.75) | 0.1× input on most models, 0.05× on GPT-6.1 Sol |
| Cache write price | 1.25× input (5-minute entry) or 2× (1-hour entry) | Implicit: none. Explicit: storage at $0.50 per million tokens per hour (3.8 Flash) | GPT-5.6 and later: 1.25× input. Earlier models: none |
| Minimum prompt | 512 tokens (Opus 5.5, Sonnet 5.5), 4,096 (Haiku 4.5) | 4,096 tokens (3.x Flash and Pro), 2,048 (2.5) | 1,024 tokens (GPT-5.6 and later) |
| Lifetime | 5 minutes, reset on every hit; 1 hour optional | Implicit: not documented. Explicit: you set it (default 1 hour) | 30 minutes (GPT-5.6 and later) |
| How to check hits | usage.cache_read_input_tokens, cache_creation_input_tokens | usageMetadata.cachedContentTokenCount, or usage.total_cached_tokens in the Interactions API | usage.input_tokens_details.cached_tokens and cache_write_tokens |
Table 2. Sources: Claude prompt caching and pricing; Gemini caching, explicit caching and pricing (3.8 Flash prices are promotional through 31 December 2026); OpenAI prompt caching and pricing. Prices and minimums change, so check them before you rely on them.
Where a write costs extra, caching pays off only if the entry is read again before it expires. On Claude, the docs work out that a 5-minute entry breaks even after one read, since 1.25× + 0.1× is less than 2×, and a 1-hour entry after two reads. For Gemini's explicit cache, here is our own calculation from the prices above. Each cached read of our 5,384-token prefix saves $0.0036, and storing it costs $0.0027 an hour. So it pays off from about one request per hour.
Eight ways to break the cache
None of these produce an error. The request succeeds, the answer is fine, and the bill is higher. Each one has a matching example in the playground.
01The current time at the top
The most common case. A template starts with Current time: {now} so the model knows the date. If the time includes seconds, no two requests share more than the text before it.
Breaks the cache
Current time: 2026-10-07T14:32:09Z
You are the support assistant…
[4,000-token handbook]Cacheable
You are the support assistant…
[4,000-token handbook]
…
User: Today's date: 7 October 2026
Where is my order?Measured: the reusable prefix fell from 5,382 to 783 tokens. Also ask whether the model needs seconds at all. A date changes once a day, so it splits the cache once a day instead of on every request. Try it →
02User details in the system prompt
You are helping {name} ({email}), a Gold member. Every user gets a different prefix, so the instructions after it are cached once per user instead of once for everyone. Within one user's conversation the cache still works, which hides the problem in testing.
Measured: the reusable prefix across users fell from 5,389 tokens (details at the end) to 774 (details at the top). Try it →
03Request IDs, counters and budgets
Agent loops often write state into the system prompt: request_id, Turn 4 of 20, Tokens remaining: 18,240. Each one is unique per call. Send IDs as request metadata, and put state the model needs in the latest message. Try it →
04Tools in a different order
Building the tool list from a dictionary, a set or a plugin registry can change its order between processes or deployments. The tools are the same, but the bytes aren't, and tools sit at the very front of the prompt.
Measured: the same seven tools, shuffled per request, left a reusable prefix of 11 tokens. Sort tools by name, and serialize JSON with sorted keys.
05Different tools for different users
Hiding tools a user isn't allowed to use is tempting, but it gives each permission level its own prefix, starting from the first token. Keep one tool set for everyone and check permissions when a tool is called. That is also the safer design. Try it →
06The question before the documents
Retrieval prompts often read "Question: … Context: …". If the documents are the same across requests, as with a policy handbook or a product manual, they should come first. Documents retrieved per query can't be shared across users anyway, but they can still be cached within a conversation if earlier turns don't change.
Measured: 1,086 reusable tokens, below Gemini's 4,096 minimum, so none were cacheable. Try it →
07Changing model or settings mid-conversation
Caches belong to one model, so routing a follow-up to a cheaper model starts from zero. On Claude, changing tool_choice, images or thinking settings also invalidates part of the cache (see the "invalidation" section of Anthropic's docs). Pick the model and settings per route, not per request.
08Parallel requests and long gaps
A cache entry can only be read after the request that writes it has started responding. If you send 20 requests with the same prefix at once, all 20 pay full price. Send one first, then the rest. At the other end, traffic slower than the cache lifetime never hits. On Claude, an entry lives 5 minutes after its last use, so a chatbot that gets one message every 10 minutes caches nothing unless it uses the 1-hour option.
The experiment
Docs describe the rules. We wanted to see them in real requests: how much of a realistic prompt each layout leaves reusable, whether the provider actually caches it, and what that does to cost and latency. We had Gemini API access, so we ran on Gemini 3.8 Flash (gemini-3.8-flash), on 7 October 2026.
The prompt
A customer-support assistant for Northwind Outfitters, a fictional outdoor-gear store. Every layout sends the same parts:
| Part | What it contains | Characters |
|---|---|---|
| Instructions | Role, tone, how to cite policy sections, what never to do | 1,211 |
| Policy handbook | 12 sections: orders, shipping, returns, refunds, warranty, trade-in, loyalty, care and sizing, privacy, escalation, a 27-product reference table and common questions | 15,940 |
| Tools | Seven function declarations: cancel_order, create_return, escalate_to_human, lookup_order, open_warranty_claim, search_products, track_parcel | 3,054 |
| Examples | Four example exchanges showing tone and length | 1,499 |
| Question | One of 12 customer questions, such as "Can I return a tent I set up once in my garden?" | 32–73 |
Table 3. Gemini counted about 5,400 prompt tokens per request, comfortably above the 4,096-token minimum. The full text is in src/fixture.js.
Eight layouts
| Layout | What changes between requests, and where |
|---|
Table 4. Each layout starts its system instruction with a tag that is fixed within the layout, such as deployment r202610079c02-stable. It stops layouts from reading each other's cache, the way two separate applications wouldn't share one.
Procedure and measures
- 12 requests per layout, one at a time, 1.5 seconds apart, layout after layout: 96 requests in about 7 minutes. Streaming, temperature 0, thinking level
LOW, up to 300 output tokens. - Cached tokens:
cachedContentTokenCount, as reported by Gemini for each request. - Reusable prefix: the number of leading characters each request shares with the one before it, rendered as tools, then system instruction, then user message, and converted to tokens using Gemini's count. This depends only on the prompt, not on the provider. It's the most any prefix cache could reuse.
- Time to first token: from sending the request to the first streamed text or tool call.
- Cost: from Gemini's token counts and list prices: $0.75 per million uncached input tokens, $0.075 cached, $3.75 output (thinking counts as output), and $0.50 per million tokens per hour of explicit cache storage.
We also sent a 2-request smoke test before the run. When implicit caching produced no hits, we sent 12 follow-up requests. They varied one thing at a time: no streaming; no tools and no thinking setting; a prompt three times larger (13,100 tokens); and Google's newer Interactions API.
Results
Figure 3. How much of each prompt could be reused
Median reusable prefix: the share of the prompt identical to the previous request, from the first character.
results/summary.json.The layout result is exactly what the prefix rule predicts. Moving the changing value to the end kept 99.4–99.7% of the prompt reusable. Putting it at the top cut that to 14%. The shuffled tools cut it to almost nothing, because the tools are the first thing in the prompt.
Figure 4. Cached tokens on each request
Stable prefix (implicit caching) vs explicit cache, 12 requests each, in order.
Implicit caching: 0 hits in 97 eligible requests. These were 84 requests in the main run, 2 in the smoke test and 11 successful follow-ups (one Interactions API request failed with an unrelated error). They used streaming and non-streaming, with and without tools, prompts from 4,500 to 13,100 tokens, and both the generateContent and Interactions APIs. Prefixes were byte-identical, and requests were seconds apart. Explicit caching: 12 hits in 12 requests, each reading 5,384 tokens (99.7% of the prompt) from cache.
Figure 5. Cost per request
Measured cost, input plus output, at Gemini 3.8 Flash list prices. Explicit includes storage.
Figure 6. Time to first token
Each dot is one request. The line marks the median. Two slow outliers (23.0 s and 14.8 s) are drawn at the edge.
| Layout | Prompt tokens | Reusable prefix | Cache hits | Cached share | First token (median) | Cost / request |
|---|
Table 5. All 96 requests, by layout (medians, 12 requests each). Every request and every answer is in results/requests.jsonl. 16 of the 96 responses were tool calls, all to lookup_order, for questions that named an order.
What it would have saved
The reusable prefix tells you what a working cache could save. If every request's reusable prefix had been read from cache at the cached price, here is the input cost of 1,000 requests. This is a projection from the measured prefixes, not a measurement:
| Layout | Without cache | Reusable prefix cached | Saving |
|---|
Table 6. Layouts with a reusable prefix below 4,096 tokens save nothing. That isn't because 783 tokens wouldn't help, but because Gemini won't cache a prefix that short.
Why it happens
Why implicit caching missed. We can only guess, because Google doesn't publish how implicit caching decides. Caches like this usually live on individual serving machines. A hit needs the next request to reach a machine that still holds the prefix. OpenAI's docs say this for their own cache: routing uses a hash of the first tokens, and "traffic above 15 requests per minute can lead to overflow routing" to other machines. Our traffic was light: about 14 requests a minute in total, and each layout's prefix was used for under two minutes. With so little traffic, a provider may not keep a prefix warm at all, or the next request may land on a different machine. It could also depend on load, region or account. Whatever the reason, implicit caching is best effort, and you can't plan costs around it until you've measured it on your own traffic.
Why the cache didn't make responses faster. Reading 5,400 tokens is quick on current hardware. Time to first token also includes network time, queueing, a short thinking step and, for tool calls, generating the call. So the prefill saving was smaller than the normal variation between requests (1.5 to 4.7 s, plus two outliers). Anthropic's docs say you will "generally see improved time-to-first-token for long documents", so expect latency gains at tens of thousands of tokens, not at five thousand.
Practical advice
- Design the prompt as layers: tools and instructions, then shared documents and examples, then per-user context, then conversation history, then the new message. Put anything with a clock, an ID or a counter in the last layer.
- Make serialization deterministic. Sort tools by name and JSON keys alphabetically. Never build prompt text by iterating a set. Strip trailing whitespace consistently.
- Pass dynamic context late. On Claude models that support it, a mid-conversation system message is designed for this: it adds instructions after the cached history instead of editing the top. Elsewhere, put it in the user message.
- Choose explicit caching when the saving matters. On Gemini, create a cache for any large prefix you reuse more than about once an hour. On Claude, add
cache_controlat the end of the stable part. - Measure hits on every request. Log cached tokens next to total input tokens and alert when the ratio drops. Caching regressions don't fail; a deploy that adds a timestamp only shows up on the bill.
- Warm up before fan-out. Send one request, wait for its first token, then send the parallel requests.
- Check the minimum. A 3,000-token prompt caches on Claude Sonnet 5.5 (512 minimum) and never on Gemini 3.8 Flash (4,096) or Claude Haiku 4.5 (4,096).
What it looks like in code
Claude, with an explicit breakpoint at the end of the stable system prompt (Python SDK):
response = client.messages.create(
model="claude-sonnet-5-5",
max_tokens=1024,
tools=sorted(TOOLS, key=lambda t: t["name"]), # same order every time
system=[{
"type": "text",
"text": INSTRUCTIONS + HANDBOOK, # no dates, IDs or names in here
"cache_control": {"type": "ephemeral"}, # cache everything up to this point
}],
messages=[{"role": "user", "content": f"Today's date: {today}\n\n{question}"}],
)
print(response.usage.cache_read_input_tokens, response.usage.cache_creation_input_tokens)
Gemini, with an explicit cache (REST, as used in our experiment):
# 1. Store the stable part once (default lifetime: 1 hour)
POST /v1beta/cachedContents
{ "model": "models/gemini-3.8-flash",
"systemInstruction": { "parts": [{ "text": "<instructions + handbook + examples>" }] },
"tools": [{ "functionDeclarations": [ ... ] }],
"ttl": "600s" }
# 2. Each request sends only the new part and the cache name
POST /v1beta/models/gemini-3.8-flash:generateContent
{ "cachedContent": "cachedContents/abc123",
"contents": [{ "role": "user", "parts": [{ "text": "Can I return a tent?" }] }] }
# → usageMetadata.cachedContentTokenCount: 5384
OpenAI caches automatically. Check usage.input_tokens_details.cached_tokens, and on GPT-5.6 and later also cache_write_tokens. The ordering rules are the same.
Limitations
- One provider, one day, one model. We measured Gemini 3.8 Flash on 7 October 2026. We didn't measure Claude or OpenAI; their sections come from documentation. Implicit caching may behave differently at other times, in other regions, or at higher traffic.
- Low volume. Requests went one at a time, about 4 seconds apart. Production traffic of many requests per second could get implicit hits that we didn't.
- Small samples. 12 requests per layout is enough to show 0 vs 12 hits, but not enough to estimate latency differences of a few hundred milliseconds.
- Reusable prefix is computed on our rendering order (tools, system, user). Gemini doesn't document its internal order. The before-and-after contrast holds either way, because the changed value was at the start or the end of a block.
- The playground estimates tokens at about four characters per token, and its cost model assumes steady traffic. Use it to compare layouts, not to forecast a bill.
- Prices change. Gemini 3.8 Flash prices are promotional and double on 1 January 2027.
Reproduce it
Everything runs with Node.js 20 or later and no dependencies. From production-deployment/prompt-caching/:
npm test # 17 tests for the analyzer and cost model
npm start # this article and the playground at http://127.0.0.1:4321
npm run experiment # dry run: prints the plan and the worst-case cost
cp .env.example .env # add GEMINI_API_KEY
npm run experiment -- --live # 96 paid requests, about $0.40
npm run analyze # writes results/summary.json, requests.jsonl, report.md
The main run cost $0.38. With the smoke test and follow-ups, the total was about $0.46.
Sources
- Anthropic, Prompt caching and Pricing, accessed 7 October 2026.
- Google, Context caching (Interactions API), last updated 2 September 2026; Context caching (generateContent), 11 September 2026; Pricing, 6 October 2026.
- OpenAI, Prompt caching and Pricing, accessed 7 October 2026.