Every major LLM API now caches prompt prefixes: send the same beginning twice and the second call is cheaper and faster. What vendors rarely say is how long a cached prefix survives. Anthropic documents it (5 min, or 1 h on request); OpenAI gives a range ("5 to 10 minutes of inactivity, up to an hour", 24 h on opt-in); DeepSeek says "hours to days". Z.ai, Moonshot (Kimi) and Mistral publish nothing. I could not find a page that measures it.
So I measured it.
The probe is the CACH round of llmprobe(off by default, since it takes as long as the TTL itself):
- A distinct ~2,200-token prefix per sample, primed once and re-checked exactly once, at a fixed age after its own prime. Never poll a single prefix: most caches reset their timer on every hit, and you would measure how long your own polling keeps the entry alive.
- Ages: every 5 min up to 1 h, then 15 min up to 2 h, 30 min up to 4 h, 1 h up to 8 h, 2 h up to 24 h.
- 3 replicas per age, all primed up front, so every age is a 0/3 to 3/3 count, not a single coin flip.
- A check is warm when at least half the prompt (and ≥ 500 tokens) comes back as provider-reported cached tokens.
- An immediate re-send first proves the endpoint caches at all.
- The run stops after two consecutive ages where all 3 replicas are cold, or at 24 h.
Each endpoint ran in its own process on an always-on server, on 2026-10-02/03, from Europe.
Each row is the survival curve: warm replicas out of 3 at each age, then the TTL bracket (last age with 3/3 warm – first age with 0/3).
| Endpoint | Model | Survival (warm / 3) | TTL |
|---|---|---|---|
| OpenRouter → Mistral | mistral-small-3.2 | 5m 0/3 (twice) | < 5 min |
| OpenRouter → Mistral | mistral-large-2512 | 5m 0/3 | < 5 min |
| Azure AI Foundry | deepseek-v4-flash | 5m 1/3 · 10m 1/3 · 15m 0/3 (control: 5m 2/3 · 10m 0/3) | < 5 min, sometimes 10 |
| Anthropic | claude-haiku-4.5, cache_control ttl 5m |
5m 3/3 · 10m 0/3 | 5–10 min |
| OpenAI | gpt-4.1-nano | 5m 3/3 · 10m 0/3 | 5–10 min |
| OpenAI | gpt-4o-mini | 5m 3/3 · 10m 0/3 (twice) | 5–10 min |
| OpenAI | gpt-4.1-nano, prompt_cache_retention: 24h |
5m 3/3 · 10m 1/3 · 15m 0/3 | 5–15 min |
| Z.ai (coding plan) | glm-5.3-flash | 5m 3/3 · 10m 2/3 · 15–20m 3/3 · 25m 1/3 · 30m 0/3 | 5–30 min |
| Kimi (coding plan) | k3 | 5–25m 3/3 · 30m 1/3 · 35m 0/3 | 25–35 min |
| Anthropic | claude-haiku-4.5, cache_control ttl 1h |
5–60m 3/3 · 75m 0/3 | 60–75 min |
| OpenAI | gpt-4.1-mini | 5–45m 3/3 · 50–60m 2/3 · 75m 1/3 · 90m 0/3 | 45–90 min |
| DeepSeek | deepseek-v4-pro | 3/3 or 2/3 at every age, 5 min – 12 h | ≥ 12 h, ~1 miss in 8 (interim, run ends at 24 h) |
| DeepSeek | deepseek-v4-flash | 3/3 at every age, 5 min – 12 h | ≥ 12 h (interim, run ends at 24 h) |
A TTL is a curve, not a number. With 3 replicas per age, most endpoints show a fuzzy edge: glm-5.3-flash had a miss at 10 min and none at 15–20 min; gpt-4.1-mini decays from 3/3 at 45 min to 0/3 at 90 min. Reporting a single "TTL" hides this; report the age of the first miss and the age where nothing survives.
A TTL is a curve, not a number. With 3 replicas per age, most endpoints show a fuzzy edge: glm-5.3-flash had a miss at 10 min and none at 15–20 min; gpt-4.1-mini decays from 3/3 at 45 min to 0/3 at 90 min. Reporting a single "TTL" hides this; report the age of the first miss and the age where nothing survives.
Anthropic does what it says. With an explicit cache_control
breakpoint, Haiku 4.5 was 3/3 warm at 301 s and 0/3 at 600 s for ttl: 5m;
3/3 at every age up to 3,601 s and 0/3 at 75 min for ttl: 1h. A hard,
documented cutoff, no exception in 51 checks.
DeepSeek's own API is in another league. deepseek-v4-flash was still 3/3 warm after 12 hours, the "hours to days" claim holds. deepseek-v4-pro on the same API misses about one check in eight (10 of 78), and the misses do not grow with age (one at 15 min, one at 12 h): not an expiry, more like requests landing on a node that does not hold the entry.
Same model, different host, different cache. deepseek-v4-flash on Azure AI Foundry was already missing at 5 minutes and fully cold by 15. On DeepSeek's API: warm at 12 hours. The model tells you nothing about the cache.
OpenAI's prompt_cache_retention did nothing here. prompt_cache_retention: "24h"
was accepted without error on gpt-4.1-mini and gpt-4.1-nano, and both
curves look like the default ones (cold by 90 and 15 min). At OpenAI, the default
itself depends on the model: 5–10 min for nano and 4o-mini, up to 75 min for
4.1-mini.
Aggregators split the cache. On OpenRouter every upstream (Novita,
DeepInfra, Mistral, …) has its own cache, and unpinned requests are spread
across them. Pinning (provider: {order: [...], allow_fallbacks: false}) is
mandatory to have proper caching.
- Cache minimums. OpenAI caches from 1,024 tokens, Anthropic Haiku 4.5 from 4,096, z.ai in 64-token blocks. Below that, every call reports 0 cached, which looks like a dead cache.
- A missing field is not "no cache". Azure and Kimi omit
cached_tokenswhen nothing was cached, instead of reporting 0. A naive probe reads the first cold check as "this endpoint has no cache". - Deterministic prefixes contaminate re-runs. If the probe builds the same prefixes every run, a smoke test half an hour earlier makes the early checks look warm. Shift the prefixes per run.
- Anthropic only caches what you mark. No cache be default: a 12,500-token prompt sent twice in a row reads 0 cached tokens both times. In my opinion this is very error prone: it is so easy to forget, and nothing warns you, you just pay full price. Every other provider here caches by default.