← /blog
· ACE Engineering#coding-agents #claude-code #cost #context #cache #scorecard #measurement #research #managed-api-stack

What 13,000 coding-agent requests actually cost

Thirty-six days of one developer's Claude Code sessions, measured request by request: 4.2B prompt tokens, $3,035 at list price, and 90.5% of it input that had already been sent. Seven revisions later, the honest saving is 4.6% — and every revision came from measuring a proxy instead of the thing.

What 13,000 coding-agent requests actually cost

Everyone building on coding agents has the same intuition about where the money goes, and the intuition is wrong in a specific, expensive way. This post is what happened when we stopped reasoning about it and read the transcripts.

The corpus: one developer, one machine, 36 days. 121 transcripts — 47 developer sessions plus the 74 subagent runs they spawned — 12,902 API requests, 4.17 billion prompt tokens, $3,035.09 valued at Anthropic list price. Every figure below is reproducible by re-running the scripts against the same committed dataset.

Two warnings before any number, because both change what the numbers mean.

Nobody paid $3,035. The developer was on a $20/month subscription for the entire window. Every dollar here is a list-price valuation — what this exact workload would bill at API rates. It is the right number for a team on BYOK or API billing, and the wrong number to describe what happened to this account. The API-equivalent run rate is roughly $2.5k/month against $20 actually paid.

It also means this corpus was generated under zero marginal cost. Nothing in it was shaped by price; sessions ran to the full 1M context because there was no reason not to. Treat the context-growth figures as an upper bound. That is also precisely why it is useful: an engineering org on API billing gets exactly this — developers with no cost signal, because the bill lands on a budget they never see.

One developer is one developer. The cost structure is a property of how agentic coding works and should generalise. The magnitudes — model mix, session lengths, absolute totals — are this fleet's and should not be quoted as industry figures.


1. The workload is not a generator. It is a re-reader.

Start with the shape of the traffic, because everything else follows from it.

Direction Tokens Share of input
Fresh input 1,067,321 0.03%
Cache read 4,096,550,551 98.33%
Cache write 68,326,182 1.64%
Input total 4,165,944,054
Output 10,721,571

Input outweighs output 389 : 1. Per request that is 322,890 tokens in and 831 out.

Priced, that becomes the only chart that really matters:

Stacked bar showing the $3,035 bill split into cache reads 67.3%, cache writes 23.2%, output tokens 9.2% and fresh input 0.2%

90.5% of spend is input that was already sent before — re-read from cache, or written into it. Output is 9.2%. Fresh input, the only thing any prompt compressor can ever touch, is 0.2%.

Three product conclusions fall out immediately, and two of them kill work we had planned:

  • Prompt compression is not "low value", it is arithmetically pointless. A perfect compressor that eliminated all fresh input would cut the bill by 0.2%. Any compressor that touches the cached prefix instead makes things dramatically worse, because it busts the cache. Remove it from the coding path entirely — not "off by default", not wired at all.
  • Output-token optimisation is a rounding error. Shortening responses, capping max_tokens, tiering for cheaper output — all of it chases 9.2% while 90.8% sits in context handling.
  • Prompt caching has already banked a 7.1× saving ($18,393 of avoided cost), and that is Anthropic's, not ours to sell. Anything we build starts after that.

2. Cost is context size × turns, and there is no cheap quartile

Grouped horizontal bars: requests and cost share by context bucket. The 500k–1M bucket is 24.6% of requests and 46.6% of cost, at $0.449 per request

Context / request Requests % of requests Cost % of cost $/request
0–100k 2,835 22.1% $205 6.7% $0.072
100–250k 3,206 25.0% $488 16.1% $0.152
250–500k 3,640 28.4% $926 30.5% $0.255
500k–1M 3,151 24.6% $1,416 46.6% $0.449

24.6% of requests carry more than 500k of context and account for 46.6% of all spend. Peak contexts reach 999,488 tokens — sessions run at the full 1M window, and every subsequent request re-reads all of it.

The distribution matters as much as the total. The p25 request already carries 111k tokens. There is no cheap quartile to optimise around, no long tail to trim. Context is uniformly enormous.

Sessions are the opposite shape. Mean cost per session is $64.58 against an $8.16 median, and the top 9 of 47 sessions (19%) account for 76.1% of all spend. A budget cap that never fires below $50 leaves 72% of sessions untouched and still sits in front of 89% of the money.

One instruction is sixteen API requests

