Skip to content

Instantly share code, notes, and snippets.

@monperrus
Created October 3, 2026 03:47
Show Gist options
  • Select an option

  • Save monperrus/9784f52d5144975fd11be7f642c4a8fc to your computer and use it in GitHub Desktop.

Select an option

Save monperrus/9784f52d5144975fd11be7f642c4a8fc to your computer and use it in GitHub Desktop.
How long does a prompt cache live? Measured TTLs for 11 LLM endpoints

How long does a prompt cache live? Measured TTLs for 11 LLM endpoints

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.

Method

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.

Results

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)

Findings

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.

Measurement pitfalls

  • 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_tokens when 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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment