Latest
Claude prompt caching in TypeScript: cache writes, cache reads and the cost difference on Sonnet 5.5
AI Tools for Developers

Claude Prompt Caching in TypeScript (With Cost Math)

6 min read
Quick Answer

Prompt caching in five lines

  • 1
    What it does → stores the start of your prompt (tools, system, earlier messages) so repeat requests reuse it at a fraction of the input price.
  • 2
    How → add cache_control: { type: "ephemeral" } to the last block you want cached, or once at the top level for automatic caching.
  • 3
    Price on Sonnet 5.5 → cache writes cost 1.25x base input (2x for the 1-hour cache); cache hits cost 5% of base input.

If your app sends the same large context to Claude again and again (a long system prompt, a product catalogue, a codebase summary, a document the user is chatting about), you're paying full input price for the same tokens every time. Prompt caching fixes that. The first request writes the shared prefix to a cache. Later requests read it back for a fraction of the cost and with lower latency. This guide shows a complete TypeScript example, how to confirm caching is working, and the exact cost difference on current models, plus the mistakes that quietly turn caching off.

How Prompt Caching Works

Claude caches a prefix of your request, in a fixed order: tools first, then the system prompt, then messages. You mark where the cached prefix ends with cache_control. Everything up to and including that block is cached; everything after it is processed normally.

A later request gets a cache hit only if its prefix is identical up to that point. Change one character in the system prompt and the system prompt and everything after it miss the cache.

  • Lifetime: 5 minutes by default, refreshed for free every time the cache is read. A 1-hour option costs more to write.
  • Minimum size: 512 tokens on Claude Opus 5.5, Sonnet 5.5, Haiku 5.5 and Fable 5.1. Shorter prefixes are silently not cached, with no error.
  • Breakpoints: up to 4 per request, so you can cache tools, system and conversation history as separate layers.
  • Automatic mode: a single top-level cache_control field moves the breakpoint forward to the last cacheable block as a conversation grows.

What It Costs: Sonnet 5.5 Example

Cache writes cost more than normal input, and cache reads cost much less. On Claude Opus 5.5 and Sonnet 5.5, a cache hit costs 5% of the base input price (10% on most other models). Per million tokens:

Now a concrete case: a 50,000-token document that a user asks 20 questions about within a few minutes, on Sonnet 5.5. Ignoring the short questions and the answers, which cost the same either way:

Without caching: 20 × 50,000 tokens × $2 per million = $2.00.

With a 5-minute cache: one write (50,000 × $2.50 per million = $0.125) plus 19 reads (19 × 50,000 × $0.10 per million = $0.095) = $0.22. That's about 89% less, and every request after the first also starts faster.

Comparison Data
modelbase inputfive min writeone hour writecache hit
Claude Sonnet 5.5$2$2.50$4$0.10
Claude Opus 5.5$4$5$8$0.20
Claude Haiku 5.5 (≤100K)$0.10$0.125$0.20$0.01

A Complete TypeScript Example

This example caches a large reference document in the system prompt and asks a question about it. The second call within five minutes reads the document from cache.

typescript

Cache a large document in the system prompt and log what each call wrote and read.

import Anthropic from "@anthropic-ai/sdk";
import { readFile } from "node:fs/promises";

const client = new Anthropic();
const handbook = await readFile("./docs/handbook.md", "utf8"); // tens of thousands of tokens

async function ask(question: string) {
  const res = await client.messages.create({
    model: "claude-sonnet-5-5",
    max_tokens: 1024,
    system: [
      { type: "text", text: "Answer only from the handbook below. Quote section titles." },
      {
        type: "text",
        text: handbook,
        cache_control: { type: "ephemeral" }, // cache everything up to here
      },
    ],
    messages: [{ role: "user", content: question }],
  });

  const u = res.usage;
  console.log({
    wrote: u.cache_creation_input_tokens, // > 0 on the first call
    read: u.cache_read_input_tokens,      // > 0 on later calls
    uncached: u.input_tokens,             // tokens after the breakpoint
  });
  return res;
}

await ask("What is the leave policy for contractors?");
await ask("Who approves expense claims over ₹50,000?");
  • Put stable content first and changing content last. The question goes in messages, after the cached system prompt.
  • Total input for billing is cache_read_input_tokens + cache_creation_input_tokens + input_tokens.
  • For a 1-hour cache, use cache_control: { type: "ephemeral", ttl: "1h" }. Use it when requests for the same prefix come in more than five minutes apart.

Caching a Growing Conversation

In a chat, the useful prefix keeps growing. Automatic caching handles this: put one cache_control at the top level of the request and the API places the breakpoint on the last cacheable block, moving it forward each turn. Each turn then reads the earlier history from cache and only writes the new part.

typescript

Automatic caching for a multi-turn chat: one top-level field, no per-block bookkeeping.

const res = await client.messages.create({
  model: "claude-sonnet-5-5",
  max_tokens: 1024,
  cache_control: { type: "ephemeral" }, // automatic breakpoint placement
  system: SYSTEM_PROMPT,
  messages: history, // grows every turn
});

Mistakes That Silently Break Caching

Caching fails quietly: the request still succeeds and you just pay full price. If cache_read_input_tokens stays at zero, check these first:

  • A timestamp, request ID or user name in the system prompt. Anything that changes per request breaks the prefix. Move dynamic values into the user message.
  • Changing tool definitions between requests. Tools come first in the cache order, so editing a tool description invalidates everything after it.
  • Toggling thinking or effort settings. Changing them invalidates the cached message blocks.
  • Adding or removing images anywhere in the prompt, or changing tool_choice.
  • A prefix under the 512-token minimum, which is never cached.
  • Requests more than five minutes apart with the default TTL. Use the 1-hour cache or accept the misses.

Frequently Asked Questions

Cache hits cost 5% of the base input price on Claude Opus 5.5 and Sonnet 5.5, and 10% on most other models. A large prefix reused many times can cut input costs by about 90%, while the first write costs 1.25x (5-minute cache) or 2x (1-hour cache) the base price.
Five minutes by default, refreshed at no cost each time the cached content is used. A 1-hour duration is available at a higher write price.
512 tokens on current models including Claude Opus 5.5, Sonnet 5.5, Haiku 5.5 and Fable 5.1. Older models need more, for example 4,096 tokens on Haiku 4.5. Shorter prefixes aren't cached and no error is returned.
Check the usage object in the response. cache_creation_input_tokens is above zero when the cache is written; cache_read_input_tokens is above zero on a cache hit.
Yes, and the discounts stack. Because batches can take longer than five minutes, Anthropic suggests the 1-hour cache duration for better hit rates in batches.

Strategic Summary

Final Thoughts

Prompt caching is one of the cheapest optimisations you can make to a Claude app: a few lines of code and a stable prompt order. Put fixed content first, mark the end of it with cache_control, watch cache_read_input_tokens to confirm hits, and keep anything that changes per request out of the cached prefix. For background jobs, combine it with the Message Batches API, which halves the price again. For picking the model behind it, see Claude Haiku 5.5 for developers. --- Last reviewed: October 23, 2026. Behaviour and prices are from Anthropic's prompt caching and pricing documentation. No affiliation with Anthropic.

What hit rate are you seeing in production? Share your cache_read share of total input and I'll add real numbers to this guide.

Get in touch

Next Up

Continue your research