DEV Community

Ali Suleyman TOPUZ
Ali Suleyman TOPUZ

Posted on Originally published at topuzas.Medium on

The Real Economics of Running Claude Code Agents in Production

A friend who runs a small platform-engineering consultancy called me in July with a problem she described as “the invoice that made our CFO walk over to my desk.” Her team had wired Claude Code into their CI pipeline and a handful of internal agent workflows back in the spring, everyone loved it, and then the June bill landed at just over 46,000 dollars. Not a typo. Forty-six thousand, for one month, for a team of eleven engineers.

She asked me to sit in on the audit because I’d spent a chunk of the previous year pulling apart agent cost curves for a different client, and I recognized the shape of the problem before we’d even opened the usage dashboard. Six weeks later, after three specific changes, their August bill came in at 6,100 dollars. Same team, same workload, roughly the same number of agent runs. A 7.5x reduction, and none of it involved doing less work with the agents.

That gap, 40,000 dollars a month sitting on the table, is what this article is actually about. Not “AI is expensive,” which is a lazy take, but the specific mechanics that make agent cost behave so differently from a normal API bill, and why almost nobody catches this until the invoice forces the conversation.

The mechanic nobody explains: cost scales with the square of the turns

Here’s the thing about a chat completion versus an agent loop. A single chat call sends a prompt, gets a response, done. An agent turn sends the entire transcript so far, every tool call, every file it read, every command it ran, plus the new instruction, and gets back the next step. Then it does that again. And again, sometimes for a hundred or two hundred turns in one debugging session.

If each turn adds roughly a constant amount of new content, call it k tokens, then by turn N the transcript being resent is roughly k times N tokens. The cost of turn N alone is proportional to N. But you don’t pay for turn N once, you pay for every turn from 1 to N, and each of those resent the transcript as it stood at that point. Sum that up and total tokens processed across the whole session comes out to roughly k times N squared over 2. That’s not a rounding error, that’s the dominant term.

Concretely: a session that runs 50 turns with an average of 800 new tokens per turn pushes through roughly 1,000,000 cumulative input tokens over its lifetime, just from the transcript-resend pattern, before you even count the actual response generation. Push the same session to 100 turns and you don’t do twice the work, you do four times the work, because (100/50) squared is 4. This is why a session that “got away from someone” for an afternoon can cost more than a week of disciplined short sessions doing the same total amount of useful output.

This is also the single biggest lever in the audit we ran. Her team’s longest-running agents, the ones doing multi-file refactors and long CI debugging loops, were routinely hitting 150 to 300 turns in a single session before anyone thought to reset it. Nobody had done the math on what that curve actually looks like until we graphed it next to the invoice.

Prompt caching is the thing that makes this survivable, when it works

The quadratic-turns problem would make long agent sessions unaffordable if every one of those resends went through at full input price. It doesn’t, because of prompt caching, and understanding how it actually works is the difference between a session that costs cents and one that costs dollars.

Here’s the plain version. When you send a prompt, you can mark a prefix of it, typically your system prompt, tool definitions, and any large static context, as cacheable. The first time that exact prefix is sent, Anthropic writes it to a cache and charges a premium for the write, 1.25x the normal input price for a 5-minute cache, or 2x for a 1-hour cache. Every subsequent call that sends that exact same prefix, byte for byte, hits the cache instead of reprocessing it from scratch, and that hit costs about 10 percent of the normal input price. That’s the “90 percent discount” people talk about, and it’s real, but it only applies to the part of the prompt that stayed identical.

The word doing all the work there is identical. The cache key is a fingerprint of the prefix. Change one character before the cache boundary, whitespace included, and you get a cache miss, which for practical purposes on a subsequent call, means paying the write premium again instead of the ten-cent-on-the-dollar read price.

Here’s the arithmetic that made this click for her team. Say the shared system prompt plus tool schema for one of their coding agents runs 15,000 tokens, which is not unusual once you count file-editing tools, a linter tool, a test runner tool, and a project-specific CLAUDE.md. On Sonnet, base input price is 2 dollars per million tokens. A clean cache hit on that block costs 15,000 times 0.20 dollars per million, which is 0.003 dollars. A cache miss that forces a fresh write costs 15,000 times 2.50 dollars per million (the 1.25x write premium), which is 0.0375 dollars. That’s a twelve and a half times difference, on one static block, repeated every single turn.

