How Claude Code Prompt Caching Works and When It Expires
Problem
I typed “hey” in Claude Code. Just one word. Then I saw this:
Usage: 22% of monthly allocation consumedI stared at my screen in disbelief. How could a single word - three characters - burn through almost a quarter of my Pro subscription?
When I checked Reddit, I found I wasn’t alone. A user posted the exact same confusion, showing that even minimal messages were consuming massive chunks of their usage limits.
I needed to understand what was happening behind the scenes.
What I discovered
My first assumption was wrong. I thought Claude measured usage by “question difficulty” - simple questions should cost less, complex analysis should cost more.
But Claude doesn’t measure difficulty. It measures tokens - and every message re-sends the entire context window.
Here’s what actually gets sent when I type “hey”:
What Claude Code sends with every message:┌─────────────────────────────────────────────────────────────┐│ System prompts (CLAUDE.md, project rules) ~10K-50K tokens││ Tool definitions (every available tool) ~5K-20K tokens││ Conversation history (all previous messages) ~varies ││ My message: "hey" ~1 token │├─────────────────────────────────────────────────────────────┤│ Total per message: ~15K-70K+ │└─────────────────────────────────────────────────────────────┘The “hey” was just 1 token. But I was paying for everything else too.
How prompt caching saved me
This is where I learned about Anthropic’s prompt caching feature. The idea is simple: instead of reprocessing the same static content every time, cache it on Anthropic’s servers.
WITHOUT CACHING (every message):┌──────────────────┐│ System prompts │──→ Full token cost every time│ Tool definitions │──→ Full token cost every time│ My message │──→ Full token cost└──────────────────┘ Total: ~15K-70K tokens × 100% cost
WITH CACHING (warm session):┌──────────────────┐│ System prompts │──→ Cache READ (90% cheaper!)│ Tool definitions │──→ Cache READ (90% cheaper!)│ My message │──→ Full token cost└──────────────────┘ Total: ~15K-70K tokens × 10% cost (after initial cache)The first message in a session pays a premium to write the cache (1.25x normal cost). But every subsequent message reads from cache at 90% discount.
The TTL trap: When cache expires
Here’s where I made my second mistake. I assumed the cache would last for my entire work session.
Wrong.
The cache has a time-to-live (TTL) that depends on my subscription tier:
┌─────────────────┬────────────────┬─────────────────────────┐│ Plan │ Cache TTL │ What this means │├─────────────────┼────────────────┼─────────────────────────┤│ Pro ($20/mo) │ 5 minutes │ Cache dies quickly ││ Max ($200/mo) │ 1 hour │ Cache survives longer │└─────────────────┴────────────────┴─────────────────────────┘As a Pro user, if I took a 6-minute break between messages, my cache expired. The next message had to rebuild everything from scratch - paying that 1.25x cache-write premium again.
This explained my 22% usage for “hey”:
- My session had been idle for more than 5 minutes
- Cache expired
- Full context reprocessing at full cost
- Plus a new cache-write fee
How cache control works
When I dug into the API documentation, I found that caching isn’t automatic. You have to explicitly mark content for caching:
from anthropic import Anthropic
client = Anthropic()
response = client.messages.create( model="claude-sonnet-4-5", max_tokens=1024, system=[ { "type": "text", "text": "You are an AI coding assistant...", }, { "type": "text", "text": "<large_codebase_context>", # Static content "cache_control": {"type": "ephemeral"} # Mark for caching } ], messages=[{"role": "user", "content": "Explain this function"}],)The cache_control parameter tells Anthropic: “Cache everything up to this point.”
Understanding the API response
When I examined the response from cached requests, I found useful metrics:
{ "usage": { "input_tokens": 100, "cache_read_input_tokens": 10000, "cache_creation_input_tokens": 500, "output_tokens": 500, "cache_creation": { "ephemeral_5m_input_tokens": 456, "ephemeral_1h_input_tokens": 100 } }}The key fields:
cache_read_input_tokens: Tokens read from cache (90% cheaper!)cache_creation_input_tokens: One-time cache write cost (1.25x)ephemeral_5m_input_tokens: Standard 5-minute cacheephemeral_1h_input_tokens: Extended 1-hour cache
The TTL refresh mechanism
Here’s a detail I initially misunderstood. The cache TTL doesn’t start once and then expire after X minutes.
It refreshes each time cached content is accessed.
Timeline (Pro plan with 5-minute TTL):
Message 1 at 0:00 → Cache created (expires at 0:05)Message 2 at 0:03 → Cache READ, TTL refreshes (now expires at 0:08)Message 3 at 0:06 → Cache READ, TTL refreshes (now expires at 0:11)[Idle 6 minutes]Message 4 at 0:17 → Cache EXPIRED, full cost againThis means active sessions maintain their cache indefinitely. The problem is idle time - if I step away for a coffee break longer than my TTL, I pay the penalty.
When cache breaks
I also discovered that certain changes invalidate the cache entirely:
Cache breaks when:├── tool_choice parameter changes├── Images are added or removed anywhere in prompt├── Cached sections aren't identical across calls└── More than 20 content blocks exist before cache checkpointThis means I can’t modify tool configurations mid-session without paying the cache-rebuild cost.
My optimization strategy
After understanding all this, I changed how I use Claude Code:
-
Stay active in sessions - The TTL refreshes with each use, so continuous work keeps the cache warm
-
Batch related tasks - Instead of spreading work across multiple sessions, I complete related tasks in one sitting
-
Upgrade for long sessions - If I need to work on complex projects over hours, the Max plan’s 1-hour TTL is worth it
-
Monitor cache efficiency - I check the API response to see cache hit rates:
def check_cache_efficiency(response): total_input = response.usage.input_tokens cache_read = response.usage.cache_read_input_tokens or 0 cache_created = response.usage.cache_creation_input_tokens or 0
if cache_read > 0: efficiency = cache_read / (total_input + cache_read) * 100 print(f"Cache hit! {efficiency:.1f}% from cache (90% cheaper)") elif cache_created > 0: print(f"Cache created: {cache_created} tokens (one-time 1.25x cost)") else: print("No caching in effect - full cost")
return responseWhy this design makes sense
Once I understood the economics, the pricing model became logical:
- Computing costs correlate with tokens processed, not question complexity
- Processing “hey” with 50K tokens of context costs similar to processing “analyze this codebase” with 50K tokens
- The GPU doesn’t care about question difficulty - it performs the same matrix operations
- System prompts and tool definitions ensure quality and safety
The 90% cost reduction from caching is Anthropic passing infrastructure savings to users who maintain warm sessions.
Summary
I discovered that saying “hey” cost me 22% of my usage because:
- Every message re-sends the entire context window (system prompts, tools, history)
- My cache had expired due to the 5-minute TTL on Pro plans
- The full context had to be reprocessed at full cost
Key takeaways:
- Prompt caching reduces costs by up to 90% for cached content
- Pro plan TTL is 5 minutes; Max plan TTL is 1 hour
- Cache TTL refreshes on each use - active sessions stay warm
- Idle time longer than your TTL means full reprocessing cost
- Cache writes cost 1.25x, cache reads cost ~90% less
The “hey” message wasn’t expensive because of the word - it was expensive because my session context was cold and had to be rebuilt.
Final Words + More Resources
My intention with this article was to help others share my knowledge and experience. If you want to contact me, you can contact by email: Email me
Here are also the most important links from this article along with some further resources that will help you in this scope:
- 👨💻 Anthropic Prompt Caching Documentation
- 👨💻 Reddit: Saying 'hey' cost me 22% of my usage limits
- 👨💻 Claude API Pricing
Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!
Comments