How Does Prompt Caching Actually Cut Your Bill?
Prompt caching stores a model’s computed work, not your text. It saves money and time, but it never saves space in the context window. What a breakpoint marks, why cache entries nest, how long an entry lives, and when caching pays for itself, worked through step by step on the Claude API.
The previous post in this series, What Fills Your Context Window, and Why Does the Answer Run Out of Room?, looked at what takes up space in a model's context window. It mentioned one thing in passing: a cached prompt still takes up that space, like anything else you send. This post picks up that point. It explains what caching stores, what it saves you, and what quietly breaks it.
Let's start with what happens to your request before the model sees it.
You send separate fields: a system prompt, tool definitions, and a list of messages. The model does not see separate fields. It sees one long line of tokens. The fields are laid end to end, and special marker tokens sit at the boundaries between them.
The fields always go in the same order. Every API that caches this way fixes that order and documents it. On the Claude API the order is tools, then system, then messages. Tool definitions come first, the system prompt next, and the conversation last.
That one long line gives you something exact to point at. You can point at the stretch that runs from the very first token up to some spot you pick. That stretch is called a prefix.
Almost everything in this post comes back to one question: has the server already seen this prefix before?
We'll answer it in five steps:
- What is stored. Not your text, but work the model has already computed.
- What a hit changes. Your cost and speed. Not what you send, and not the window.
- Where the cached part ends. An exact prefix, and what a breakpoint marks.
- How long it lives. A timer that runs out, and how a read resets it.
- What it costs. A small extra charge to store, a big discount to reuse, and when it pays off.
Two more posts follow this one. How Do You Keep a Growing Conversation Cached? covers chats and agent loops, where the prompt gets longer every turn. Why Isn't Your Prompt Cache Hitting? covers what breaks a cache, and how to find out.
What is actually cached
The word "caching" is a little misleading here. It sounds like the server keeps a copy of your text so it can skip reading it next time. That is not what happens.
Inside the model, your tokens pass through a stack of layers called attention layers. These layers let the model compare each token with the tokens before it.
At every layer, each token produces two small lists of numbers. Each list is called a vector. One is the key and one is the value. Roughly, the key says "this is what I am." The value says "this is what to hand back if a later token looks at me."
Working out a key and a value for every token, at every layer, is most of the work the model does before it writes any output. This first step has a name: the prefill pass.
Now gather every key and value, from every layer, for every token in the prefix. That full set is called the KV cache. This set of numbers is what prompt caching stores. It does not store your words.
Once the KV cache for a prefix is stored, a later request that starts with the same prefix can skip prefill for that part. The server loads the stored numbers instead of computing them again.
Two things follow from this. The rest of the post builds on both.
First, the stored numbers only work for the model that made them. A different model can't use them. Even a different version of the same model can't. For example, a Sonnet KV cache means nothing to an Opus model.
Second, the same input always gives the same numbers. Send the same bytes twice, and prefill produces the same keys and values both times. So a stored KV cache only matches a prefix that is exactly the same, byte for byte.
The mechanism is universal — the marker isn't
Anthropic did not invent prompt caching. Every major provider that offers it does the same basic thing. It reuses the stored KV cache for an exact prefix instead of computing it again.
This follows directly from how transformer models work. Any provider running one can skip prefill on a prefix it has already computed. So you should expect every provider to offer caching in some form.
What differs between providers is not the mechanism. It is two practical choices:
- Who marks where the cached part ends.
- Who pays the cost of storing it.
The providers mentioned in this post follow three patterns. (The numbers behind them come later, in the "Every provider has this" section, where they are clearly marked unverified.)
- On Anthropic's Claude API, you place the marker yourself, on a specific part of your request.
- OpenAI reportedly places it for you, automatically. You can't move it.
- Gemini reportedly asks you to create a named cache first, then refer to that name in later requests.
This post uses the Claude API as its worked example. That is on purpose. When you place the marker yourself, you can see every moving part: the breakpoint, the time limit, the layers, and the lookback window. That makes it easier to learn from. It may or may not be the most convenient design to build on, but that isn't the point here.
It also means some parts of this post won't apply to every provider. The details of placing a marker only matter when you place it yourself. Those details include where a marker can go and how many you get.
The rest of the post applies everywhere. It rests on three facts:
- Caching matches an exact prefix, byte for byte.
- Content that stays the same has to come before content that changes.
- One changed byte breaks the match for everything after it.
All three come from how the KV cache works. They don't depend on who places the marker.
What changes on a hit — and what doesn't
Say the server already has a stored KV cache for some prefix. Then a new request arrives that starts with the same prefix. What changes?
Nothing about what you send. You still send the full request, every field and every message, on every call.
This is true on the Claude API and on every API built like it. The server keeps no memory of your conversation between calls. This is called being stateless. A request that uses the cache looks exactly the same, on the wire, as one that doesn't.
The server compares the incoming bytes with what it has stored. Then one of two things happens:
- A write: no matching prefix was found. The server runs the full prefill pass, computes the KV cache, and stores it for later.
- A read: a matching prefix was found. This is also called a cache hit. The server skips prefill for that part and loads the stored keys and values instead.
Either way, you get back a normal response. Here is what a hit changes, and what it doesn't:
| Dimension | On a cache hit |
|---|---|
| Bytes you send | Unchanged — the full payload, every call |
| Server compute | Skipped, for the cached prefix |
| Billing | The cached prefix is billed at a steep discount instead of full price |
| Context window | Unchanged — cached tokens occupy exactly as much window as uncached ones |
People often get the last row backwards. Caching changes what you pay and how fast the answer starts. It does not change how much of the window your prompt uses. A cached prefix still counts against the model's context limit, token for token. The previous post, What Fills Your Context Window, and Why Does the Answer Run Out of Room?, covers that limit.
A build cache in software is a good comparison. You still give the compiler your whole project on every build. The command you type doesn't change. The compiler just skips files that haven't changed since last time. Same code, same command, but a faster build. It isn't storing less of your project. It is doing less work on the parts it already knows.
Why skipping the work saves money
Why does skipping work lower your bill? Because the price of input tokens is mostly the price of computing them. That holds for any provider that prices input this way.
Prefill uses GPU time, and more tokens means more GPU time. The price per input token is that GPU time plus the provider's margin. A read skips prefill for the cached part, so the provider passes some of that saving back to you.
Only some of it, though. A read is never free, on any provider. The server still has to keep your stored keys and values somewhere fast, and that storage costs money too. So you swap a large compute bill for a smaller storage bill.
On the Claude API, a read costs about 0.1× the normal input price. On Claude Fable 5.1 it is 0.025×. The exact discount depends on the provider, but the swap itself is the same everywhere.
A write works the other way. It costs more than a normal request, everywhere this mechanism exists. A write does the full prefill, like any request. Then it also saves the result so it can be found later. That is extra work, so it costs extra. On the Claude API, a write costs 1.25× the normal rate.
The cache key is the bytes themselves
A read happens only when the server finds a matching prefix. But how does it decide that two prefixes match?
Byte for byte. The match is exact. The server doesn't look for a prefix that means the same thing, or one that is close enough. It checks whether the incoming bytes, from the first token up to a given point, are identical to a stored prefix.
Change one character in that prefix and the match fails from that point on. That could be reordering a tool, editing one word in the system prompt, or adding a stray space.
Think of a bookmark at page 40 of a book. It only helps if pages 1 to 40 are still the same as when you placed it. Add a paragraph on page 3, and every later page shifts. The bookmark is still there, but it no longer marks where you stopped.
So what identifies a stored KV cache? Not your API key, not your account, and not a name you choose. It is a content hash. The server runs the exact bytes of the prefix through a hash function. That gives a short fingerprint of that exact content.
This post calls one stored, reusable piece of KV cache an entry. Each entry is identified by its fingerprint.
A few things follow from that:
- Two pipelines on the same API key, with different tools and system prompts, get different fingerprints. They land in different entries on their own. You don't need to set anything up to keep them apart, and they can't collide.
- Two separate pipelines that happen to send the exact same prefix to the same model share one entry. That is a benefit, not a leak. The same bytes on the same model always produce the same KV cache, no matter who sent them.
This idea is called content-addressed storage. A Docker layer cache works the same way. You never name the entry yourself. The content is the name.
Using the content as the name has two more effects.
The first is isolation. An entry is never shared across certain boundaries. Where that boundary sits depends on the platform:
- On the Claude API, and on Claude Platform on AWS and Microsoft Foundry, entries are kept separate per workspace. One workspace can't read another workspace's cache, even inside the same organization.
- On Amazon Bedrock and Google Cloud, the boundary is per organization instead.
Either way, nothing is ever shared between organizations. One practical tip: if you split the same prompt across two workspaces, you get two separate entries. Check for this before you blame the prompt for a low hit rate.
The second is that the model is part of the fingerprint. A Sonnet 5 KV cache and an Opus 5 KV cache for the same prompt text are two different entries. The numbers one model produces are meaningless to the other.
Setting a breakpoint
This section and the next few use the Claude API as the worked example. Providers that cache automatically have no marker to place. So the placement details here won't apply to them, but the prefix rule still does.
On the Claude API, you tell the server where a cacheable part of the prompt ends. That marker is called a breakpoint. It is a cache_control field that you attach to one block of your request. Once it is there, writes and reads happen automatically.
{"type": "text", "text": "...", "cache_control": {"type": "ephemeral"}}
ephemeral is the only kind of cache entry the API makes right now. It means the entry has a time-to-live (TTL). That is a countdown. If nothing reads the entry before it runs out, the entry is deleted. There are two TTLs, covered a few sections down. The default shown above is 5 minutes.
The model never sees this field. The serving system reads it before generation starts. It is not an instruction for the model.
There is one more condition. The prefix must be at least as long as the model's minimum cacheable length. If it is shorter, cache_control quietly does nothing. You get no error, no hit, and no write.
| Models | Minimum |
|---|---|
| Claude Opus 5, Fable 5, Mythos 5, Fable 5.1, Mythos 5.1 | 512 tokens |
| Opus 4.8, Sonnet 5, Sonnet 4.6, Sonnet 4.5, Opus 4.1, Opus 4, Sonnet 4 | 1,024 tokens |
| Opus 4.7, Mythos Preview, Haiku 3.5 | 2,048 tokens |
| Opus 4.6, Opus 4.5, Haiku 4.5 | 4,096 tokens |
Note that newer models don't always have a lower minimum. The newest models need 512 tokens, but Opus 4.6, Opus 4.5, and Haiku 4.5 need 4,096.
So a 3,000-token prompt caches fine on Opus 5, Opus 4.8, and Sonnet 4.5. On Opus 4.6 or Haiku 4.5, it quietly doesn't cache at all, and nothing tells you. These minimums are the same on every platform the model runs on.
The marker ends the span; it never starts one
This is the detail that trips people up first. A breakpoint marks where a cached prefix ends. It never marks where it begins.
There is nothing to mark for the start. A prefix always starts at the first token of the prompt. So "set a breakpoint" means just one thing: pick a block and say, "cache everything from the start of the prompt up to and including this block."
Remember the order: tools, then system, then messages. So if you mark the last block of your system prompt, the tool definitions are cached too. You didn't ask for that. It happens because tools come before the system prompt.
Here is a customer-support example. It has three tool definitions, a persona paragraph, and then an escalation-policy paragraph with the breakpoint on it.
response = client.messages.create(
model="claude-opus-5",
max_tokens=1024,
tools=[
{"name": "lookup_order", "input_schema": {...}},
{"name": "search_kb", "input_schema": {...}},
{"name": "create_ticket", "input_schema": {...}},
],
system=[
{"type": "text", "text": PERSONA},
{
"type": "text",
"text": ESCALATION_POLICIES,
"cache_control": {"type": "ephemeral"}, # breakpoint here
},
],
messages=[
{"role": "user", "content": f"Context:\n{chunks}\n\nQ: {question}"},
],
)
Here is what gets fingerprinted and stored:
tools ──────────► system ──────────►│ messages
lookup_order PERSONA │ chunks
search_kb ESCALATION_... │ question
create_ticket ▲ │
marker │
└──────── cached span ──────────┘ └── everything after: not cached
You never said "cache the tools." You only said "the cached part ends here." The three tools came along for free, because they come before the system prompt.
Automatic caching, and when it backfires
Placing breakpoints by hand is the explicit way. There is also an automatic way. You put a single cache_control at the top level of the request, instead of on a block.
response = client.messages.create(
model="claude-opus-5",
max_tokens=1024,
cache_control={"type": "ephemeral"}, # top level, not on a block
system=[...],
messages=[...],
)
Here is how the automatic mode behaves:
- The server puts the breakpoint on the last cacheable block for you.
- As the conversation grows, it moves the breakpoint forward. You never touch it again.
- You can use it alongside your own explicit breakpoints.
- It uses up one of your four breakpoint slots. So the most you get is one automatic marker plus three explicit ones.
Two mistakes return an HTTP 400 error instead of failing quietly:
- Using all four slots for explicit markers and then adding the automatic one too.
- Putting an explicit marker on the last block with a different TTL from the top-level one.
Now, when does it backfire? Automatic caching puts the breakpoint at the end of the prompt, on whatever the last block is.
Whether that is right depends on one question about the last block:
Will the next request send this block again, unchanged?
- In a plain chat, yes. The newest message becomes part of the history. The next request sends it again, byte for byte, with the new turn added after it. So a marker on it gets read next time. That is the right place for it.
- In a one-off question, no. Say the prompt ends with retrieved chunks and a question that are used once and thrown away. The breakpoint then lands after that unique part.
In the one-off case, every request pays the 1.25× write price to store bytes nobody will ever ask for again. You pay extra on every call and get nothing back.
Here is how to spot it in your logs:
cache_creation_input_tokensshows a write on every request.cache_read_input_tokensnever covers your whole shared prefix.
If you see that, automatic caching is marking the wrong end of the prompt. The fix is the same as in the example above. Place an explicit breakpoint yourself, at the end of the part that is actually shared.
Chats are where the answer gets interesting. In an agent loop it is yes, and automatic caching works well. In a RAG chat, whose newest message carries this turn's chunks, it is usually no. The next post, How Do You Keep a Growing Conversation Cached?, works out where the markers go in both.
Nested, not sliced
A common mistake is to think each breakpoint caches its own separate section. That would be like slicing a loaf of bread into pieces that sit side by side.
That's not how it works. Each breakpoint's entry holds everything from the first token up to that marker. That includes whatever an earlier breakpoint already covered.
Prompt: [ Tools ][ System ][ Msg 1 ][ Msg 2 ][ Msg 3 ]
▲ ▲ ▲
BP1 BP2 BP3
Entry 1: ├────────┤ (tools)
Entry 2: ├───────────────────┤ (tools + system)
Entry 3: ├────────────────────────────────┤ (tools + system + msg 1 + msg 2)
Think of Russian dolls, not slices. Entry 3 contains entry 2, and entry 2 contains entry 1.
There is no entry that holds only "Msg 1 and Msg 2" by themselves. Every cached part starts at the very first byte. So every entry reaches all the way back to the start of the prompt.
Which entry gets used
With entries nested inside each other, which one does a new request use? You don't choose. The server works it out in two steps:
- It finds the point where your new request stops matching what is stored.
- It uses the longest stored prefix that ends at or before that point.
Here are the three breakpoints from above, at realistic sizes:
BP1 after tools + system → 10,000 tokens
BP2 after a chat summary → 13,000 tokens
BP3 after the message history → 16,000 tokens
Now three different follow-up requests, with three different results:
| Next request | Where it diverges from what's stored | Result |
|---|---|---|
| A plain follow-up question | After BP3 — nothing earlier changed | Reads BP3 → cache_read_input_tokens: 16000 |
| The conversation got re-summarized | At the summary, inside BP2's span | BP3 and BP2 both miss, but BP1 still hits → cache_read_input_tokens: 10000 |
| A seventh tool got added | Inside tools, which renders first | Everything misses — a full prefill on the whole prompt |
The middle row is the whole reason to use more than one breakpoint.
Without BP1, re-summarizing the chat would force the server to recompute the tools and the system prompt too. Those two parts didn't change. With BP1 underneath, that stable base still pays off, even after everything above it has changed.
Breakpoints themselves are free. You only pay for tokens written or read, never for the marker. You get up to four per request, so there is rarely a reason to use just one.
TTL, and a clock that starts earlier than you think
On the Claude API, every entry expires unless something reads it in time. There are exactly two lifetimes. You can't pick a custom one, like six hours.
{"cache_control": {"type": "ephemeral"}} # 5 minutes, the default
{"cache_control": {"type": "ephemeral", "ttl": "1h"}} # 1 hour
A read is a hit. It writes nothing new and costs the discounted read price. And here is the part worth remembering: a read resets the entry's timer, for free.
Adding cache_control to a request never forces a new write. A write only happens on a real miss.
Here is a timeline with the 5-minute default:
t = 0:00 request 1 → MISS. Writes the entry. Expires ≈ 5:00.
t = 3:00 request 2 → HIT. Reads it. Expires ≈ 8:00 — clock reset, free.
t = 6:00 request 3 → HIT. Expires ≈ 11:00.
t = 25:00 request 4 → MISS (the last hit was at 6:00; nothing arrived before 11:00).
Writes a fresh entry.
So you pay the 1.25× write price once. After that, every request pays only the cheap read price, as long as no gap between requests is longer than the TTL. A busy chatbot can keep one entry alive for as long as it likes on the 5-minute setting, because every turn refreshes it.
The trap: the timer starts when the request that writes or reads the entry begins. It does not start when the response finishes.
That means the time spent generating the answer counts against the TTL, just like idle time. Say a response takes 4 minutes to stream out. That leaves only about 1 minute for the next request to arrive before a 5-minute entry expires. A long agent turn can use up most of its own TTL just writing output, even though nothing looks idle.
One rule applies if you use both TTLs in one request. Entries with the longer TTL must come earlier in the prefix than entries with the shorter one.
In practice you'd want that order anyway. Put your most stable content (tools, system prompt) at the front on the 1-hour TTL. Put the faster-changing part after it on the 5-minute default.
Reading the usage fields
On any API that offers caching, you should check how much of your input came from the cache and how much was computed fresh. Each provider names these numbers differently.
On the Claude API, every response includes a usage object. It splits your input into three parts that never overlap:
total input = input_tokens
+ cache_creation_input_tokens
+ cache_read_input_tokens
| Field | Meaning |
|---|---|
cache_creation_input_tokens | Tokens written to a new entry this request — a miss, billed at the write rate |
cache_read_input_tokens | Tokens served from an existing entry this request — a hit, billed at the read rate |
input_tokens | Everything else: not written, not read, processed at full price |
The last one catches almost everyone at least once: input_tokens is what's left over, not the total.
It is the part of the prompt after the last breakpoint that wrote or read anything. Usually that is the part that changes every request, like retrieved chunks or the newest message. If you read input_tokens as your full prompt size, you can undercount by a factor of ten or more.
Two more fields add detail:
usage.cache_creationsplits a write by which TTL it used:ephemeral_5m_input_tokensandephemeral_1h_input_tokens.- If your request uses a server-side tool like web search, you may see an extra 5-minute write right after the tool results. You didn't mark that spot. The server adds its own cache point after tool output. This is normal. It does not mean your cache broke.
What it actually costs
The multipliers
These are the Claude API's prices, as multiples of the normal input price:
| Write multiplier | Read multiplier | |
|---|---|---|
| 5-minute TTL | 1.25× | 0.1× |
| 1-hour TTL | 2× | 0.1× (0.025× on Claude Fable 5.1) |
A read costs the same no matter which TTL wrote the entry. So the 1-hour TTL isn't "better caching." It just keeps the entry alive longer between requests, and the write costs twice as much.
Think of it as insurance. You pay more up front in case your traffic comes less often than every five minutes.
Break-even
Here is the cost per token of a cached prefix, over N requests that all use the same entry:
No caching: N × 1.0
Cache, 5m: 1.25 + 0.1 × (N − 1)
Cache, 1h: 2.0 + 0.1 × (N − 1)
With the 5-minute TTL, two requests already break even. One write plus one read costs 1.25× + 0.1× = 1.35×. Two uncached calls cost 2×.
With the 1-hour TTL, you need at least three requests. One write plus two reads costs 2× + 0.2× = 2.2×. Three uncached calls cost 3×.
Below those counts, caching costs more than not caching. At or above them, it is cheaper, and every extra read makes it cheaper still.
A worked example
Take a customer-support RAG bot that answers one-off questions, shaped like the breakpoint example earlier. Each request is a new question with its own chunks. There is no chat history.
System prompt (persona, escalation policy) + 6 tool definitions
— identical on every request 10,000 tokens
Retrieved knowledge-base chunks — different every query 3,000 tokens
The user's message 200 tokens
──────────────
Total input 13,200 tokens
The breakpoint sits at the end of the 10,000-token fixed block.
The numbers below are worked out by hand to show what a real bill looks like. They don't come from a live call. But the math matches what a real usage object shows for a prompt built this way.
Request 1, nothing cached yet:
{
"cache_creation_input_tokens": 10000,
"cache_read_input_tokens": 0,
"input_tokens": 3200,
"output_tokens": 450
}
Request 2, same fixed prefix, a new question and new chunks (a little longer this time):
{
"cache_creation_input_tokens": 0,
"cache_read_input_tokens": 10000,
"input_tokens": 3750,
"output_tokens": 380
}
Look at the pattern across requests, not just the numbers. cache_read_input_tokens stays at 10,000 every time. input_tokens goes up and down with the size of each question and its chunks. For a bot like this, that is the healthy pattern.
A chat has a different healthy pattern, because its prompt grows every turn. The next post works that pattern out.
Only 10,000 of the 13,200 tokens get cached. That is on purpose. The chunks and the user's message change on every call. Caching them would mean paying the 1.25× write price on bytes you know you'll never read again. You cache what repeats. You leave what changes after the breakpoint.
Now run it over 100 questions, with a normal input price of 1.0 per token:
No caching: 100 × 10,000 × 1.0 = 1,000,000 units
With caching: 1 × 10,000 × 1.25 (one write) = 12,500
99 × 10,000 × 0.10 (99 reads) = 99,000
──────────
111,500 units
That is about 89% off the cost of the fixed prefix. On top of that, every request after the first starts answering sooner, because prefill was skipped.
There is one more bonus on the Claude API. Cache reads mostly don't count against your input-token rate limit. So a workload that reads a lot from cache can get more done within the same rate limit.
Should you cache everything? No.
Trying to cache isn't free. Because writes cost extra, caching the wrong workload costs more than not caching at all. Three conditions must all be true before it is worth it:
- The prefix is longer than the model's minimum. Below that,
cache_controlquietly does nothing. - The prefix stays exactly the same, byte for byte, across the requests you want to share it.
- It gets read at least twice before a 5-minute entry expires, or at least three times before a 1-hour one does. These are the break-even counts from above.
| Use it for | Why |
|---|---|
| Agentic tool loops | Same prefix read many times within a minute — the best return on the write |
| Q&A over one fixed document | The document itself is the stable prefix |
| Long-system-prompt chat | Prefix only grows, and it's read on every turn |
| Batch jobs sharing a prefix | Use the 1-hour TTL — batches often run past 5 minutes between calls |
| Skip it for | Why |
|---|---|
| One-shot, unique-prompt calls | Every call is a write that's never read — pure loss |
| Short prompts | Below the model's minimum, it silently does nothing |
| Sporadic traffic | Gaps consistently longer than the TTL mean every call is a fresh write |
| Prompts still being edited | Every edit orphans the entry the previous edit just paid to write |
To choose a TTL, look at the typical gap between requests that share a prefix:
- Under about 4 minutes: use the 5-minute default. Each read keeps it alive for free, so you never need the 1-hour price.
- Between 4 minutes and an hour: use the 1-hour TTL. Paying 2× once beats paying 1.25× again and again.
- Usually over an hour: don't cache that path at all.
The real test is simple. Record a day of real traffic. Compare cache_read_input_tokens with cache_creation_input_tokens. Reads should be much larger than writes. If they are about equal, caching is costing you money right now, not saving it.
Every provider has this — they just disagree about who drives
As we saw near the start, prompt caching isn't unique to Anthropic or the Claude API. Every major provider that offers it reuses a stored prefix the same way underneath.
What differs is the same two things as before:
- who places the marker, and
- who pays to write the cache.
That also decides whether a lookback limit applies to you at all. On the Claude API, the server searches at most 20 positions back from a marker for an entry an earlier request stored. The next post covers that limit.
Unverified below. Everything in this section about OpenAI, Gemini, and Bedrock comes from the author's working notes, not from the reference this post is otherwise verified against. None of it was checked against live provider documentation in this session — last checked 2026-09-09. Provider prices, minimums, and TTLs change often; treat every number below as a starting point to re-verify, not as a fact to build a cost model on.
| Anthropic (Claude API) | OpenAI — unverified | Gemini — unverified | |
|---|---|---|---|
| Who decides what gets cached | You — explicit breakpoints, chosen TTL | The provider, automatically, reportedly on prompts over ~1,024 tokens | Both — automatic implicit caching, plus explicit named caches you create and reference |
| Breakpoints | Up to 4 explicit, plus one automatic slot | None | N/A — you create and name a cache object instead |
| Lookback window | 20 positions | N/A | N/A |
| Minimum cacheable prefix | 512 / 1,024 / 2,048 / 4,096 tokens, model-dependent (see above) | Reportedly ~1,024 tokens | Reportedly ~1,024 tokens (Flash) / ~4,096 tokens (Pro) |
| Write cost | 1.25× (5-minute TTL) / 2× (1-hour TTL) | Reportedly free | Reportedly billed as storage, per hour, on explicit caches |
| Read cost | 0.1× (0.025× on Claude Fable 5.1) | Reportedly ~90% off | Reportedly ~90% off |
| TTL | Exactly 5 minutes or 1 hour | Reportedly not configurable | Reportedly configurable |
The biggest difference isn't the lookback limit. It is who pays for the write. Going by the unverified figures above:
- OpenAI reportedly charges nothing for writes. There is no decision to make and no way to lose money. It just happens in the background.
- Anthropic charges extra for a write, and in return gives you control. You choose where the breakpoint goes and which TTL to use. The price is that you have to think about placement.
- Gemini's explicit caches reportedly charge by storage time instead of by token. That is a different kind of cost to plan for.
The same models on a different platform can also have different numbers:
| Anthropic first-party | AWS Bedrock — unverified | |
|---|---|---|
| Minimum cacheable prefix | 512 / 1,024 / 2,048 / 4,096 tokens, model-dependent — no per-platform exception remains | Reportedly 1,024 tokens per breakpoint |
| Default TTL | 5 minutes | Reportedly 30 minutes, via prompt_cache_options.ttl |
| Write cost | 1.25× (5-minute) / 2× (1-hour) | Reportedly 1.25× |
| Read discount | 0.1× (0.025× on Fable 5.1) | Reportedly ~90% |
Note a conflict in the first row. The verified minimum table earlier in this post says there is no longer any per-platform exception to the minimums, on any model. The Bedrock figure here is unverified.
Whether Bedrock's default TTL differs from Anthropic's own is a separate question. It stays unverified either way. Treat the 30-minute figure as unconfirmed until you check it against Bedrock's current documentation.
One thing holds for every provider, verified or not: keeping the prefix exactly the same is what decides your hit rate. Stable content first. Changing content last. No timestamps or request IDs near the front of the prompt.
That habit pays off the same way on Claude, GPT, and Gemini. The only difference is whether you get to say where the stable part ends.
Recap
- Caching stores the model's computed work: the KV cache of keys and values from the prefill pass. It never stores your text or the output. This is how it works on every provider.
- A hit lowers your cost and speeds up the answer. It never changes what you send. It never reduces how much of the context window your prompt uses.
- The cache matches on the exact bytes of the prompt, up to a breakpoint. A breakpoint marks where the cached part ends. The start is always the first token.
- Breakpoints nest; they don't slice. The server uses the longest stored prefix that still matches. That is why placing more than one breakpoint pays off.
- Automatic caching marks the last block. That is right only if the next request sends that block again, unchanged. For a one-off question with chunks, place the breakpoint yourself.
- On the Claude API, the TTL is exactly 5 minutes or 1 hour. A read resets the timer for free. But the timer starts when the request begins, so a slow answer uses up its own TTL.
- On the Claude API,
input_tokensis what's left over after caching, not your prompt size. Add all three usage fields to get the total. - On the Claude API, a write costs 1.25× (5 minutes) or 2× (1 hour). A read costs about 0.1× (0.025× on Fable 5.1). You break even after two requests on the short TTL, or three on the long one.
- Cache only a prefix that is long enough, stays byte-for-byte the same, and gets read again before it expires.
Prompt caching never stores your text and never shrinks your context window. It stores the model's computed work for an exact prefix. It only pays off when that exact prefix is read again before its timer runs out.
Next up: everything so far uses a prompt whose start stays put. A chat is different, because every request is longer than the last. How Do You Keep a Growing Conversation Cached? covers:
- where the markers go when the end of the prompt moves,
- what each turn really costs,
- and how to shrink a long history without breaking the cache.
After that, Why Isn't Your Prompt Cache Hitting? covers what breaks a cache, and how to catch it.