Repository navigation
Description
Preflight Checklist
- I have searched existing issues and this hasn't been reported yet
- This is a single bug report (please file separate reports for different bugs)
- 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 "" --dangerously-skip-permissions --model claude-opus-5
and send the prompts one by one as stream-json user messages, waiting for eachresult. Recordrate_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): Explain why a headless (sdk-cli) opus request costs ~1.8x more 5h-window per token than a desktop one futin/claude-agents-dashboard#145 (follow-ups Installation mechanism besides npm? #80 and Add NPM version badge #157 in that repo)
- Authentication docs don't state which usage pool
claude -pdraws from #81692 (which usage poolclaude -pdraws from; closed)
I can share the test scripts and the per-turn measurements if that helps.
Activity
qvarker commentedon Sep 25, 2026
Two follow-ups:
The gap looks stable over time, not like a one-off repricing. In Token-value drift verdict fires on a session-surface mix shift, not a repricing futin/claude-agents-dashboard#143, splitting their data by entrypoint shows each surface flat within noise over time (−1.6% interactive, −4.1% headless), with the headless rate at ~1.8× the interactive one. Their interactive surface is the Desktop app (
claude-desktop) and ours is the TUI (cli), so the difference seems to be headless vs any interactive entrypoint.Checked two other possible causes:
<session>/subagents/) were included in our historical numbers, and the A/B test used no subagents.qvarker commentedon Sep 25, 2026
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.
Z3l20 commentedon Sep 27, 2026
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.entrypointin transcriptfive_hourclaude -p --input-format stream-jsonsdk-clicliHeadless 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.futin commentedon Sep 28, 2026
A third account, compared against the desktop app (
claude-desktop) rather than the TUI. This is passive data, not a controlled A/B. We read it off one Mac's transcripts with the usage-ledger audit in futin/claude-agents-dashboard (pnpm audit:rates -- gap).Method: every rise in
five_hourutilization is matched to the Opus 5 tokens that the transcripts show in that interval. Tokens are weighted at list price. We compare weighted tokens per 1% of the window, by the transcript'sentrypoint.claude-desktopsdk-cli(claude -p, headless)--permission-mode autoorchestrator runs with subagents, so it is a different workload mix from your fixed-prompt A/B.The headless side being dearer with the desktop app as the interactive side fits your reading that the weighting separates
sdk-clifrom any interactive entrypoint, not just fromcli.DimaVIII commentedon Sep 30, 2026
I measure a way higher usage number on my side. I spend around $120 on Claude -p and lost ~10% of my weekly x20 limit… that’s crazy! I will give detailed numbers later, track each call and spending on Claude -p and this is true! My limit is melting! I setup a few small automation task which spend $3-5 a day an I noticed that my limit was dropping way faster than usual. But was not sure why… than I setup a few bigger automation with which speed ~$100. I reload my weekly limit and 10 % weekly limit GONE!… I track in VS code my Claude code usage and a $50 eats maybe 2% Of my 5h usage. So what the hell did Anthropic did here? Should I go to OpenAI where I get a higher limit and not created in code automation on my computer?
MoralStrike commentedon Oct 5, 2026
Seeing lots of data points confirming this.
Is this a bug or deliberate? Some transparent communication would be nice.
imnakul commentedon Oct 8, 2026
@km-anthropic Kindly, take action, before it Eats up whole usage of ours... Noticed similar issue 2 weeks back also...
Adding another observational comparison from one account on Windows, covering Claude Desktop, the interactive CLI, and headless
claude -p(entrypoint: sdk-cli). All three runs used Sonnet 5.5 at low effort and started in fresh sessions.claude-desktopcliclaude -psdk-cliThe transcript audit counted each model request once, deduplicating repeated usage records by message/request identity rather than summing every transcript line. The headless run had approximately 4,806,459 cache-read tokens, 150,250 cache-write tokens, and 48,565 output tokens. The weighted figures above are estimates based on API price ratios, not a direct measurement of subscription quota accounting.
Important limitations:
With those caveats, Desktop and the interactive CLI were broadly similar when normalized by transcript-visible weighted usage (approximately 192k and 158k input-token equivalents per utilization point), while headless was approximately 43k–47k. The exact multiplier needs a controlled retest, but the observed direction is consistent with this issue.
Could Anthropic clarify whether
sdk-cliusage is intentionally weighted differently against subscription limits, or whether this is a quota-accounting bug? If intentional, please document the weighting so users can understand the impact of using headless Claude Code.imnakul commentedon Oct 8, 2026
I've also shared this issue and my observations in a thread on X:
https://x.com/NAKUL71632061/status/2108039056621965592
kler commentedon Oct 17, 2026
I measured this with a controlled A/B harness and found a clear entrypoint effect, including for the VS Code extension. Harness, method and raw data: https://github.com/kler/claude-usage-ab
Result (Team plan, Claude Code 2.1.300,
claude-opus-5-5at high effort, 2026-10-17):sdk-ts(Agent SDK apps) andsdk-cli(claude -p) used up the 5-hour limit about 2.9× as fast asclaude-vscode(the VS Code extension) for the same work. The same allowance lasts only about a third as long through them.claude-vscodeby (95% CI)sdk-tssdk-cliclaude -p, label chosen by the CLIclaude-vscodeThis doesn't depend on how token types are weighted: to move the meter by the same 6 points,
sdk-tsneeded 70 turns andsdk-cli76, whileclaude-vscodeneeded 257. Every arm had the same three session starts and ran identical turns.Full report (environment, token mix, every block): https://github.com/kler/claude-usage-ab/blob/main/results/2026-10-17T09-41-03Z-run.md
What the code shows
sdk-ts, notsdk-cli. In@anthropic-ai/claude-agent-sdk0.3.276, the process transport setsCLAUDE_CODE_ENTRYPOINT="sdk-ts"when it isn't already set.--output-format stream-json --verbose --input-format stream-json). The difference is that it setsCLAUDE_CODE_ENTRYPOINT="claude-vscode"itself. So "headless vs interactive" doesn't explain the gap: the extension is a headless stream-json session too.sdk-ts,sdk-cliandsdk-pymap to one client category (claude_code_sdk), andclaude-vscodetoclaude_code_vscode. The raw label is sent with every request ascc_entrypointin thex-anthropic-billing-headerblock.Method
sdk-ts: left to the SDK.sdk-cli: the same launch with-pand no label, so the CLI labels itself.claude-vscode:CLAUDE_CODE_ENTRYPOINT=claude-vscode, set by the harness to emulate the extension.A note for anyone measuring this. The usage lookup (
get_usage//usage) only refreshes about once a minute, and a long-lived CLI process adds its own cache on top. Measuring with it credits up to a minute of usage to the wrong arm, which makes results noisy. The 5-hour utilization in eachrate_limit_event(unifiedWindows.five_hour.utilization) changes at the request that caused it, so it's the better meter.Caveats: one account, one plan and one model so far. The
claude-vscodearm emulates the extension's label rather than driving the real extension. A run with the real extension, using the harness'swatchmode, would confirm it. Reports from other plans and models are welcome as pull requests to the repo.Could someone from Anthropic confirm whether plan usage is weighted by entrypoint, and if so, which entrypoints are weighted which way?
112 remaining items
feat: per-seat cc_entrypoint override for Anthropic metering A/B
Run Claude Code bots with the noodle entrypoint label
kvokka commentedon Nov 12, 2026
Up, this one should get a higher priority, or we should get an official answer that it's working as intended
docs: Name aimux as the client of its chats because headless sdk-cli …
Identify Claude runs as Patchwork
NomaDigitalAds commentedon Nov 12, 2026
Another data point, passive (no controlled A/B), Pro plan, Linux, Claude Code 2.1.322, claude-opus-5-5 at medium effort
I moved my daily work from the TUI to a home-made front end that drives claude -p --input-format stream-json (transcripts: sdk-cli). Same kind of work, same account, nothing else on it. API-equivalent spend from the transcripts until each "hit your limit":
That is roughly $0.80 vs $0.25 per 1% of the window, about 3×, in line with the recent Pro measurement above. Per request the headless side used less context (143K vs 187K average), and thinking share and cache TTL were the same. So this is still happening on 2.1.322. An official answer on whether sdk-* is weighted on purpose, and whether a self-identifying CLAUDE_CODE_ENTRYPOINT is acceptable, would help everyone building front ends on -p.
fix(bridge): identify to Claude as tidy-ve, not sdk-ts (TIDY-20479)
feat(claude): shadow-code and t3-code sessions count as native clients
fix(billing): report our own `pi` entrypoint instead of `sdk-cli`
fix(billing): report our own `pi` entrypoint instead of `sdk-cli`
fix(billing): report our own `pi` entrypoint instead of `sdk-cli`
Claude threads label their requests threadbear on a subscription login
fix(claude): Sikemux chats leave out Claude Code's claude.ai Artifact…
fix(claude-cli): label Claude CLI turns as opencodex (carries #7837) (#…
fix(directsdk): run native as hermes-agent so subscriptions meter it …