Claude Code Cost: 4 Rules That Work, 4 That Don't
Claude Code cost comes down to two things: how much context gets re-sent on every turn, and which model reads it. I checked the rules I use to keep both small against my own usage numbers and the docs as they read on September 19, 2026.
Four hold up. Four do not, and one of those, .claudeignore, is a file I can’t find anywhere in the docs.
One note on the word cost. I’m on the $200 Max plan, so I never see a bill.
What I spend is session and weekly limits, and these rules are about fitting more work under them. The dollar figures below are list-price estimates that I use as a measuring stick.
If you are on the $20 plan instead, how to get the most out of a $20 Claude plan applies the same ideas to Pro.
Here is the scorecard before the detail.
| Rule | Verdict | Why |
|---|---|---|
opusplan |
Works | Sonnet types, Opus plans. Each switch re-reads the context uncached. |
/clear with a /wrap skill |
Works | Clearing costs nothing, and the notes keep continuity. |
| Haiku subagents | Works, with limits | The built-in Explore and Plan agents ignore the variable alone. |
| Disconnect unused MCP servers | Works, modestly | Their tool names still load: 2,659 tokens per request, measured. |
.claudeignore |
Not in the docs | The documented way is Read deny rules. |
| CLAUDE.md under 60 lines | Saves almost nothing | 1.8k tokens, and it is cached. |
CLAUDE_CODE_MAX_OUTPUT_TOKENS |
No effect | A ceiling, not a bill. |
effortLevel: "high" |
Doesn’t save | Already the default on most models. On Opus 5.5 it may cost more than the medium default. |
What actually drives Claude Code cost
I ran npx ccusage@latest over July to September 2026. It counted about 8 billion tokens, worth roughly $4,590 at list prices.
September alone, through the 19th, was 7.4 billion tokens and about $4,040. That is around twenty times the price of the plan.
ccusage prices at list rates and may not know the newest models, so read the dollars as estimates.
The shape of the numbers matters more than the total. Input outnumbers output 210 to 1. About 97 percent of that input is cache reads.
Using the standard multipliers, a cache read costs a tenth of a normal input token and a five-minute cache write costs 1.25 times.
My rough arithmetic says cache reads are about three quarters of my input cost and cache writes about a fifth. That is my math over ccusage output, not a published figure, and one-hour cache writes cost more than I assumed.
The docs explain why. In their words, Claude Code sends your full conversation with every request.
The cache makes the re-read cheap, not free. A session left open all day re-reads all of it for every one-line question.
So Claude Code cost is context size, times turns, times the price of the model reading it. Every rule below pushes on one of those three.
Three commands show where you stand. /usage shows the session’s prompt cache hit rate and, on a plan, which skills, subagents, plugins and MCP servers used your limits.
/context shows what is filling the window. ccusage shows the history.
My settings.json, minus the plugins
Here is the part of my settings.json that touches cost, with the plugins cut out.
{
"model": "opusplan",
"env": {
"CLAUDE_CODE_SUBAGENT_MODEL": "haiku"
},
"hooks": {
"SessionStart": [
{
"matcher": "clear",
"hooks": [
{
"type": "command",
"command": "cat \"$CLAUDE_PROJECT_DIR/SESSION_NOTES.md\" 2>/dev/null || true"
}
]
}
]
}
}
Three things in there matter. model picks opusplan.
env sends subagents to Haiku. The hook prints my session notes after every /clear.
I left out four keys that are in my real file: CLAUDE_CODE_MAX_OUTPUT_TOKENS, CLAUDE_AUTOCOMPACT_PCT_OVERRIDE, effortLevel and advisorModel. They are the second half of this post.
Rule 1: opusplan, because Sonnet can type
"model": "opusplan" runs Opus in plan mode and Sonnet for execution, as the model docs describe.
I’m a software engineer, and I don’t vibe code. I know what I want changed before I ask, so Sonnet is more than enough to type it.
Opus earns its price on the planning step, when the question is what to build. The docs agree with the split: Sonnet “handles most coding tasks well and costs less than Opus.”
Here is my hot take. A lot of Opus spend is a very expensive way to work out what you want. If you already know, you don’t need the big model for most of the job.
There is one catch, and the caching docs are upfront about it. Each model has its own cache.
Under opusplan, every plan-mode toggle is a model switch, and the next request reads the whole conversation with no cache hits.
So opusplan is cheapest when the context is small at the switch. That is one more reason to start each task from a clear.
Rule 2: /clear is the cheapest command you have
The docs put it plainly: “When you want a fresh start instead of continuity, /clear costs nothing.” Stale context is re-sent with every message.
In one long session, /context showed 254k of the 290.8k tokens in use was the conversation itself, or 87 percent. The cheapest tokens are the ones you never carry into the next task.
The problem is continuity. Clear, and the session forgets what it was doing. I fixed that with a skill and a hook.
The skill, /wrap, writes a short notes file before I clear. The hook prints that file into the new session. Context dies, but continuity doesn’t.
The hook is the SessionStart block above. matcher: "clear" fires it only after a clear, and it prints SESSION_NOTES.md from the project root.
I have watched it fire: after a clear, the notes are right there at the top of the new session. I keep the file out of git with a global gitignore.
The /wrap skill that makes /clear safe
This is the whole skill, as it sits on my machine.
---
name: wrap
description: Save session notes before clearing
disable-model-invocation: true
allowed-tools:
- Read
- Write
- Bash(git *)
- Bash(cat *)
- Bash(wc *)
---
Current notes:
!`cat SESSION_NOTES.md 2>/dev/null || echo "(none)"`
Git state:
!`git branch --show-current 2>/dev/null`
!`git log -1 --oneline 2>/dev/null`
!`git status --short 2>/dev/null | head -20`
Write SESSION_NOTES.md in the directory this session started in.
Replace it fully.
Do not create or edit any other file.
Rules:
- Max 40 lines, each under 120 characters. Run `wc -l` and trim if over.
- Merge with the current notes: keep decisions that still apply, drop
finished items. Keep any other unfinished task under its own heading.
- Mark each done item VERIFIED (you saw the output) or UNVERIFIED.
- Never write secrets, API keys, tokens, connection strings, or .env values.
- Name every file, agent, and model. Never write "it". No em dashes.
- If nothing is left open, write only: "No open task. Last finished: <one line>"
Headings: Task, Branch and state, Done, Left to do, Decisions, Rejected (and why)
Reply with one line only: the line count and "Ready to /clear".
Four choices in there earn their place.
disable-model-invocation: true makes it manual. The skills docs say a skill set this way has no description in context at all, so /wrap costs nothing until I type it. /context agrees: wrap is not in my skills list, which runs to 9.9k tokens.
The ! lines run before Claude sees the skill. The command output replaces the placeholder, so the current notes and the git state arrive pre-loaded.
One gotcha from the docs: a failing command aborts the whole skill. That is why the notes line ends in || echo "(none)".
allowed-tools limits it to reading, writing, git, cat and wc. It has no reason to touch anything else.
The 40-line cap and “replace fully” are cost controls. The notes get printed into every fresh session, so a file that keeps growing is a tax I pay after every clear.
The cap forces a merge: keep what still applies, drop what is finished. Running the skill also costs little in cache terms, because the caching docs say a skill’s instructions are appended as a message and nothing earlier in the conversation changes.
Here is an example of what it writes. The project is made up.
## Task
Add rate limiting to the login endpoint.
## Branch and state
Branch feature/rate-limit, 3 commits ahead of main, working tree clean.
## Done
- VERIFIED: limiter added in auth.js, 5 attempts per minute per IP; 12 of 12 tests pass.
- UNVERIFIED: the Redis backend under load; only ran it locally.
## Left to do
- Load test at 200 requests per second.
## Decisions
- Fixed window, not sliding: simpler, and good enough for login.
## Rejected (and why)
- Per-account limits: they lock out real users during an attack.
Every done item is marked VERIFIED or UNVERIFIED, so the next session knows what was actually seen. The Rejected (and why) heading stops the new session from re-proposing ideas you already turned down.
Where to keep a skill
A skill is a folder with a SKILL.md file in it. The folder name is the slash command. Where you put the folder decides who gets it.
mkdir -p ~/.claude/skills/wrap # personal: every project on this machine
mkdir -p .claude/skills/wrap # project: commit it and your team gets it
Put the file in the folder and type /wrap. The description is recommended, and name defaults to the folder name.
wrap lives in my personal folder because I want it everywhere. A skill that only makes sense in one repo goes in the project folder instead.
Add that folder to .gitignore if you don’t want to share it.
Rule 3: Send subagents to Haiku, and know which ones ignore you
CLAUDE_CODE_SUBAGENT_MODEL: haiku sends subagent work to the cheapest model. Subagents matter for cost because verbose work, like test runs and log reading, stays in their context while only a summary comes back. The costs page recommends this.
There are two limits. The docs rank how a subagent picks its model: the per-invocation parameter, then the agent’s own model frontmatter, then this variable, then the main conversation’s model. And the subagent docs are blunt: setting the variable “by itself doesn’t change the model the built-in Explore and Plan subagents run on.”
That takes a second variable, CLAUDE_CODE_SUBAGENT_MODEL_FORCE=1, which I haven’t tried. I checked my own session. The built-in Explore agent ran on Opus 5.
Then there is the lesson. While I was checking these settings against the docs, I sent a subagent to look them up.
It ran on Haiku. It told me that CLAUDE_CODE_SUBAGENT_MODEL and CLAUDE_AUTOCOMPACT_PCT_OVERRIDE don’t exist. Both are in the docs.
A cheap model gave a confident wrong answer, and I only caught it because I re-checked the pages myself. Haiku is fine for reading files and running tests. I wouldn’t let it deliver the verdict.
Rule 4: Disconnect the MCP servers you aren’t using
I picked this one up recently: connected MCP servers cost tokens even when you never call them. That is true, so I measured it.
By default the cost is small. The MCP docs say tool search defers tool definitions, so “only tool names and server instructions load at session start.” The costs page tells you to run /mcp and disable servers you aren’t using.
Here is the measurement. I sent the same prompt in print mode on Haiku, once with my normal setup and once with --strict-mcp-config and an empty config. Then I added up the input, cache write and cache read tokens.
echo '{"mcpServers":{}}' > empty.json
claude -p "Reply with the single word: ok" --model haiku --output-format json
claude -p "Reply with the single word: ok" --model haiku --output-format json \
--strict-mcp-config --mcp-config empty.json
My normal setup used 26,526 input tokens. With no MCP servers it used 23,867.
The difference is 2,659 tokens per request, identical on three runs, or about 11 percent of a bare session. That is the price of the names and instructions for everything I have connected, and it is re-read every turn at the cached rate.
Other models count tokens differently, so your number will differ.
/context gives the other half. In a long session on a 1M window it showed 290.8k tokens in use.
The MCP tools I had actually loaded took 4.1k. Another 163.1k of MCP tool definitions sat in a deferred pile that never entered the window, which is 16 percent of it.
That pile is what deferral keeps out, and ENABLE_TOOL_SEARCH=false would load it on every request.
Two more things stood out. First, the 23,867 is not MCP.
A bare “say ok” carries almost 24,000 tokens of Claude Code’s own prompt and whatever else I have installed. In an interactive session /context splits that as a system prompt of 4.8k, built-in tools of 15.5k, skills of 9.9k, custom agents of 1.5k and CLAUDE.md of 1.8k.
That is a different model and mode, so the totals won’t match.
Second, the small number depends on defaults. The docs say tool definitions load up front when ENABLE_TOOL_SEARCH is false, or auto while they total under 10 percent of the window.
They also load up front when a server sets alwaysLoad, or when ANTHROPIC_BASE_URL points to a non-first-party host.
I set none of those. If you do, the cost per server jumps.
Disconnecting is safe mid-session under the default. The caching docs say a deferred server connecting or disconnecting only appends to the conversation and leaves the cache alone. With deferral off, that change invalidates it.
What did not hold up
Four of my rules didn’t survive checking.
Hiding files takes a Read deny rule, not a .claudeignore
I thought I was hiding files with a .claudeignore. The permissions page never mentions one. What it describes is Read deny rules: add one for the path, “such as Read(./.env) or Read(./secrets/**).”
It gets worse. My user settings have no deny rules, this project’s deny list is empty, and there is no .claudeignore file in this repo either.
I believed I followed a rule that I didn’t. Here is what I’d add for this site, in .claude/settings.json.
{
"permissions": {
"deny": [
"Read(./node_modules/**)",
"Read(./dist/**)"
]
}
}
Two caveats from the docs. Read deny rules cover the built-in file tools and file commands that Claude Code recognizes in Bash, like cat. They don’t cover grep -r pattern . or a script that opens files itself, and for Grep and Glob the docs call it a best-effort attempt.
And I haven’t measured how many tokens this saves. It keeps Claude out of files it has no business in.
That is safety first, savings maybe.
Deny rules are one wall. Claude Code auto mode adds another: a classifier that reviews risky actions before they run.
A 60-line CLAUDE.md saves almost nothing
My rule is that CLAUDE.md stays under 60 lines. Mine is 57 lines, and /context puts it at 1.8k tokens.
That is 0.2 percent of a 1M window, and about a fifth of what my skill descriptions take. It sits in the cached project context and gets read at the cached rate.
Against 8 billion tokens, the size is not where the savings are.
The docs’ own advice is to keep it under 200 lines and move workflow-specific instructions into skills, which load only when invoked. One more fact worth knowing: editing CLAUDE.md mid-session doesn’t invalidate the cache, but the edit doesn’t apply either. It loads on the next /clear, /compact or restart.
Keep it short because that is tidy. Just don’t count it as a saving.
Two settings that don’t save anything
CLAUDE_CODE_MAX_OUTPUT_TOKENS: 64000 is a ceiling. You pay for the tokens the model writes, not for the room it had.
I couldn’t confirm the variable in the docs, but it doesn’t matter. A cap you don’t hit costs nothing either way.
effortLevel: "high" is already the default on most models. The docs call it “the default on every model except Opus 5.5 and Opus 4.7.”
Opus 5.5 defaults to medium. The docs also say a top-level effortLevel in your user settings “doesn’t count for Opus 5.5,” but my own session log recorded high on its turns and /effort shows high, so check yours before you trust either. If the line does apply, it costs more on Opus 5.5, not less. On Opus 4.7, where the default is xhigh, it is a step down.
The level that saves is medium, which the docs say “reduces token usage for cost-sensitive work.” On most models each effort level also has its own cache, so pick one at the top of a session instead of switching halfway. Opus 5.5 is the exception: with a subscription or an API key, changing effort keeps the cache.
Autocompact at 200k: a trade I would not defend
I ran the auto-compact window at 200k for a while. The documented way is /autocompact 200k, which saves as the autoCompactWindow setting.
My real settings use CLAUDE_AUTOCOMPACT_PCT_OVERRIDE at 60, which is also documented. It sets the percentage of the window where compaction fires, and it can only compact earlier.
On models with a 1M window, the default is about 967K tokens. So 200k compacts almost five times sooner, and for me it sometimes compacted too early. That is the setting working as designed.
Does it cut Claude Code cost? On paper, partly.
Every turn re-reads the whole context, so a smaller window caps that. The caching docs also say that with a warm cache, a mid-session compaction “costs a fraction of what the context size suggests.”
Against that, compaction throws away detail and rebuilds part of the cache. If the cache has gone cold, the summary request reprocesses everything. I haven’t measured any of it, so I won’t claim a saving.
Here is what I’d do instead. Leave the window alone, clear between tasks, and run /compact by hand at a natural break. That matches the docs’ own tip: compact between tasks instead of waiting for auto-compaction to trigger mid-task.
What I would change first
Delete CLAUDE_CODE_MAX_OUTPUT_TOKENS. Set the effort level on purpose instead of restating the default. Swap the autocompact override for the default window.
Add the deny rules. Keep the four rules that work: opusplan, clearing with /wrap, Haiku subagents with their limits in mind, and a short MCP list. Run /mcp and disable anything you haven’t used this week.
advisorModel: "opus" is not a saving either. It picks the model that answers advisor calls, so it is a quality purchase. Keep it if it earns its place.
Then measure. Run /usage and read the Prompt cache (main) line, which shows your hit rate and misses. If a rule doesn’t move a number, it is a habit, not a saving.
The opusplan split above is also what runs my automated code review pipeline: a strong model for the review stages that matter, a faster one for everything else. That pipeline costs real minutes on top of everything in this post, and I think it’s worth it.
What is the one rule you follow to keep your Claude Code cost down, and have you checked it against the docs lately?