首页 / 文章 / Cut Repeated Prompt Costs by 90% With One Line of Prompt Caching

本文以英文发布。

Cut Repeated Prompt Costs by 90% With One Line of Prompt Caching

Turn on automatic prompt caching with a single field, verify hits in the usage block, and avoid the prefix mistakes and minimum-length traps that silently erase savings.

914 词

Paying again for text that never changes

Somewhere in your codebase sits a block of text you've paid for ten thousand times, and it hasn't changed once. Open the usage dashboard and put yesterday's input tokens next to the number of requests. If the ratio comes out near the size of your system prompt, this applies to you. You've been paying the full price to send the same wall of instructions repeatedly, and the model reads it cold each time, as if it had never met you.

Take a support bot with roughly 9000 tokens of policy documents in the system prompt and about 40000 calls a month. Every call hauled that whole block across the wire. The fix is a single line. There's also a trap that makes the line worthless, covered below, and it's where most people lose the savings they think they switched on.

Nothing on the server remembers your context

The Messages API is stateless. No session is parked anywhere keeping your prompt warm between calls. Each request rebuilds the prompt from scratch, tokenizes it, runs it through the model and throws the work away. The next call repeats the work at the same price.

without caching

call 1   [ 9,000 tok system ][ 40 tok question ]   full price
  call 2   [ 9,000 tok system ][ 35 tok question ]   full price
  call 3   [ 9,000 tok system ][ 52 tok question ]   full price
            ^^^^^^^^^^^^^^^^^
            same bytes, billed three times

The one-line change

Here's the call as most people write it:

resp = client.messages.create(
    model="claude-sonnet-5",
    max_tokens=1024,
    system=DOCS,
    messages=[{"role": "user", "content": q}],
)

And here is the entire fix, one extra field placed above system:

cache_control={"type": "ephemeral"},

This top-level field turns on automatic caching. The API places the breakpoint on the last cacheable block and moves it forward as the conversation grows.

Check the usage block

Don't trust the impression that things got faster. Read the usage data the API returns with each response:

creation    read    input
  call 1  cold          9012       0       40
  call 2  warm             0    9012       35

That second row is the whole point. Nine thousand tokens were served from cache at a tenth of the price, while input_tokens shrinks to just the part after the breakpoint.

The trap

Put a timestamp at the top of the system prompt, or a user name, or the current date because it seemed harmless, and the prefix changes on every request. The hash never matches, so you pay the write premium indefinitely and collect zero reads. That's worse than doing nothing.

The API cares about a fixed order. Stable content goes first and anything that changes goes last:

tools  ->  system  ->  messages

[ stable stuff ----------------- | volatile ]
                                    ^
                               breakpoint lives here

When cache reads come back empty

A minimum length applies, and when it isn't met, nothing tells you. The minimum is 512 tokens on Opus 5, 1024 on Sonnet 5 and 4096 on Haiku 4.5. Below it there's no caching and no error, only the bill you were trying to avoid.

The cache lasts 5 minutes and refreshes for free on every hit, so steady traffic keeps it warm unaided. If your calls are spaced further apart, request a one-hour TTL. Also remember that matching is exact to the byte: one extra space, one reordered tool definition or one flipped setting sends you back to full price with no warning.

Beyond the single line

Later you might switch to explicit breakpoints and attach cache_control to individual blocks, of which four are allowed. A sensible placement is on the tool definitions and at the end of the documents, since tools rarely change while documents change weekly. Begin with the single line anyway: ship it, watch the usage fields, and only then get clever.

In agent loops the effect compounds. A task with ten tool calls reads the same prefix ten times, so by the last pass caching no longer saves a little but is what keeps the feature affordable.