← Blog

How to Cache Claude API Responses Efficiently

2026-10-28 · 5 min read · SubToAPI Team

Caching Claude API responses efficiently means combining two distinct layers: Anthropic's native prompt caching, which reduces the cost of repeated input tokens, and application-level response caching, which skips the API call entirely when you already have the answer. Most teams only implement one of these and leave money and latency on the table.

The short version: cache the static parts of your prompt (system instructions, long context, tool definitions) with cache_control, and cache full responses on your own infrastructure keyed by a hash of the normalized request. Below is how to do both correctly, including the mistakes that quietly break caching in production.

Why caching matters for cost and latency

Every uncached call to Claude pays full price for every input token, and every response takes a full round trip through the model. If your app sends the same system prompt, the same document context, or the same user question repeatedly, you're paying for compute you've already paid for once. For high-traffic endpoints — chatbots, support tools, search assistants — this adds up fast and directly affects your API bill and p95 latency.

Layer 1: Prompt caching

Claude supports prompt caching through the cache_control field on content blocks. When you mark a block as cacheable, Anthropic stores the processed version of that prefix for a short window (typically five minutes, refreshed on each hit), so subsequent requests that reuse the same prefix skip reprocessing it.

This is most useful for:

{
  "model": "claude-opus-4",
  "system": [
    {
      "type": "text",
      "text": "You are a support assistant. Here is the full product manual: ...",
      "cache_control": { "type": "ephemeral" }
    }
  ],
  "messages": [
    { "role": "user", "content": "How do I reset my password?" }
  ]
}

The key rule: the cached prefix must be byte-identical across requests. Reordering fields, changing whitespace, or injecting a timestamp into the system prompt invalidates the cache silently — you'll still get a response, just at full price. Keep variable content (user messages, timestamps, session IDs) out of the cached block and append it afterward.

Layer 2: Application-level response caching

Prompt caching reduces cost per call. Response caching eliminates the call entirely. If a user asks the exact same question twice, or your app regenerates the same summary for the same document, there's no reason to hit Claude again.

The pattern is simple: hash a normalized representation of the request, check a store (Redis, a KV store, even an in-memory LRU for low-traffic cases) before calling the API, and write the response back after a successful call.

import crypto from "crypto";
import Redis from "ioredis";

const redis = new Redis(process.env.REDIS_URL);

function cacheKey(model, systemPrompt, messages) {
  const normalized = JSON.stringify({ model, systemPrompt, messages });
  return "claude:" + crypto.createHash("sha256").update(normalized).digest("hex");
}

async function getCachedOrFetch(model, systemPrompt, messages, callClaude) {
  const key = cacheKey(model, systemPrompt, messages);
  const cached = await redis.get(key);
  if (cached) return JSON.parse(cached);

  const response = await callClaude(model, systemPrompt, messages);
  await redis.set(key, JSON.stringify(response), "EX", 3600); // 1 hour TTL
  return response;
}

A few details that matter more than they look:

Deduplicating concurrent requests

A cache miss under load can cause the classic "thundering herd" problem: ten identical requests arrive at once, all miss the cache, and all hit the API simultaneously. Guard against this with an in-flight request map so only one actual call happens per unique key:

const inFlight = new Map();

async function dedupedFetch(key, fn) {
  if (inFlight.has(key)) return inFlight.get(key);
  const promise = fn().finally(() => inFlight.delete(key));
  inFlight.set(key, promise);
  return promise;
}

Caching with streaming responses

Streaming complicates caching because you don't have a full response object until the stream closes. The usual approach is to buffer the stream server-side, store the complete assembled response once it finishes, and still stream it to the end user token by token on the first request. On a cache hit, you can replay the stored response as a synthetic stream (chunked by whitespace or sentence) so the client-side experience stays consistent. If you're building this through SubToAPI, the streaming endpoints documented at /docs/streaming give you clean SSE output you can buffer and cache the same way.

Setting sane TTLs and invalidation rules

Tag cache entries with a version string tied to your prompt template. When you edit the system prompt, bump the version — old cache entries become unreachable automatically instead of serving stale instructions under a new prompt.

Where SubToAPI fits

If you're exposing Claude to your own app or team through a REST layer, the usage metadata returned on every request (see /docs/messages) makes it straightforward to measure how much a caching layer is actually saving — compare token counts before and after you turn it on. Getting an API key and testing this against your own traffic takes a few minutes at /signup, and the full request/response reference is in /docs if you want to see exactly what fields are available to build your cache keys from.

Questions

Does prompt caching and application-level caching conflict with each other? No — they operate at different layers. Prompt caching reduces the cost of a call that still happens; response caching skips the call. Use both together: cache static prefixes with cache_control, and cache full responses for deterministic, repeatable queries.

How long should I cache Claude API responses? It depends on how fast the underlying answer goes stale. Static content (documentation, policies) can be cached for hours or days; user-specific but repeatable queries are usually safe for minutes to an hour. Anything involving live or personalized data in real time shouldn't be cached at all.

Can I cache responses that use a high temperature setting? Not reliably. High-temperature requests are intentionally non-deterministic, so caching them means serving one random output as if it were canonical. Reserve response caching for low-temperature, deterministic tasks like classification, extraction, or FAQ-style answers.

Turn your Claude access into an HTTPS API

SubToAPI gives you application API keys, streaming, tool use and usage insights on top of your existing Claude access — set up in minutes.

Start free  Read the quickstart →