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:

The five steps this post takes, numbered in order: what is stored, what a hit changes, where the cached span ends, how long a cache entry lives, and what caching costs
  1. What is stored. Not your text, but work the model has already computed.
  2. What a hit changes. Your cost and speed. Not what you send, and not the window.
  3. Where the cached part ends. An exact prefix, and what a breakpoint marks.
  4. How long it lives. A timer that runs out, and how a read resets it.
  5. 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

Tokens entering a model's attention layers, each producing a key and a value vector, with the full set of those vectors across every layer being the KV cache that gets stored

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

The same request sent twice with identical bytes and an identical context window, where the second time compute is skipped and the bill is discounted because the prefix was read from cache instead of recomputed

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:

DimensionOn a cache hit
Bytes you sendUnchanged — the full payload, every call
Server computeSkipped, for the cached prefix
BillingThe cached prefix is billed at a steep discount instead of full price
Context windowUnchanged — 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.

ModelsMinimum
Claude Opus 5, Fable 5, Mythos 5, Fable 5.1, Mythos 5.1512 tokens
Opus 4.8, Sonnet 5, Sonnet 4.6, Sonnet 4.5, Opus 4.1, Opus 4, Sonnet 41,024 tokens
Opus 4.7, Mythos Preview, Haiku 3.52,048 tokens
Opus 4.6, Opus 4.5, Haiku 4.54,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

The rendered prompt in tools-then-system-then-messages order, with a breakpoint marked on the last system block and the cached span running from the very first token up to that marker

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:

  1. Using all four slots for explicit markers and then adding the automatic one too.
  2. 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_tokens shows a write on every request.
  • cache_read_input_tokens never 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

Three cache entries drawn as nested spans that all start at the same first token, not as adjacent slices sitting side by side

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:

  1. It finds the point where your new request stops matching what is stored.
  2. 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 requestWhere it diverges from what's storedResult
A plain follow-up questionAfter BP3 — nothing earlier changedReads BP3 → cache_read_input_tokens: 16000
The conversation got re-summarizedAt the summary, inside BP2's spanBP3 and BP2 both miss, but BP1 still hits → cache_read_input_tokens: 10000
A seventh tool got addedInside tools, which renders firstEverything 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

A timeline showing a cache write, a hit that resets the expiry clock for free, a second hit, and then an expiry followed by a fresh write once the gap grows too long

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
FieldMeaning
cache_creation_input_tokensTokens written to a new entry this request — a miss, billed at the write rate
cache_read_input_tokensTokens served from an existing entry this request — a hit, billed at the read rate
input_tokensEverything 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_creation splits a write by which TTL it used: ephemeral_5m_input_tokens and ephemeral_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 multiplierRead multiplier
5-minute TTL1.25×0.1×
1-hour TTL2×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:

  1. The prefix is longer than the model's minimum. Below that, cache_control quietly does nothing.
  2. The prefix stays exactly the same, byte for byte, across the requests you want to share it.
  3. 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 forWhy
Agentic tool loopsSame prefix read many times within a minute — the best return on the write
Q&A over one fixed documentThe document itself is the stable prefix
Long-system-prompt chatPrefix only grows, and it's read on every turn
Batch jobs sharing a prefixUse the 1-hour TTL — batches often run past 5 minutes between calls
Skip it forWhy
One-shot, unique-prompt callsEvery call is a write that's never read — pure loss
Short promptsBelow the model's minimum, it silently does nothing
Sporadic trafficGaps consistently longer than the TTL mean every call is a fresh write
Prompts still being editedEvery 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 — unverifiedGemini — unverified
Who decides what gets cachedYou — explicit breakpoints, chosen TTLThe provider, automatically, reportedly on prompts over ~1,024 tokensBoth — automatic implicit caching, plus explicit named caches you create and reference
BreakpointsUp to 4 explicit, plus one automatic slotNoneN/A — you create and name a cache object instead
Lookback window20 positionsN/AN/A
Minimum cacheable prefix512 / 1,024 / 2,048 / 4,096 tokens, model-dependent (see above)Reportedly ~1,024 tokensReportedly ~1,024 tokens (Flash) / ~4,096 tokens (Pro)
Write cost1.25× (5-minute TTL) / 2× (1-hour TTL)Reportedly freeReportedly billed as storage, per hour, on explicit caches
Read cost0.1× (0.025× on Claude Fable 5.1)Reportedly ~90% offReportedly ~90% off
TTLExactly 5 minutes or 1 hourReportedly not configurableReportedly 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-partyAWS Bedrock — unverified
Minimum cacheable prefix512 / 1,024 / 2,048 / 4,096 tokens, model-dependent — no per-platform exception remainsReportedly 1,024 tokens per breakpoint
Default TTL5 minutesReportedly 30 minutes, via prompt_cache_options.ttl
Write cost1.25× (5-minute) / 2× (1-hour)Reportedly 1.25×
Read discount0.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_tokens is 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.