CLAUDE LABJP
OPUS48 — The default model on Bedrock, Vertex, and AWS is now Claude Opus 4.8. Opus 4.8 and Haiku 4.5 are also in the Messages API, with prompt caching and extended thinkingSTREAM — A Claude Code stability and quality update landed: subagent text over stream-json, stronger permission and hook handling, and better background-agent reportingFASTEND — Fast mode for Claude Opus 4.7 is removed on July 24. After that, speed: 'fast' returns an error, so migrate to fast mode on Opus 4.8TEACH — Claude for Teachers is here, giving verified US K-12 educators free premium access, plus curriculum connections aligned to standards in all 50 states and new education connectorsFIX — The update fixes issues across Chrome, Windows, Bedrock, Vertex, hooks, and session recovery, and speeds up terminal renderingIPO — Bankers are reportedly lining up investor meetings for Anthropic ahead of a possible public listing as soon as OctoberOPUS48 — The default model on Bedrock, Vertex, and AWS is now Claude Opus 4.8. Opus 4.8 and Haiku 4.5 are also in the Messages API, with prompt caching and extended thinkingSTREAM — A Claude Code stability and quality update landed: subagent text over stream-json, stronger permission and hook handling, and better background-agent reportingFASTEND — Fast mode for Claude Opus 4.7 is removed on July 24. After that, speed: 'fast' returns an error, so migrate to fast mode on Opus 4.8TEACH — Claude for Teachers is here, giving verified US K-12 educators free premium access, plus curriculum connections aligned to standards in all 50 states and new education connectorsFIX — The update fixes issues across Chrome, Windows, Bedrock, Vertex, hooks, and session recovery, and speeds up terminal renderingIPO — Bankers are reportedly lining up investor meetings for Anthropic ahead of a possible public listing as soon as October
Articles/API & SDK
API & SDK/2026-07-19Advanced

When Prompt Caching Was On but the Bill Didn't Move — Field Notes on Instrumenting cache_read to Close the Hit-Rate Gap

You added cache_control but your Claude API bill barely changed. Before guessing at fixes, record cache_read and cache_creation on every request and split the hit-rate gap into three causes: non-deterministic prefixes, TTL expiry, and sub-threshold blocks.

prompt-caching13cache-hit-ratecost-optimization29observability21api39

Premium Article

I opened last month's invoice and paused. I had added cache_control to my system prompt three weeks earlier. I never checked the hit rate — I just assumed "that's a 90% discount now." But the input-token charges had barely moved.

That was the moment it finally landed: caching isn't a binary of on or off. It was on. It just wasn't being read. And that gap only shows up in the invoice — you cannot see it by staring at the code.

These are my notes from raising the cache hit rate on a summarization batch I run as an indie developer, from the low 30s to nearly 90%. This isn't a tour of clever optimization tricks. Measure first, name the leak, then move one breakpoint at a time. It's a record of that unglamorous back-and-forth.

"Enabled" and "working" are two different states

Prompt caching stores the prefix — the leading portion of system, tools, and messages — on the server. When a later request reuses the same prefix, that portion is billed at roughly a tenth of the normal input rate.

The catch is that writing cache_control guarantees nothing at the moment you write it. Whether the cache was actually read only appears in the response usage. Not looking there was the whole shape of my three-week blind spot.

usage fieldMeaningBilling (relative to normal input)
cache_creation_input_tokensTokens written to the cache (this call missed)~1.25x (5-minute cache)
cache_read_input_tokensTokens read from the cache (this call hit)~0.1x
input_tokensNormal, non-cached input1x

Reading it is simple. If cache_read_input_tokens comes back non-trivial on most calls, caching is working. If cache_creation_input_tokens keeps firing instead, the cache is being built and thrown away over and over. "Added but not working" is almost always this state.

Add one thin measurement layer first

Before I started guessing at causes, I inserted a thin layer that records usage on every request. Ahead of any optimization, I wanted to know where I actually stood, in numbers.

import logging
from dataclasses import dataclass, field
 
logger = logging.getLogger("cache")
 
@dataclass
class CacheProbe:
    """Minimal harness that logs cache_read / cache_creation per request."""
    requests: int = 0
    hits: int = 0            # count of calls where cache_read > 0
    rewrites: int = 0        # count of calls where cache_creation > 0
    read_tokens: int = 0
    write_tokens: int = 0
    last_prefix_fingerprint: str | None = field(default=None)
 
    def record(self, usage) -> None:
        self.requests += 1
        read = getattr(usage, "cache_read_input_tokens", 0) or 0
        write = getattr(usage, "cache_creation_input_tokens", 0) or 0
        self.read_tokens += read
        self.write_tokens += write
        if read > 0:
            self.hits += 1
        if write > 0:
            self.rewrites += 1
 
    @property
    def hit_rate(self) -> float:
        return (self.hits / self.requests * 100) if self.requests else 0.0
 
    @property
    def rewrite_rate(self) -> float:
        return (self.rewrites / self.requests * 100) if self.requests else 0.0
 
    def snapshot(self) -> str:
        return (
            f"req={self.requests} hit={self.hit_rate:.1f}% "
            f"rewrite={self.rewrite_rate:.1f}% "
            f"read_tok={self.read_tokens:,} write_tok={self.write_tokens:,}"
        )

It's a layer that just calls record(). I wanted to see one thing: is the rewrite rate stuck high? Measured on the summarization batch, hit rate was 34.8% and rewrite rate was 61.2%. More than half of all requests were rebuilding the cache. That put a number on why the bill wasn't dropping.

Why make rewrite rate the lead metric instead of hit rate? Because hit rate alone hides the cause. It tells you a call missed, but not whether the prefix shifted or whether the cache timed out. Seeing how often — and at what spacing — writes happen makes that split fall out quickly.

Thank you for reading this far.

Continue Reading

What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.

WHAT YOU'LL LEARN
A minimal harness that pulls cache_read and cache_creation from usage and logs hit rate and rewrite rate on every request
A measurement-first way to split a low hit rate into three distinct causes: non-deterministic prefix, TTL expiry, and sub-threshold blocks
How to walk one cache breakpoint backward at a time to locate the block that keeps re-triggering cache_creation
Secure payment via Stripe · Cancel anytime

Unlock This Article

Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.

or
Unlock all articles with Membership →
Share

Thank You for Reading

Claude Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • Copy-paste ready implementation code
  • New advanced guides published daily
  • $5/mo or $10 for lifetime access
View Membership →

Related Articles

API & SDK2026-06-23
When Claude API Prompt Caching Quietly Stops Hitting in Production — Field Notes on TTL and Measured Savings
Prompt caching works beautifully the day you ship it, then quietly stops hitting in production. The five things that break the prefix, how to choose between 5-minute and 1-hour TTL, and how to measure real savings from usage instead of guessing.
API & SDK2026-03-26
Claude API Cost Optimization Production Guide — Combining Batch API, Prompt Caching, and Adaptive Thinking for Up to 90% Savings
Learn practical implementation patterns to cut Claude API costs by up to 90%. Covers Batch API, Prompt Caching, and Adaptive Thinking strategies, plus production monitoring and budget management.
API & SDK2026-07-13
When Extended Thinking Flattened My Accuracy but Doubled the Bill — Field Notes on Measuring the Marginal Utility of Thinking Tokens
You pinned budget_tokens high 'to be safe,' accuracy barely moved, and the bill kept climbing. These field notes show how to ledger real thinking-token usage by p50/p95/hit-rate and measure how much accuracy each extra 1,000 thinking tokens actually buys.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links
See all →