Stop Wasting Tokens: Optimize Claude Sonnet 5 Project Cache
- Every message in a Claude Project resends your entire Project Knowledge — creating massive hidden costs.
- Consolidating documents to clear the 1024-token cache minimum and adding prompt breakpoints drops repeat-read costs to ~10%.
- The 5-minute cache TTL means slow, spaced-out queries silently lose the discount; batching or 1-hour TTL preserves savings.
I'd uploaded a stack of internal API references and a codebase summary into a Claude Project weeks earlier, expecting it to just sit there quietly in the background. Instead, it turned out that "quietly sitting there" was exactly the problem. Every single message, no matter how short, was dragging that entire document set along with it.
If your Claude API bill looks wrong and you can't figure out why, there's a good chance the answer is sitting in how Project Knowledge actually gets sent to the model. Let's walk through what's happening under the hood, and how to fix it without ripping out your workflow.
Why Your Claude Project Bill Looks Wrong (The Hidden Resend Problem)
Here's the part that surprised me most: Claude Projects doesn't remember your uploaded documents the way a human assistant would. It re-sends them.
Every time you start a new conversation inside a Project, the platform automatically bundles your uploaded Project Knowledge and custom instructions into that conversation's context. This isn't a one-time setup step — it happens on every new chat, because the model itself has no persistent memory between separate conversations.
Think of it like mailing the same thick folder of paperwork to a client every single time you want to ask them one small question. You don't just send a sticky note — you re-stuff the whole envelope with every page, staple it shut, and mail it again. That's structurally what a Project does with your knowledge base on each new thread.
This matters more as your document set grows. Claude Sonnet 5 supports a standard 200K-token context window, which sounds huge — until you realize your input token baseline for every request rises in proportion to how much you've uploaded. If your Project Knowledge is pushing toward that ceiling, older documents or earlier turns in a conversation can even get trimmed or dropped automatically to make room.
So the first mental shift is this: Project Knowledge isn't "stored" for free. It's re-transmitted, and re-billed, constantly. The question isn't "how do I stop this" — you can't, structurally — it's "how do I make each resend as cheap as possible." That's exactly what prompt caching is built for.
The Cache Discount You're Probably Missing (1024-Token Rule)
Anthropic's prompt caching feature exists precisely to solve the resend problem, but most teams either don't turn it on correctly, or don't structure their documents to actually qualify for it.
Here's the pricing mechanic. When you write a new block of content into the cache, that's called a cache write, and it costs roughly 1.25x the normal input token price for a 5-minute cache, or about 2x if you opt into the extended 1-hour cache. When you reuse that same cached block on a later call, it's a cache read, and it costs only about 10% (0.1x) of the standard input rate.
That gap is enormous once you run the numbers on a real Project.
If your team asks 20 follow-up questions in a session, uncached, you're paying full price 20 times over. With caching working correctly, you pay the slightly elevated write cost once, then roughly a tenth of the price for the next 19 reads.
But there's a catch that trips up a lot of people: caching only kicks in once a prompt block hits a minimum token size. For Sonnet and Opus-tier models, that threshold is 1024 tokens. For Haiku-tier models, it's 2048 tokens. Anything smaller than that simply isn't eligible for caching — it gets billed as regular input every time, no discount at all.
This is basically a restaurant's minimum party size for a reservation. If the policy is "tables of 6 or more only," and you show up with 3 people, you don't get the group rate — you're treated as a regular walk-in, full price, no exceptions. A short, thin instructions file uploaded to your Project behaves the same way: too small to qualify, so it never gets the cache discount.
The fix is straightforward. Instead of uploading five tiny fragments of documentation separately, merge them into a single consolidated file that clears the 1024-token floor. A short "coding style guide" that's 200 tokens on its own is dead weight for caching purposes — combined with your API reference and architecture notes into one document, it easily clears the threshold and becomes cache-eligible.
Structuring Your Project Prompt for Maximum Cache Hits
Getting past the token minimum is only half the job. The other half is where you place things in the prompt, because caching is order-sensitive.
Anthropic's API lets you mark up to 4 cache breakpoints in a single request using cache_control. The rule of thumb is simple: put the stuff that never changes at the top, and put the stuff that changes every message at the bottom.
I like to compare this to building construction. You pour the foundation and erect the structural frame once — that's expensive and time-consuming, but you don't redo it every day. After that, you're just swapping out interior fixtures, paint, and furniture on top of a foundation that's already solid. Your system prompt and stable reference docs are the foundation. The user's current question is the interior decorating that changes constantly.
Here's what that looks like in an actual API request:
import anthropic
client = anthropic.Anthropic()
# ── STRUCTURE OVERVIEW ────────────────────────────────
# Breakpoint 1: System instructions -> almost never changes
# Breakpoint 2: Large reference doc(s) -> rarely changes
# Breakpoint 3: Project-specific guidelines-> changes occasionally
# (No breakpoint needed on the final user turn -- it always changes)
# ───────────────────────────────────────────────────────
response = client.messages.create(
model="claude-5-sonnet-20260301",
max_tokens=1024,
system=[
{
"type": "text",
"text": "You are a senior backend engineer assistant. "
"Follow the team's internal coding conventions strictly.",
# This block rarely changes across sessions,
# so we mark it as our first cache breakpoint.
"cache_control": {"type": "ephemeral"}
}
],
messages=[
{
"role": "user",
"content": [
{
"type": "text",
# This is the bulky, stable reference material.
# Merged so it clears the 1024-token minimum.
"text": "<full API reference + codebase summary here>",
"cache_control": {"type": "ephemeral"}
},
{
"type": "text",
# This changes with every single question,
# so it gets NO cache_control tag at all.
"text": "Why is my /users/:id endpoint returning a 500?"
}
]
}
]
)
print(response.content)
Notice that the actual user question at the bottom carries no cache_control tag — it's expected to change every time, so caching it would be pointless. The stable foundation above it, though, gets tagged and reused across dozens of follow-up turns at the discounted read rate.
A quick gut check before you ship this: if your reference block keeps changing slightly between requests (even a single character), the cache misses and you're back to paying the write price. Keep genuinely stable material — style guides, architecture docs, static API specs — separate from anything that gets edited day to day.
The 5-Minute Trap (When Prompt Caching Isn't Saving Money)
This is the part that actually caused our surprise bill, and it's a pattern I've now seen described as "claude prompt caching not saving money" in a few developer threads.
The default cache lifetime is 5 minutes. If nobody reuses that cached block within that window, it expires, and the next request pays the full cache-write price again — as if caching never happened at all.
Picture a team standup where someone asks Claude a question, then the conversation gets interrupted by an actual meeting for 15 minutes. When they come back and ask a follow-up, the cache has already expired. That follow-up gets billed as a fresh cache write, not a cheap cache read. If your team's usage pattern is bursty — a question here, a long gap, then another question — you will keep hitting this "claude cache write cost higher than expected" scenario without realizing it, because on paper caching is "on," it's just never actually landing a hit.
There are two practical fixes:
- Batch your questions. If you know you'll need to ask Claude five things about the same document, ask them back-to-back within the same few minutes instead of spacing them out across the day.
- Use the extended 1-hour TTL for genuinely long-running work. It costs more upfront on the write (roughly 2x base rate instead of 1.25x), but if your team touches the same Project across a multi-hour work session, that upfront cost is repaid many times over by avoiding repeated 5-minute expirations.
That third row matters too. If your team only pings a Project once or twice a day with big gaps in between, no TTL setting will save you — the real fix is trimming what's uploaded in the first place, so even an uncached resend is smaller and cheaper.
The high bill wasn't a bug. It was Claude Projects doing exactly what it's designed to do — resending your full knowledge base on every turn — combined with caching that wasn't actually configured to catch it.
Three things fixed it for us: merging small documents so they clear the 1024-token cache floor, restructuring the prompt so stable content sits at the top with proper cache_control breakpoints, and matching our TTL choice to how our team actually works. Check your usage metrics today and stop wasting tokens!
댓글
댓글 쓰기