Multiply that gap by 200 turns in a session and you’re looking at roughly 7.50 dollars in miss-penalty on just the static prefix, versus 60 cents if the cache had held. And that’s before counting the much larger, and much more frequently mutated, conversation transcript itself.

The timestamp anti-pattern

So what actually breaks the cache in practice? In her team’s case, it was one line, added by a well-meaning engineer three months earlier, that injected the current timestamp into the system prompt so the agent would “know what time it is” for scheduling-related tasks.

You are a coding assistant. Current time: 2026-06-14T09:41:22Z.
Repository: internal-billing-service.
Available tools: read_file, write_file, run_tests, run_linter...
Enter fullscreen mode Exit fullscreen mode

That timestamp sits before the cache boundary, and it changes on every single call because it’s generated fresh each time. The fingerprint of the prefix is different on every request, which means every request is a cache miss, which means every request pays the write premium on that entire block instead of the read discount. The tool schema below it, the CLAUDE.md content, all of it gets invalidated by one line of dynamic text sitting above it.

The fix is boring and that’s the point: move anything that changes per-call, timestamps, request IDs, session-specific metadata, out of the cached prefix and into the part of the prompt that comes after the cache boundary, ideally down in the user turn itself where it belongs anyway. If the agent genuinely needs the current time, give it a get_current_time tool call instead of baking it into the system prompt. One tool call a session versus a broken cache on every single turn is not a close call.

We found two more instances of the same pattern elsewhere in their prompts, a build number and an environment tag, both sitting above the cache boundary for no reason other than “that’s where someone pasted it.” Moving three lines of text fixed a meaningful chunk of the bill on its own.

Daily habits: /clear, /compact, and the @ shortcut

Once the caching bug was fixed, the next lever was session hygiene, and this is the part that’s genuinely just about developer habits rather than configuration.

/clear wipes the working context and starts fresh. Use it when you're done with one thing and starting something unrelated. There is no reason to carry a transcript from debugging a flaky test into a session where you're now writing a new feature; the past context isn't just useless there, given the quadratic-cost mechanic above, it's actively expensive to keep dragging along.

/compact condenses the current session into a structured summary and keeps going. Use it when a single task is genuinely still in progress, the context window is filling up, but you still need the thread, active file state, decisions made three turns ago, the reasoning behind a chosen approach. Compacting manually, before the tool auto-triggers it, tends to produce a tighter summary than waiting for the automatic version, because you know what actually matters to keep.

The rule that stuck with her team: /clear when the past doesn't matter, /compact when it does but there's too much of it. If you find yourself compacting the same session three or four times in an afternoon, that's usually a sign the task should have been split into separate sessions or handed to a subagent instead of kept alive indefinitely.

The other habit, smaller but it adds up: reference files with @ instead of describing them. Typing "check the auth logic" makes the agent spend a turn running a search tool, then another turn reading whatever file the search turned up, then finally acting on it. Typing @src/middleware/auth.ts pulls the file straight into context with no search round trip at all. Two tool calls saved per reference sounds small until you count how many times a real session references a specific file, at 150+ turns, that's routinely a double-digit percentage of the session's total tool calls doing pure search-and-fetch instead of actual work, all of it billed the same as everything else.

The tokenizer curveball

Partway through the audit we ran into something that had nothing to do with her team’s usage patterns and everything to do with a change on Anthropic’s side that quietly distorts anyone’s before-and-after cost comparison if they don’t account for it.

Claude Sonnet 5 uses a new tokenizer, and the same input text produces roughly 30 percent more tokens on it than it did on Sonnet 4.6. At the same time, Anthropic priced Sonnet 5 at 2 dollars per million input tokens and 10 dollars per million output, down from 4.6’s 3 dollars and 15 dollars, and then made that pricing permanent instead of letting it step up to 3/15 as originally scheduled for September 1.

