The rule
The cache matches a prefix: the longest run of tokens from the start of your prompt that is byte-identical to a previous request. Everything from the first difference onward is recomputed and billed as uncached. So an agent turn that appends to the end of its context reuses almost everything. A turn that changes something near the beginning reuses almost nothing, however small the change. The match is counted in whole cache blocks, and the trailing partial block is billed at the input rate. The unit size varies by model, so a very short prompt may never hit, and the discount does the most work on long prefixes. Readusage.prompt_tokens_details.cached_tokens on your own responses to see how much of your prompt is matching.
One more shape matters on qwen3-8-flash-next: a conversation that appends
warms on turn 2, as in the diagram, but a shared prefix re-sent with a
changing suffix, many questions against one document, may take an extra send before cached_tokens shows it warm.
What breaks a prefix
Every item below has been observed reducing a real workload’s hit rate. They are ordered by how often they turn out to be the cause.A timestamp or date in the system prompt
A timestamp or date in the system prompt
The single most common cause.
Current time: 2026-08-21T09:14:22Z at the top
of a system prompt changes on every request, so nothing after it can ever
match.Move it to the end of the conversation, as the last user message or a
trailing system note. It is just as visible to the model there and it stops
invalidating everything behind it.Tool definitions in an unstable order
Tool definitions in an unstable order
If your tool list is built from a set, a dictionary, or a directory listing,
its order can change between processes even when the tools do not. Sort tool
definitions by name before serializing, once, and keep that order for the life
of the session.
Non-deterministic JSON serialization
Non-deterministic JSON serialization
JSON.stringify over an object preserves insertion order, which can differ
between runs. Python’s json.dumps accepts sort_keys=True. If you build
tool schemas or context blocks programmatically, serialize them
deterministically.Whitespace counts too: a pretty-printer that changes indentation between
versions changes the bytes.A session or request id inside the prompt
A session or request id inside the prompt
Anything unique per request belongs outside the prompt. If you need the model
to know a session id, put it at the end, not in the system block.
Context compaction that rewrites the middle
Context compaction that rewrites the middle
Summarizing older turns is good practice, but replacing the middle of a
conversation invalidates the prefix from the summary onward. Compact at a
boundary you keep stable, and prefer appending a summary to rewriting history
in place.
Reordering retrieved documents
Reordering retrieved documents
If retrieved context is sorted by a score that shifts slightly between calls,
the block order changes. Sort retrieved chunks by a stable key, such as
document id, rather than by score.
Reading your own hit rate
Every hosted response reportsusage.prompt_tokens_details.cached_tokens,
the count of input tokens that request was billed at the cached input rate,
and the same number as usage.runinfra.cached_input_tokens beside the cost.
It is 0 rather than absent when nothing was billed as cached. Print it on
every turn: on a workload that should be repetitive, the value that keeps
coming back is where your prompt stops matching itself. The field is
described in Chat completions.
Your workspace’s cache report is on the dashboard’s
Usage Analytics page, read with your
signed-in dashboard session rather than an API key. The Cache by API key
band carries the workspace totals: cached input tokens, the confirmed hit
rate, confirmed misses, and confirmed savings. The By API key and
By model tables on the same page carry cached tokens, hit rate, and
savings per key and per model. A low rate on a workload that should be
repetitive means something above is changing.
To compare many requests, record their cached-token counts and look at the
distribution, not just an average. Its most useful summary is the mode:
the cached-token count that recurs most often. Because matching is counted in
whole cache blocks, this is roughly where your prompt stops matching itself;
past that point, a field in your prompt may be differing between requests.
Keeping a session warm
On every chat model but one, retention of a warm prefix is best effort: an idle prefix can be evicted under capacity pressure, and no lifetime is promised. The exception is GLM 5.3 Flash (glm-5-3-flash), where the prefix behind a session you name is kept across idle gaps, for up to 6 hours at the fastest reuse and up to 72 hours since the last request, at no charge. Those windows are ceilings rather than guarantees; idle state can be dropped earlier under capacity pressure, and the tier table is on the
session affinity page.
On /v1/models, cache_tiers and cache_retention
tell you which kind of cache a model runs. best_effort with cache_tiers
["gpu"] is the GPU copy alone. tiered_host_memory means a prefix can outlive the fastest tier: DeepSeek V4 Pro (deepseek-v4-pro) publishes it, and retention there is still best effort. The
glm-5-3-flash keep windows are
published on the session affinity and
data retention pages rather than as fields in that
listing. session_affinity_idle_ttl_seconds is a routing window, how long a
quiet session keeps its place close to its cache, not a
retention promise.
The cached-token count on the first turn after a pause is the readout of what
survived it. If the copy was lost while your agent ran tools, waited on a
human, or slept overnight, that turn is billed as a miss; the count and the
bill agree. The matching rule at the top of this page is unchanged: a
returning prompt must be byte-identical to the prefix it wants back.
On any chat model, a stable x-session-id
keeps a session’s follow-up turns close to its warm cache, and pins that placement for the model’s published session_affinity_idle_ttl_seconds idle window. Without one, a
prompt_cache_key in the request body routes the request by its value but
does not pin it. Without either, placement still keeps steady sessions warm;
the full placement order is on the session affinity page.
What does not affect caching
- Sampling parameters.
temperature,top_p,seed, andmax_tokensare not part of the prefix. - Streaming. A streamed and a non-streamed request cache identically.
- Your traffic volume. Caching is per prefix, not a plan or an allowance.
Isolation
On models that publishcache_isolation: "isolated" in
/v1/models, a cached prefix is scoped to your
workspace within the model’s published isolation scope. Another customer
does not reuse your isolated prefix, and you do not reuse theirs. That scoping
applies on every request. Read the per-model tier scope in
Data retention.
On a model that does not publish isolation, a hit is neither disclosed nor
priced. A billable response charges every input token at the standard input
rate. The response reports cached_tokens: 0, including when it settles at zero.
Next steps
Session affinity
Keep every turn close to your warm prefix.
Models
Read cache isolation, tiers, and retention per model.
Tool calling
The agent loop these prefixes come from.