●PRICING — September 1 was the scheduled date for the Sonnet 5 price increase, and it did not happen. The introductory $2/$10 per MTok now stands as the regular price●PARTNER — Salesforce and Anthropic announced Claudeforce, an expanded partnership. The Salesforce in Claude plugin ships with 37 prebuilt sales skills, from meeting prep to pipeline management●TRUST — Claudeforce serves Claude through Amazon Bedrock inside the Salesforce Trust Boundary, so data and inference never leave the security perimeter — an answer aimed squarely at regulated industries●BETA — Salesforce in Claude is with select pilot customers for now, with an open beta expected during September●LIMITS — The 50% weekly-limit boost runs through September 13. From September 14 the permanent level is 25% above the pre-promotion baseline, roughly a 17% cut from today●RELEASE — Claude Code has shipped nothing since v2.1.251 on August 28. Against a pace of one release every 0.8 days, a four-day gap is among the longest yet●PRICING — September 1 was the scheduled date for the Sonnet 5 price increase, and it did not happen. The introductory $2/$10 per MTok now stands as the regular price●PARTNER — Salesforce and Anthropic announced Claudeforce, an expanded partnership. The Salesforce in Claude plugin ships with 37 prebuilt sales skills, from meeting prep to pipeline management●TRUST — Claudeforce serves Claude through Amazon Bedrock inside the Salesforce Trust Boundary, so data and inference never leave the security perimeter — an answer aimed squarely at regulated industries●BETA — Salesforce in Claude is with select pilot customers for now, with an open beta expected during September●LIMITS — The 50% weekly-limit boost runs through September 13. From September 14 the permanent level is 25% above the pre-promotion baseline, roughly a 17% cut from today●RELEASE — Claude Code has shipped nothing since v2.1.251 on August 28. Against a pace of one release every 0.8 days, a four-day gap is among the longest yet
Diagnosing Claude API Prompt Cache Misses — How to Read the usage Field
If your Claude API prompt cache isn't reducing your bill, the usage field is where to start. This guide walks through the five most common reasons cache_read_input_tokens stays at zero and how to fix each one.
"I added cache_control last week, but my invoice didn't shrink at all" — this is the single most common message I get from people who just tried Claude's prompt caching. I went through the same thing the first time I shipped it: three days of zero hits before I noticed the cache hadn't been working at all.
The good news is that the failure modes are surprisingly limited, and every diagnosis starts in the same place: the usage object that the API returns on every response. This guide walks through the five misses I run into most often, in the order I check them.
Start with the usage field
Every Claude API response includes a usage object. When you have prompt caching enabled, two extra fields appear there.
input_tokens: tokens that bypass the cache and get re-read every request
cache_creation_input_tokens: tokens written to the cache on this request (billed at 1.25× standard for the 5-minute TTL, 2× for the 1-hour TTL — but only on first write)
cache_read_input_tokens: tokens served from the cache (billed at 0.1× standard)
A working cache means: from the second request onward, cache_read_input_tokens is positive and cache_creation_input_tokens drops toward zero. If you keep firing identical prompts and these numbers never shift, something is wrong.
Cause #1: The prefix is below the minimum token count
Claude enforces a minimum number of tokens for a prefix to be eligible for caching. As of April 2026:
Claude Sonnet 4.6 / Claude Opus 4.6: 1,024 tokens
Claude Haiku 4.5 family: 2,048 tokens
If your cached prefix is shorter than that, the server quietly skips caching. cache_creation_input_tokens stays at zero, input_tokens keeps climbing, and you wonder why nothing is happening.
This is the trap I fell into first — a 600-token system prompt with cache_control attached and lots of confused log diving. Run your prefix through the token counter before assuming the cache is broken. If you're under the threshold, your next move isn't caching; it's compressing the prompt itself.
✦
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 five-cause diagnostic order for reading cache_read_input_tokens in the usage field
✦Real numbers from a 1,000-image batch — input_tokens from 3,210 to 12, TTFT from 1.42s to 0.83s
✦A decision table for choosing the 5-minute vs 1-hour TTL and holding a 10-to-1 read-to-write ratio
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.
Prompt caching treats every block up to and including the one with cache_control as a single cache key. The cache_control marker has to sit on the last block of the static prefix.
That layout pulls today's date into the cache key, so the cache rotates every midnight and you never get a hit. Move dynamic content after the cached block.
# ✅ Correct: dynamic block comes after the cached prefixsystem=[ {"type": "text", "text": SYSTEM_INSTRUCTION}, {"type": "text", "text": "Output format: ...", "cache_control": {"type": "ephemeral"}}, {"type": "text", "text": f"Today is {today}"}, # outside the cache]
The same rule applies to tools. Putting cache_control on the last entry of your tools array caches every tool definition above it as one prefix.
Cause #3: The TTL has expired
The default cache TTL is 5 minutes. If five minutes elapse without a request that hits the same prefix, the cache is gone and the next request pays the write cost again. For low-frequency batch jobs or chatbots where users idle for ten minutes between turns, the cache rarely survives long enough to pay back its write premium.
The 1-hour TTL that became generally available in 2026 is the right tool for these workloads. Writes cost 2× standard instead of 1.25×, but the longer survival makes it cheaper overall when access is sparse.
Log the ratio of cache_read_input_tokens to cache_creation_input_tokens over a full day before and after the change so you have evidence the switch was worth the higher write premium.
Cause #4: A dynamic value sneaks into the cached prefix
This is the bug I keep making. A "static" prefix turns out to contain something that quietly drifts on every request.
Patterns I've seen in production code:
A timestamp like Current time: 2026-04-28T09:00:34Z injected at the top of the system prompt
A user ID embedded in the system prompt to "remind" Claude who it's talking to
A per-session tracking UUID baked into the cached prefix
Inconsistent line endings (\n vs. \r\n) across environments
The fastest way to catch these is to hash the cached portion before you send it.
import hashlibdef cache_key_hash(text: str) -> str: return hashlib.sha256(text.encode("utf-8")).hexdigest()[:16]prefix = "".join(b["text"] for b in system_blocks_until_cache_control)print(f"cache prefix hash: {cache_key_hash(prefix)}")
If the hash changes between requests that ought to be identical, the cause is in your prefix construction. Diff two consecutive prefixes side by side; the offending field tends to be obvious. This single trick saved me half a day chasing a phantom Anthropic-side issue when the real culprit was a request timestamp my own code was adding.
Cause #5: Tool ordering isn't deterministic
If you cache a tools array and still see misses, look at the ordering. Claude compares cache keys as raw bytes, so the same set of tools in a different order is a different cache.
When you build the array from a Python dict whose insertion order depends on runtime conditions, the array shuffles between requests and the cache rotates with it. Pin the ordering with an explicit list.
TOOL_ORDER = ["search_docs", "execute_sql", "send_email"]def build_tools(enabled: set[str]) -> list[dict]: # Always emit tools in a fixed order return [TOOL_DEFINITIONS[name] for name in TOOL_ORDER if name in enabled]
If different users genuinely have different tool sets, the cache hit rate has a hard ceiling no matter what you do. The realistic design is to split tools into "common" and "user-specific" blocks, attach cache_control only to the common block, and append the per-user tools after it.
A diagnostic script: three calls in a row
Here's the small script I keep around for triage. It fires the same prompt three times and prints how usage evolves.
import anthropicfrom anthropic.types import TextBlockParamclient = anthropic.Anthropic()SYSTEM_PROMPT = open("system_prompt.txt").read() # must be >= 1,024 tokensdef diagnose(): for i in range(3): resp = client.messages.create( model="claude-sonnet-4-6", max_tokens=64, system=[ TextBlockParam( type="text", text=SYSTEM_PROMPT, cache_control={"type": "ephemeral"}, ) ], messages=[{"role": "user", "content": f"test {i}"}], ) u = resp.usage print( f"req {i}: input={u.input_tokens} " f"create={u.cache_creation_input_tokens} " f"read={u.cache_read_input_tokens}" )diagnose()
If read becomes positive on req 1, caching is working. If it's still zero, walk through causes #1 to #5 in order — they cover almost every miss I've seen in real codebases.
Once your cache is firing reliably, you'll see three patterns in your dashboards.
First, the per-request input token cost drops to roughly one-tenth of its old value for any prefix you cached, with brief spikes back to full cost every time the TTL window rolls over and a new write happens. If you're charting cost in a grid, the daily shape becomes a flat line with a few small bumps rather than the old constant ceiling.
Second, the ratio of cache_read_input_tokens to cache_creation_input_tokens tells you whether your TTL choice is right for the traffic shape. Anything above 10:1 read-to-write is a healthy steady state. If the ratio sits below 3:1, your write premium is eating most of the savings — that's usually a TTL problem (cause #3) or a prefix that drifts more than you realized (cause #4).
Third, latency improves measurably on cached prefixes — usually a 30 to 50 percent drop in time-to-first-token for the same prompt. This is the side benefit nobody talks about: prompt caching isn't only a cost feature, it's a UX feature for any product where users wait on the first chunk.
Wire those three signals into the same dashboard you already use for error rates and rate-limit headroom. Once the team can see them, regressions get caught the same week they ship rather than the next billing cycle.
A measured example: an image-metadata generation queue
The backend of an app I run as an indie developer at Dolice has a queue that generates image metadata through the Claude API. Every item ships the same ~3,200-token system prompt (classification rules and an output-format definition), and before caching, input tokens alone dominated the processing cost.
Here is the usage data aggregated over the same 1,000-image batch, before and after enabling the cache.
Metric
Before
After (5-min TTL)
Avg input_tokens / image
3,210
12
Avg cache_read_input_tokens / image
0
3,198
Effective input unit price
1.00x
~0.12x
Time to First Token (median)
1.42s
0.83s
The detail that is easy to miss is the queue's submission interval. At first, each item paused for tens of seconds of external I/O, so the 5-minute TTL just barely survived. But in the overnight window, where the gap between items stretched close to ten minutes, cache_creation_input_tokens spiked — cause #3, exactly. Once I batched submissions into bursts and kept the interval under five minutes, the cache_read ratio stayed above 10-to-1 across every hour of the day.
The single number worth watching is whether read-to-write stays above 10-to-1. Looking back at data from a period when I assumed the cache "must be working," I found the ratio sitting around 4-to-1, with the write premium quietly eating most of the savings. Emitting the metric is what keeps you from fooling yourself.
Choosing between the two TTLs
Whether to use the 5-minute or 1-hour TTL comes down to access interval and burstiness. Here's the rule of thumb I actually use.
Workload shape
Recommended TTL
Why
Chat UI, continuous turns (seconds to a minute apart)
5 minutes
Lowest write premium (1.25x); hits never lapse
Bursty processing every few minutes
5 min + burst batching
Keep submissions within 5 min and you never need the 1-hour TTL
Low-frequency jobs tens of minutes apart
1 hour
2x write cost, but longer survival is cheaper overall
Internal tools used a few times a day
1 hour + measure
If the ratio still sits below 3-to-1, consider not caching at all
When in doubt, log the read-to-write ratio for 24 hours on the default 5-minute TTL first, then switch only the workloads whose ratio splits badly across the day. Flipping everything to 1 hour uniformly just loses the extra write premium on your high-frequency traffic.
A checklist for the fastest diagnostic loop
When you spot a miss, work through these in order — one at a time, watching usage after each change.
Count the cached prefix's tokens and confirm it clears the model's minimum (1,024 for Sonnet, 2,048 for Haiku) — cause #1
Confirm cache_control sits on the last block of the static prefix, with dynamic blocks after it — cause #2
Check request timestamps in your logs to confirm the access interval doesn't exceed the TTL (5 min by default) — cause #3
Print a SHA-256 hash of the prefix on every request and confirm the value is stable — cause #4
Confirm the tools array is emitted in a fixed order — cause #5
Running through these five once has explained nearly every cache miss I've hit in production. Causes often compound, so if one fix doesn't move the numbers, move on to the next item.
What to do next
Drop the diagnostic script into one of your live services for two minutes and watch whether cache_read_input_tokens moves. If it doesn't, start at cause #1 and work down — that's the shortest path to a fix.
Caching isn't a feature you set and forget. It's a feature you instrument with usage, then iterate on until the numbers tell you it's working. Get that loop running once and your bill will start cooperating.
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.