DEV Community

Andrey Altrouter
Andrey Altrouter

Posted on

Prompt caching has a break-even point, and it's 22%

Every guide to cutting LLM costs eventually says the same thing: turn on prompt caching. Almost none of them mention that caching can make your bill larger.

It has a break-even point, it is computable, and most teams never check which side of it they're on.

Two words first, because the whole argument lives in them. A token is roughly ¾ of a word; models bill per million of them. Prompt caching stores the front part of your prompt — the system prompt, the tool definitions, the documents you resend every time — so the model doesn't reprocess it on the next request. It matches on an exact prefix: change one byte anywhere near the start and everything after it is a miss.

Cache writes cost more than plain tokens

Here is the part that gets skipped. Caching does not have one price, it has two, and one of them is a penalty.

On Anthropic's API, relative to the normal input price per token:

Operation Multiplier
Normal input token 1.0×
Cache write, 5-minute TTL 1.25×
Cache write, 1-hour TTL 2.0×
Cache read 0.1×

TTL is time-to-live: how long the entry survives after it was last touched. Five minutes by default.

So a cache hit is a 90% discount, and a cache miss is a 25% surcharge. Every request that doesn't find a warm entry writes a new one and pays extra for the privilege. Caching is a bet: you're wagering that the entry you just paid 1.25× to create will be read at least a couple of times before it expires.

The break-even hit rate

Your hit rate is the share of cacheable tokens served from cache rather than written to it. Call it h, the write multiplier W, and the read multiplier R. Caching pays off when the blended cost drops below plain input:

(1 − h)·W + h·R  <  1
h  >  (W − 1) / (W − R)
Enter fullscreen mode Exit fullscreen mode

Plug the numbers in:

  • 5-minute cache: (1.25 − 1) / (1.25 − 0.1) = 21.7%
  • 1-hour cache: (2 − 1) / (2 − 0.1) = 52.6%

Below roughly a 22% hit rate, the five-minute cache is costing you money. The one-hour TTL doubles the write cost, so it needs more than half your requests to hit before it earns its keep — it exists for bursty traffic with gaps longer than five minutes, not as a default upgrade.

Make it concrete. A support bot on claude-sonnet-5 ($2.00 per 1M input tokens at list price) with a 20K-token prefix and 200 requests a day. Uncached input: $8.00/day. At a 90% hit rate: $1.72/day. At a 15% hit rate — requests spread far enough apart that most entries expire unread — $8.62/day. Same code, same feature flag, a bill that moved 8% in the wrong direction.

"But my traffic is steady" is a reasonable objection, and if it's true you're fine. The teams that get burned are the ones with low-volume production traffic, a per-user prefix that's never shared, or a nightly batch job spread thin across an hour.

Measure your actual hit rate

Don't estimate it. Every response reports it. Run your real traffic, in its real order, through this:

import anthropic
client = anthropic.Anthropic()

created = read = fresh = 0
for prompt in prompts:          # your real requests, in real order
    r = client.messages.create(
        model="claude-sonnet-5", max_tokens=512,
        system=[{"type": "text", "text": SYSTEM,
                 "cache_control": {"type": "ephemeral"}}],
        messages=[{"role": "user", "content": prompt}],
    )
    u = r.usage
    created += u.cache_creation_input_tokens
    read += u.cache_read_input_tokens
    fresh += u.input_tokens

hit = read / (read + created)
billed = created * 1.25 + read * 0.10 + fresh
print(f"hit rate {hit:.0%} | billed {billed / (created + read + fresh):.2f}x uncached")
Enter fullscreen mode Exit fullscreen mode

If hit comes back at 0% across repeated requests with an identical prefix, something is silently invalidating it — a timestamp in the system prompt, a UUID, an unsorted json.dumps, a tool list assembled in a different order. The cache key is the exact bytes.

If cache_creation_input_tokens is 0 too, your prefix is below the minimum cacheable length. That threshold is per-model and it is not monotonic across generations: 512 tokens on Claude Opus 5, 1024 on Opus 4.8 and Sonnet 5, 4096 on Opus 4.6 and Haiku 4.5. A 3K-token prompt caches on one and silently doesn't on the other, with no error either way.

What this doesn't fix

A good hit rate cuts the token count. It does nothing to the other multiplier — the price per token — and your bill is the product of both. That's where a gateway comes in: altrouter.ai resells the same models at 10–25% below the vendors' own list prices (claude-sonnet-5 at $1.69 per 1M input against Anthropic's $2.00), through the OpenAI-compatible API, so switching is a base_url change. Its honest gap on this exact topic: our usage records don't break out cache-read tokens yet, so the measurement above has to come from the provider's response body rather than our dashboard.

The one number to take away

Caching is not free money, it's a bet at fixed odds: 22% hit rate on the five-minute cache, 53% on the one-hour one. Print your hit rate before you argue about breakpoint placement. If it's under the line, the cheapest change you can make today is turning caching off.

Top comments (0)