12,902 requests produced 820 handbacks to the user. That is 15.7 API requests per thing the developer actually asked for, at $3.70 per handback.

This is why per-turn payload optimisations lose to context-size optimisations. The payload is re-sent sixteen times per instruction.

3. The work has no rhythm, and that rules out most alerting

Column chart of daily token volume across 37 days, from idle days to a 412M peak

Day over day, the median change in token volume is +0.1%, with an interquartile range of −58.9% to +104.6%. Volume routinely doubles or halves between consecutive days. Weeks are no steadier — the swing from week 1 to week 3 is 17.5×. Five of 37 calendar days are idle.

A 2× day is the median week's experience, not a signal. Every anomaly detector, capacity plan or budget alert built on a day-over-day or week-over-week delta will fire constantly and mean nothing. Budgets for this workload have to be cumulative — rolling spend against a period cap — never rate-of-change.

Tokens and commits are also only loosely coupled. 07-14 spent $396 for 5 commits; 07-16 spent $47 for 15; 07-09 produced 106 commits on 33M tokens. Across the window: 11.1M tokens per commit, of which 28k are the model's own output. For every token the model generated, 395 tokens of context were read back in. That ratio, not the price of any model, is the whole problem in one number.


4. Where the tokens actually sit

If context size is the cost function, the next question is what is in the context. The unit here is token-turns — tokens × turns resident — because content in the context is re-read on every subsequent request, which is exactly what cache-read billing is proportional to.

Ranked horizontal bars of resident token-turns by tool: Bash 45.95%, Read 39.59%, Edit 4.35%, then a tail under 4%

Tool Resident token-turns Share
Bash 487,265,664 45.95%
Read 419,811,300 39.59%
Edit 46,134,368 4.35%
chrome/computer 38,729,141 3.65%
ExitPlanMode 18,816,234 1.77%
Agent 15,594,672 1.47%
Write 9,319,374 0.88%
AskUserQuestion 8,636,594 0.81%

Bash is 51.6% of tool calls and averages only 915 bytes per result — it is high-frequency and small. But by occupancy it is still the largest resident tool, ahead of Read, and it is the one with no natural size bound. A single pytest -v or find dump enters the context and is re-read on every subsequent turn for the rest of the session.

And here is the ceiling on all of it: tool results are only 24.2% of prompt-token volume. The other 75.8% is system prompt, tool definitions and message history — none of which a transcript can see. Every lever below reaches a quarter of the problem.

We got one look at that 75.8%, by running a live session through a capture proxy. The result was not what we predicted: in that session tool definitions were ~80% of the assembled prompt — 64,029 bytes of them — against 12% system prompt and 9–11% messages. 27 tools were defined; 1 was used. 95.4% of the definition bytes were never touched, and they render first, so they sit at the head of the prefix and are re-read every single turn. That is a large, zero-quality-cost lever nobody was looking at, and it only became visible when we stopped reading transcripts and started reading request bodies.


5. Four levers, measured — and two of them died

We named four tail-side hygiene levers: de-duplicate repeat reads, supersede stale results, age out old content, truncate Bash output. Then we measured each one against the corpus instead of arguing about it.

Ranked bars of lever value with risk labels: age-out at 100 turns $304.66 high risk, age-out at 400 turns $122.57 medium, Bash truncate $54.94 low, supersede $9.50 low, read de-dup $0.33 none

Rank Lever?% of prompt tokens $ % of billed Risk
1 age-out @100 turns 17.19% $304.66 9.53% HIGH, unmeasured
1c age-out @400 turns 7.45% $122.57 3.84% MEDIUM
2 Bash truncate @512 tok 2.90% $54.94 1.72% LOW with head+tail
3 supersede 0.67% $9.50 0.30% LOW
4 read de-dup (verified) 0.03% $0.33 0.01% NONE — and worth nothing

The lever that fell by 540×

Read de-duplication was supposed to be the safe, obvious, ship-first lever. The case for it was a single statistic: 59% of file reads are re-reads of a file already read that session. That count is correct. This corpus puts it at 57.1%.

The inference drawn from it does not follow.

Stacked bar: of 1,154 repeat reads, 97.6% request a different line range, 2.4% the same range, 0.2% are byte-identical

Of 2,022 Read calls with a path
First read of that path 868 42.9%
Repeat read of an already-read path 1,154 57.1%
→ same line range (identical tool input) 28 2.4% of repeats
different line range (pagination, adjacent slice) 1,126 97.6% of repeats
→ byte-identical to an earlier read 2 0.2% of repeats

