[BUG] Headless `claude -p` (entrypoint `sdk-cli`) uses ~1.8× more of the 5-hour window per token than interactive `cli`, same workload
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet
- [x] This is a single bug report (please file separate reports for different bugs)
- [x] I am using the latest version of Claude Code
What's Wrong?
On a Max subscription, the same workload uses about 1.8× more of the 5-hour window when it runs headless (claude -p --input-format stream-json, recorded as entrypoint: "sdk-cli" in the transcripts) than when it runs in the interactive TUI (entrypoint: "cli"). Token usage is the same in both modes.
The Agent SDK help article (https://support.claude.com/en/articles/15036540-use-the-claude-agent-sdk-with-your-claude-plan) says claude -p "still draw[s] from your subscription's usage limits", and I could not find anything saying it is weighted differently.
After moving the same work from the TUI to claude -p, we hit the 5h limit several times a day with no change in workload, and we paid for extra usage (rejected + isUsingOverage: true) that the same work in the TUI would not have needed.
What Should Happen?
The same tokens on the same model should use the same share of the 5-hour window, whether they come from claude -p or from the interactive TUI. If headless usage is intentionally weighted differently, it should be documented.
Error Messages/Logs
Controlled A/B test, 2026-09-25 10:27–10:41 UTC, Claude Code 2.1.282, claude-opus-5, effort high
Phase | Test spend (API-equiv.) | Other spend | five_hour utilization | $ per 1% of 5h
-------------|-------------------------|--------------------|-----------------------|---------------
headless | $12.74 | $0.40 (headless) | 52% -> 60% | ~1.6 (1.5-1.9)
interactive | $12.91 | $1.12 (headless) | 60% -> 65% | ~3.0 (2.4-3.9)
Ranges come from the 1% resolution of the utilization reading; they do not overlap.
Historical (same account, all Opus 5, API-equivalent spend per 1% of each 5h window that peaked above 25%):
2026-09-18..22 cli 2.1.267/2.1.270 3.1-4.1 $ per 1% (8 windows)
2026-09-23..24 sdk-cli 2.1.267 1.6-1.9 $ per 1% (5 windows)
Steps to Reproduce
- Create a directory with ~250K tokens of plain text split into files small enough for
Read(I used 18 files of ~45K characters of source code). - Prepare a fixed prompt list: turn 1 asks Claude to read all files in parallel and reply only "read"; turns 2–41 are short questions about the content answered in one sentence, without tools.
- Headless phase: run
claude -p --input-format stream-json --output-format stream-json --verbose --permission-prompt-tool stdio --replay-user-messages --append-system-prompt "<one short paragraph>" --dangerously-skip-permissions --model claude-opus-5
and send the prompts one by one as stream-json user messages, waiting for each result. Record rate_limit_event.rate_limit_info.unifiedWindows.five_hour.utilization.
- Interactive phase: run
claude --model claude-opus-5 --dangerously-skip-permissionsin tmux, send the same prompts withtmux send-keys, detect turn end with a Stop hook, and readrate_limits.five_hour.used_percentagefrom the statusline JSON. - Before and after each phase, run a tiny probe (
claude -p --model claude-haiku-4-5 --output-format stream-json --verbose "reply: ok") to read utilization the same way. - Compute each phase's token cost from the session transcripts (
message.usage, deduplicated by message id + request id) at list API prices, and subtract any other activity on the account during the phase (measured from the transcripts of every machine on the account). - Compare the $ per 1% of the 5-hour window: headless comes out ~1.8× more expensive for the same tokens.
Checks already done:
- Not the auto-mode classifier: both phases used --dangerously-skip-permissions, and 40 of 41 turns made no tool calls.
- Not a larger harness in headless: the first request of a headless session is slightly smaller than an interactive one (median ~66K vs ~88K input tokens on 2.1.267).
- Not missing transcript entries: headless
result.total_cost_usdmatches the transcript totals. - Not usage from other machines: measured and subtracted.
Claude Model
Opus
Is this a regression?
I don't know
Last Working Version
_No response_
Claude Code Version
2.1.282 (Claude Code) — also observed on 2.1.267
Platform
Anthropic API
Operating System
Ubuntu/Debian Linux
Terminal/Shell
Non-interactive/CI environment
Additional Information
Related:
- Independent measurement of the same ~1.8× gap from someone else's logs (no controlled test): https://github.com/futin/claude-agents-dashboard/issues/145 (follow-ups #80 and #157 in that repo)
- #81692 (which usage pool
claude -pdraws from; closed)
I can share the test scripts and the per-turn measurements if that helps.
Showing cached comments. Read the full discussion on GitHub ↗
3 Comments
Two follow-ups:
claude-desktop) and ours is the TUI (cli), so the difference seems to be headless vs any interactive entrypoint.<session>/subagents/) were included in our historical numbers, and the A/B test used no subagents.Re-ran the A/B on 2.1.282 through a local logging proxy (ANTHROPIC_BASE_URL), other account activity subtracted: headless 0.69 $ per 1% of the 5h window (0.59–0.82), interactive 1.59 $ (1.19–2.39). Same gap.
No hidden requests. Every headless /v1/messages call the proxy saw is in the transcript (plus the compaction call, which neither mode records). No retries, no 429/529. Interactive actually sends more requests (a max_tokens: 1 quota check and a title request).
Fewer tokens, more window. Headless used fewer tokens of every type and still more of the window.
What differs on the wire: cc_entrypoint=sdk-cli vs cli in the x-anthropic-billing-header system block and the User-Agent, and the redact-thinking-2026-02-12 beta, which only the interactive client sends. Model, effort, thinking, context_management and the other betas are identical.
Independent reproduction on a different plan and model: Pro plan, Claude Code 2.1.283,
claude-opus-5-5at--effort low, Linux, 2026-09-27 09:09 to 09:19 UTC.Both arms ran the same 31 turns. Turn 1 reads six source files of about 48 KB each with
Read; turns 2 to 31 ask for a one-word reply without tools. Both used--permission-mode default --setting-sources project, so no user hooks and no auto-mode classifier, andDISABLE_PROMPT_CACHING=1, so every turn sends about 150K uncached input tokens and the meter moves far more than its 1% step. Utilization came from a Haiku probe (claude -p --output-format stream-json --verbose,rate_limit_event.rate_limit_info.unifiedWindows.five_hour.utilization) before and after each arm, two minutes after the arm ended. Cost is from the session transcripts at list price, and for the headless arm it matchesresult.total_cost_usd. The transcripts of every machine on the account show no other Claude activity during the test.| Arm |
entrypointin transcript | List-price cost |five_hour| $ per 1% ||---|---|---|---|---|
|
claude -p --input-format stream-json|sdk-cli| $19.74 | 27% to 56% | 0.68 || Interactive TUI in tmux |
cli| $22.78 | 56% to 73% | 1.34 |Headless used the 5-hour window about 2.0 times as fast for the same work, 1.8 to 2.2 times within the 1% steps. The headless stream's own readings agree: 35% at turn 10, 45% at turn 20, 55% at turn 31. A later probe two minutes after the last arm still read 73%, so the gap is not meter lag.
A pitfall for anyone reproducing this: an interactive
claudestarted from a shell that hasCLAUDE_CODE_ENTRYPOINT=sdk-cliin its environment recordssdk-clitoo. My first run did that by accident, because the tmux server inherited the variable from an SDK-launched session. With both arms onsdk-cli, they moved the meter by the same amount: $2.51 and $2.73 of cached usage moved it 2 points each. That control is weak at 2 points, but it fits the entrypoint being what is weighted rather than something else the TUI does.Apps built on the Agent SDK record
sdk-clias well; in my case that is Paseo, where most of my Claude Code use runs.