Claude API Response Caching to Reduce Cost
If you're spending more on Claude API calls than expected, caching is the single most effective lever you have. There are two distinct caching strategies you should be using together: prompt caching (a native Claude feature that discounts repeated input tokens) and application-level response caching (storing full responses so you skip the API call entirely for repeated requests). Most teams only use one, and leave significant savings on the table.
This article covers both, shows you how to implement them, and explains when each one actually helps versus when it's wasted effort.
The two layers of caching you need
Prompt caching happens on Anthropic's side. You mark part of your prompt (a system prompt, a long document, a set of tool definitions) as cacheable, and subsequent requests that reuse that exact prefix get charged a fraction of the normal input token price for the cached portion. This is ideal when you send the same large context repeatedly — a RAG document, a codebase excerpt, a set of few-shot examples — with only the user's question changing.
Application-level response caching happens in your own infrastructure, before the request ever reaches Claude. If a user asks the exact same question twice, or if ten users trigger the same classification prompt on the same input, you don't need to call the model at all — you serve the cached response. This saves 100% of the cost for that call, not just a discount on input tokens.
Used together, you can cut a meaningful chunk of your Claude spend without changing your prompts or your product behavior.
Using native prompt caching
Anthropic's prompt caching works by marking a block in your request with cache_control. The first call writes the cache; subsequent calls within the cache lifetime (currently a few minutes, refreshed on each hit) read from it at a steep discount.
curl https://api.anthropic.com/v1/messages \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "content-type: application/json" \
-d '{
"model": "claude-opus-4-20250514",
"max_tokens": 1024,
"system": [
{
"type": "text",
"text": "<your long system prompt or document here>",
"cache_control": {"type": "ephemeral"}
}
],
"messages": [
{"role": "user", "content": "Summarize section 3."}
]
}'
Good candidates for prompt caching:
- Long system prompts that don't change between requests
- Large reference documents used across many questions
- Tool/function definitions that are identical across calls
- Few-shot examples embedded in the prompt
Prompt caching does not help if every request has unique content throughout — there's nothing stable to cache. It also doesn't eliminate the API call itself; you still pay for output tokens and get a new generation each time.
Application-level response caching
This is where you skip the call entirely. The pattern is simple: hash the inputs that determine the output, check your cache, and only call Claude on a miss.
import crypto from "crypto";
const cache = new Map(); // swap for Redis in production
const TTL_MS = 10 * 60 * 1000;
function cacheKey(systemPrompt, userMessage, model) {
return crypto
.createHash("sha256")
.update(systemPrompt + "::" + userMessage + "::" + model)
.digest("hex");
}
async function askClaude(systemPrompt, userMessage, model) {
const key = cacheKey(systemPrompt, userMessage, model);
const hit = cache.get(key);
if (hit && Date.now() - hit.timestamp < TTL_MS) {
return hit.response;
}
const res = await fetch("https://api.subtoapi.app/v1/messages", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.SUBTOAPI_KEY}`,
"content-type": "application/json",
},
body: JSON.stringify({
model,
max_tokens: 1024,
system: systemPrompt,
messages: [{ role: "user", content: userMessage }],
}),
});
const data = await res.json();
cache.set(key, { response: data, timestamp: Date.now() });
return data;
}
This pattern pays off most for:
- Classification and extraction tasks where the same inputs recur (support ticket tagging, content moderation)
- Shared endpoints where multiple users ask near-identical questions (FAQ bots, documentation search)
- Development and testing — never burn tokens re-running the same prompt while debugging
It pays off least for anything conversational or personalized, where the input is unique per request by design. Don't cache responses that depend on real-time data (stock prices, current date, live search results) unless your TTL is short enough to stay accurate.
Cache invalidation: the part people skip
A cache with no invalidation strategy is a bug generator. Set a TTL that matches how often the underlying "truth" changes — minutes for anything time-sensitive, hours or days for static reference content. If your system prompt or document changes, make sure your cache key includes a version identifier for it, otherwise you'll serve stale answers against an updated prompt.
For semantically similar-but-not-identical queries ("what's your refund policy" vs "how do refunds work"), exact-match hashing won't catch the overlap. That requires embedding-based similarity caching, which is a heavier lift and only worth it at high query volume with a narrow domain.
Combining both for maximum savings
A well-tuned pipeline looks like this: check your application cache first (zero cost on a hit), and if it's a miss, send the request with prompt caching enabled on the stable parts of your context (reduced cost on a hit, full cost only on a true first-time call). This stacks the savings — most requests cost nothing, and the ones that do reach Claude are cheaper than they'd otherwise be.
If you're running this through SubToAPI, the dashboard's usage metadata shows token counts per request, so you can measure how much your caching layer is actually saving before and after you ship it — see /docs/messages for the response fields and /docs/streaming if you're caching around streamed responses. You can test the setup free at /signup; plans start at €9/month on the Solo tier, see /pricing for details.
questions
Does prompt caching change the model's output? No. It only affects billing and latency for the cached portion of the input. The model still generates a fresh response to your actual message every time.
How long does Claude's prompt cache last? The cache lifetime is short — typically a few minutes — and refreshes on each cache hit. It's designed for bursts of related requests, not long-term storage, which is why application-level caching is still necessary for less frequent repeats.
Is application-level caching safe for all Claude use cases? It's safe for deterministic or near-deterministic tasks like classification, extraction, and FAQ-style answers. Avoid it for conversational, personalized, or time-sensitive queries where a stale cached response would be misleading or wrong.