A repeat read of a file is almost never a repeat read of the same bytes. 97.6% of the "re-reads" justifying this lever are the agent walking a different part of a file it already opened — new content, carrying new information, impossible to collapse to a pointer.

The most striking number in the original analysis was "73 reads of one file in one session". It reproduces exactly, and it dissolves on inspection: those were 73 different offsets into one large file. No identical range is re-read more than 4 times in any session in the corpus.

What broke the earlier measurement was mundane. Our dataset fingerprinted a tool call by hashing file_path alone. Session 4b5ae4dd reads proxy.py at offset 1 limit 470, then offset 470 limit 450, then offset 920 limit 200 — three disjoint slices, one walk down a 1,119-line file. Scored as three reads of the same target, that single walk contributed $2.89 of "de-duplication value".

Key on the whole tool input, and require the bytes to already be resident, and the lever goes from $36.88 to $0.33. Its measured value across four passes: $178.10 → $43.27 → $37.58 → $0.33.

Recommendation: drop it, don't defer it. At $0.33 over 36 days it does not pay for the per-path staleness tracking it requires, and the tracking is the part that has to be correct.

The lever we had rated too risky to ship

Bash truncation was rated HIGH risk on a reasonable-sounding objection: "Bash output is the signal — stack traces, test failures, grep hits. A 2 KB cut removes most of a Python traceback."

True about tracebacks. Irrelevant to where the money is. Splitting the recovered value by whether the output's head carries a diagnostic (Traceback, FAILED, assert, panic:, an error line):

Cap Dumps & listings Diagnostics
1024 tok $19.90 (90%) $2.15 (10%)
512 tok $49.30 (90%) $5.63 (10%)
256 tok $87.24 (89%) $10.29 (11%)

Nine tenths of the value is in sed/cat dumps, grep roll-ups, git diffs and black/ruff summaries — output the agent skimmed once and never consulted again. The risk is concentrated in the other tenth, and it is separable two ways: keep head and tail so a traceback's exception line and its final frame both survive, or exempt anything matching the diagnostic predicate outright and take 90% of the value at close to zero risk.

One example makes it concrete. Session e170b28f, turn 4 of 1,317:

sed -n '125,230p' docs/requirements.md      →  16,756 chars ≈ 4,189 tokens

Issued as the fourth request of the session. It stayed resident for all 1,313 remaining turns — 5,500,157 token-turns, $2.75. A 512-token cap recovers $2.41 of that one call, which is 7× the entire verified Read de-dup lever across 36 days.

The published ordering was exactly inverted. Bash truncation is first by 166×; Read de-duplication is last by two orders of magnitude.

The lever that is now the whole thesis

Age-out — simply dropping tool results older than N turns — carries 71% of the balanced tier's value and 88% of the aggressive tier's. With de-dup and supersede corrected downward, the entire token-removal case rests on one parameter whose quality cost nobody has measured.

The cleanest example in the corpus: session e170b28f, turn 147. A whole-file Read of simulator_studio.py returns 10,003 tokens. The string simulator_studio never appears again after turn 198 — not in a tool call, not in a result, not in assistant text. The session then ran for another 1,118 turns.

Read at turn 147
Last mention of the file, anywhere 198
Idle residency 1,118 turns
Dead token-turns 11,183,354
Cost of that dead residency $5.59
Full residency cost from birth $5.85

96% of that read's lifetime cost accrued after the agent had provably stopped referring to the file. One read, one session, $5.59 — against $0.33 for the entire read de-dup lever across the whole corpus.

The gap between a 400-turn window ($122.57) and a 100-turn window ($304.66) is $182 — larger than every other lever combined. And nothing in this corpus tells you what a dropped result costs in task success, because no session was ever run with content removed. That single experiment gates 71–88% of all remaining value.


6. The other axis: where the clock goes

Cost is one measure of efficiency. Elapsed time is the one developers actually feel, and it decomposes very differently. Measured over 162 sessions and 1,035.9 hours of wall clock:

Ranked bars of session time: idle over 5 minutes 89.3%, model thinking 3.6%, tool execution and approval 3.5%, human composing 1.7%, model generating 1.2%, first response 0.6%

Phase Hours Share
Idle > 5 min 925.6 89.3%
Model thinking after a tool 37.0 3.6%
Tool execution + approval 36.7 3.5%
Human composing a prompt 17.6 1.7%
Model generating 12.5 1.2%
Model first response 5.9 0.6%
Active (excluding idle) 110.3 10.7%