Read quickly, that looks like a straightforward 33 percent price cut. It isn’t, because you’re also paying for 30 percent more tokens to represent the exact same text. Do the actual math on a fixed piece of text that used to tokenize to N tokens on 4.6: on Sonnet 5 it’s now roughly 1.3N tokens. Compare the two bills directly. 4.6’s cost was N times 3 dollars per million. Sonnet 5’s cost is 1.3N times 2 dollars per million, which is 2.6N per million. The ratio is 2.6 over 3, which is about 0.87. You saved roughly 13 percent on that piece of text, not 33 percent, and the same ratio holds for output tokens.

If you built a cost forecast or a customer pricing model assuming the sticker-price cut would flow straight through, you overestimated your savings by more than half. This isn’t a criticism of the pricing move, permanent pricing is a genuinely good thing for planning, but it’s exactly the kind of quiet denominator shift that breaks a cost model nobody re-derives after a model swap. If you migrated workloads to Sonnet 5 and your bill didn’t drop the way the announcement implied it should, this is almost certainly why, and the fix is simply to re-tokenize your representative prompts on the new model rather than reusing old token counts.

Model routing: when the cheap model actually saves you money

The third lever, and the one that took the most convincing, was routing. Her team was sending essentially everything, including trivial formatting and classification tasks, through the same model tier they used for actual multi-step reasoning, because someone had set it up that way early on and nobody had revisited it.

Routing sounds like a free win, send easy stuff to a cheap model, hard stuff to an expensive one, but it isn’t automatically a win, and the cases where it backfires are worth walking through with real numbers.

Take a simple, well-defined task: classify a support ticket into one of eight categories. Call it 500 input tokens and 300 output tokens. Sent directly to a larger model at 5 dollars input / 25 dollars output per million tokens, that costs roughly 500 times 5 plus 300 times 25, all over a million, which comes out to 0.01 dollars. Route it through a cheaper model at 1 dollar input / 5 dollars output instead, and it costs roughly 0.002 dollars, a fifth of the price, for a task that a small model handles reliably. That’s routing working as intended.

Now take a task near the edge of what the cheap model can actually do. Say it succeeds outright only 70 percent of the time, and the other 30 percent produces a wrong or unusable answer that then has to be caught and re-run on the larger model anyway. Now your expected cost is the cheap-model cost every time, plus the larger-model cost 30 percent of the time: 0.002 plus 0.3 times 0.01, which is 0.002 plus 0.003, or 0.005 dollars. Still cheaper than 0.01 on average, but you’ve also added a second round trip’s worth of latency to every failed attempt, and if that 70 percent success rate creeps down to 50, the math flips: 0.002 plus 0.5 times 0.01 is 0.007, and once you count that the failed attempts also blocked a user waiting on a response, the “savings” stopped being worth it well before the dollar figures crossed.

The break-even rule we used going forward: routing to a cheaper model only pays off, on average, when its standalone success rate exceeds the ratio of cheap-model cost to expensive-model cost. In the example above that ratio is 0.002 over 0.01, or 20 percent, so anything the cheap model gets right more than one time in five is worth attempting first, on cost grounds alone. Below that threshold, or in a latency-sensitive interactive path where users notice the extra hop, skip the cascade and just call the model that will get it right the first time.

Here’s the decision matrix her team ended up pinning above the routing config:

+------------------------------+---------------------+-------------------------------------------+
| Task Complexity | Recommended Tier | Notes |
+------------------------------+---------------------+-------------------------------------------+
| Trivial (format, extract, | Haiku-class | Near-zero failure rate on well-scoped |
| lookup, simple classify) | | input; route directly, no cascade needed |
+------------------------------+---------------------+-------------------------------------------+
| Simple, well-defined | Haiku-class, with | Cascade pays off if standalone success |
| (single-step reasoning) | Sonnet fallback | rate exceeds cost_cheap/cost_expensive |
+------------------------------+---------------------+-------------------------------------------+
| Moderate (multi-step, | Sonnet-class | This is where most agent turns actually |
| some judgment required) | | live; cascading down usually costs more |
+------------------------------+---------------------+-------------------------------------------+
| Complex (long-horizon | Opus-class, or | Route directly; a failed cheap attempt |
| agentic, ambiguous spec) | Sonnet w/ extended | here costs more in wasted turns than it |
| | thinking | ever saves in fees |
+------------------------------+---------------------+-------------------------------------------+
| Interactive, latency | Whatever tier | Skip cascades entirely; the extra round |
| sensitive (user waiting) | handles it in one | trip costs more in perceived lag than it |
| | pass | saves in dollars |
+------------------------------+---------------------+-------------------------------------------+
Enter fullscreen mode Exit fullscreen mode

The honest summary of that table: routing is a real lever for high-volume, well-scoped, non-interactive tasks, and mostly a distraction everywhere else. Her team’s biggest single mistake wasn’t under-routing, it was running long agentic coding sessions on a model tier chosen for a completely different, much simpler workload, and never revisiting the choice once the agents grew more complex.

Measuring your own spend before you get the invoice

None of the above matters if you can’t see it happening before the bill arrives, and this was the tool that turned the audit from guesswork into line items: ccusage, an open-source CLI that reads Claude Code’s local usage logs and turns them into cost and token breakdowns.

Install and run it with no global setup needed:

npx ccusage@latest daily
Enter fullscreen mode Exit fullscreen mode

That gives you a day-by-day breakdown of tokens and estimated cost. The commands worth knowing:

npx ccusage@latest daily # usage and cost per day
npx ccusage@latest weekly # rolled up by week
npx ccusage@latest monthly # rolled up by month, good for invoice reconciliation
npx ccusage@latest session # broken out by individual conversation/session
npx ccusage@latest blocks # tracks usage against Claude's billing windows live
npx ccusage@latest daily --json # structured output for piping into your own dashboard
Enter fullscreen mode Exit fullscreen mode

A daily report looks roughly like this:

Date Model Input Output Cache Create Cache Read Total Tokens Cost (USD)
---------- ------------- --------- -------- ------------ ----------- ------------- ----------
2026-06-12 sonnet-4-6 42,100 8,900 210,400 1,840,200 2,101,600 $18.42
2026-06-13 sonnet-4-6 51,300 9,750 318,900 1,120,800 1,500,750 $24.91
2026-06-14 sonnet-4-6 48,900 9,100 289,600 402,100 749,700 $19.88
Enter fullscreen mode Exit fullscreen mode

The column that matters most for diagnosing exactly the problem her team had is the ratio of Cache Read to Cache Create. On June 12, cache reads dwarfed cache creates, roughly 9 to 1, which is a healthy session where the prefix stayed stable and most calls hit the cache. By June 14, that ratio had collapsed to roughly 1.4 to 1, cache creates almost catching up to cache reads, which is the fingerprint of a broken cache, repeated fresh writes instead of cheap hits. That’s the exact signature the timestamp bug left behind, and it’s visible in about four seconds of looking at the right column, if you know to look.

The session view is the other one worth running weekly, because it surfaces which specific sessions ran the longest and cost the most, which is how we found the 150-to-300-turn sessions in the first place. Sorted by cost descending, the outliers are obvious, and they're almost never the sessions doing the most valuable work, they're the ones that got left open.

The invoice, revisited

Back to the 46,000 dollars. When we broke down where the roughly 40,000 dollar monthly reduction actually came from, it landed approximately like this: about half came from session hygiene, disciplined /clear and /compact use cutting the average session length enough to blunt the quadratic-turns effect; a third came from fixing the timestamp bug and restoring the cache hit ratio across their shared prompts; and the rest came from moving the genuinely trivial classification and formatting work off the model tier they'd been defaulting to for everything.

None of those three fixes required using the agents less. They required understanding that agent cost isn’t API cost with a markup, it’s a different curve entirely, one shaped by how many turns a session runs, whether your prompt prefix stays byte-identical across calls, and whether you’re paying premium rates for work a cheaper tier could have handled just as well. The 46,000 dollar month wasn’t a pricing problem. It was a mechanics problem, and mechanics problems are the kind you can actually fix.

Tags: claude-code, ai-agents, llm-cost-optimization, prompt-caching, devops, ai-engineering, finops

Top comments (0)