Median session span is 10.7 minutes. Nearly nine tenths of elapsed session time is idle, so any optimisation aimed at the active loop is competing for a tenth of the clock.

Splitting that idle time by whether the agent was holding a pending tool call when it went quiet is where the finding is:

Idle > 5 min Hours Occurrences Mean
Parked on a pending tool call 242.5 227 64 min
Idle for any other reason 683.1 310

227 times, an agent sat holding a tool call for an average of 64 minutes. That is 26.2% of all idle time. What it was parked on: Bash 190, AskUserQuestion 13, ExitPlanMode 9, Edit 5, Agent 3, Read 2. Bash is 84% of parked events. This is the "came back an hour later to find the agent waiting" failure mode, and it dwarfs every active-loop figure.

It is an upper bound, not a saving — some of those stretches are a human who left for unrelated reasons while a call happened to be pending, and a transcript cannot tell that apart from a human who would have returned to an agent that had made progress.

The framing that is two orders of magnitude off

Under this configuration, per-prompt approval latency is already gone: median gap on a low-risk call is 0.0s, and low-risk gap time totals 1.9h against 1,035.9h — 0.2% of wall clock. acceptEdits and auto mode already collapsed it.

So "save keystroke latency by clicking yes faster" is worth ~0.2% here. "Stop the agent parking for an hour" is worth up to 23%. Same feature, two framings, two orders of magnitude apart.

7. What developers actually refuse

A declined tool call is recorded verbatim in the transcript — is_error: true carrying the string "The tool use was rejected". That makes 14,786 calls a labelled dataset, so risk does not have to be argued from first principles.

93 rejections in 14,786 calls — 0.63%.

Rejection rate by tool against a 0.63% base rate: tabs_context 10.81%, navigate 7.50%, WebFetch 2.83%, chrome/computer 1.44%, Bash 0.70%, Write 0.46%, Edit 0.03%, Read 0.00%

Two readings matter.

The highest-rate entries are not risk refusals. ExitPlanMode at 29% and AskUserQuestion at 18% are tools whose entire purpose is to hand control back — "rejecting" one means picking a different option or continuing to plan. They have to be excluded from any risk accounting or they poison it.

Browser and egress tools are the genuine elevated-risk clusternavigate 7.50%, tabs_context 10.81%, WebFetch 2.83%, all an order of magnitude above base. These are the calls that reach outside the machine, and a human declines them far more often. Meanwhile Read, across 2,015 calls, was never once declined.

Scored against that ground truth, a deterministic five-tier classifier that fails closed puts 3,725 calls (25.2%) in a SAFE tier. Its raw rejection rate looks bad at 0.56% — until you notice that 18 of those 21 rejections are the control-flow tools above. Excluding them: 3 rejections in 3,640 calls — 0.082%, and all three are read-only commands (git show, cat) where the refusal meant "wrong file, don't bother", not a safety judgement.

A deterministic SAFE tier covering a quarter of all tool calls has an effective false-approve rate of 0.08% against real human decisions. That is a measurement, not an argument.

The engineering work is the other bucket. 44.5% of calls land in UNPARSED — meaning "ask", not "allow" — and nearly all of it is one problem: shell commands are not classifiable by their head. cat x > y writes. echo $(rm -rf .) deletes. 4,410 calls contain a redirect or command substitution; 2,127 are compound commands with a non-safe stage. Draining that requires real per-segment shell parsing, and it is where the effort goes if this is ever built.


8. Seven revisions, every one downward

This is the part worth internalising, more than any individual figure.

Column chart of the expected saving as claimed on each date, falling from ~70% to 4.6% across six revisions

Date Claim What changed it
initial 50–90% input-cost cut asserted from how prompt caching works
2026-07-27 25–40% Claude Code already caches well; 6.7× is banked before we arrive
2026-07-27 18.4% levers backtested instead of estimated
2026-07-27 16.6% 55.2% of repeat reads follow an Edit — naive de-dup served stale content
2026-07-27 ~5% usage headroom a cost saving is not a token saving
2026-07-28 6.3% expected / 3.4% floor corpus re-extracted per API request
2026-07-29 ~4.6% expected / 1.7% floor levers re-measured off raw transcripts

Six of the seven revisions were corrections to our own analysis, not new information about the world. And the pattern across all of them is identical: we measured a proxy for the thing instead of the thing.

  • Transcript records as API requests. Claude Code writes one transcript record per content block and repeats the whole usage object on each. A response containing [thinking, text, tool_use] is three records carrying the same prompt-token counts, three times. Per-record summing overstated prompt volume 1.95× and output 2.34×. Every absolute halved — $6,134 became $3,035.
  • File paths as tool targets. Covered above: a paginated walk became 73 duplicate reads.
  • base64 length as image tokens. A screenshot arrives as a base64 blob. Charging len(base64) / 4 bills a 1451×840 JPEG at 27,878 tokens; Anthropic bills it at roughly w·h/7501,625. A 17× overstatement — and it moved the entire denominator: tool results are 24.2% of prompt volume, not the 40.1% we had published.

The 2026-07-28 revision is the only one that moved a conclusion rather than a magnitude, and it moved two. Duplicate records sit milliseconds apart carrying identical context — which is exactly the signature our backtest classified as "prefix mutation". The bug was manufacturing mutation events out of its own duplicates. With them removed:

  • Prefix mutation fell from 61.4% of cache loss ($828.72) to 13.4% ($87.11) — from the largest lever in the analysis to the smallest of three.
  • TTL expiry rose from 21.8% to 47.6% ($207.58) — and became the largest single lever we have, at zero UX risk, because refreshing a cache entry before it expires changes nothing the model sees.

The next figure to distrust on the same grounds is the one no lever reaches: the remaining 75.8% of context volume, which is measured by subtraction rather than observed.


9. Where this leaves it

Tier What it contains Cost Tokens
FLOOR Bash truncation + supersede 1.7% 2.93%
EXPECTED + prefix-mutation fix 4.6% 2.93%
+ TTL keep-alive accounting only, removes zero tokens ~11.4% 2.93%
BALANCED + age-out @400 turns 13.4% 8.1%
AGGRESSIVE + age-out @100 + 256-tok cap 19.8% 18.8%

Two columns that change the conclusion. %cost and %tokens are different units and do not track each other. TTL keep-alive and mutation elimination are pure price conversion — the tokens that fell out of cache still get sent, they simply stop being billed as writes (2.0× input) and become reads (0.1×). Same volume, ~20× cheaper.

So the levers rank completely differently depending on who you are:

Team on API billing Developer under a token cap
TTL keep-alive largest lever worthless — conversion, not reduction
Mutation fix modest worthless
Tail-side hygiene small the only thing that helps

And the ordering is uncomfortable in a way we did not expect going in: the only zero-risk lever is worth more than all validated tail-side hygiene combined, and the levers that actually remove tokens are small while the large ones only move price.

The honest recommendation today is balanced-minus: ship Bash truncation at 512 tokens with head-and-tail retention and a diagnostic exemption ($55, low risk), ship supersede alongside it because it is nearly free ($9), and hold age-out until someone replays a session with aged context and measures task success.

The five things blocking everything else

# Blocker What it gates
1 Message history and tool definitions not measurable from a transcript the other 75.8% of context volume
2 Cause of prefix mutation unknown $87 / 2.7% of cost
3 Tiering quality cost unmeasured a 5× price spread on 67.3% of spend
4 Aging risk unquantified 71–88% of all token removal
5 Single-client corpus whether any of this generalises

Blockers 1 and 2 are the same experiment: one live session through a capture proxy yields both the mutating field and the assembled prompt. That is still the highest-leverage hour of work available — it is what produced the tool-definitions finding in §4, and it has already paid for itself once.


What we would tell anyone measuring their own agents

  1. Count API requests, not transcript records. If you are summing usage per record, you are roughly doubling everything. Join on message.id.
  2. Key tool calls on the whole input, not on the target. Otherwise pagination looks like duplication and you will build a lever that isn't there.
  3. Price images as images. w·h/750, not len(base64)/4. It is a 17× error and it moves your denominator.
  4. Verify redundancy against the bytes. "Same argument" is not "same content"; requiring byte-identity or verbatim containment took our best lever from $37.58 to $0.33.
  5. Measure the clock separately from the money. They decompose completely differently, and the biggest time finding — 242 hours of agents parked on approval — is invisible in any cost metric.
  6. Instrument the assembled prompt, not just the transcript. Three quarters of the volume is only visible there, and in our one capture it was dominated by tool definitions nobody had thought to look at.

Every one of those is a correction we published against ourselves. The direction was always the same: down, and toward measuring the thing rather than a proxy for it.


The instrument that produced all of this now runs as a local sidecar on the developer's own machine — nothing uploaded, transcripts read but never written. We wrote about what it shows in Announcing the ACE sidecar.