Repository navigation
[BUG] Headless claude -p (entrypoint sdk-cli) uses ~1.8× more of the 5-hour window per token than interactive cli, same workload #97074
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?
52 remaining items
offyotto commentedon Oct 28, 2026
Feels like Anthropic is almost purposely doing this.
ladieman217 commentedon Oct 28, 2026
It's a feature, not bug
MaxForAI commentedon Oct 28, 2026
Damn, no wonder my token consumption is growing as fast as Anthropic’s valuation.
Relabel headless Claude entrypoints on subscription traffic
feat(launch): add opt-in entrypoint relabeling via CPROXY_ENTRYPOINT (#…
albertofa commentedon Oct 28, 2026
Data point on how the entrypoint label gets set, from a T3-Code-style desktop app that drives the real
claudebinary headless (Windows 11, Claude Code 2.1.312, Haiku).entrypointin transcriptclaude -p --output-format stream-json --verbose --input-format stream-json --permission-prompt-tool stdiosdk-cli-pstream-json flags, standalone,CLAUDE_CODE_ENTRYPOINTunsetsdk-cliCLAUDE*env vars strippedcli-palone selectssdk-cli, even when the caller sets nothing. Any desktop or GUI wrapper that runs the CLI headless to get structured output and approvals over stdio falls into the faster-draining group. There's no headless way to be billed likeclishort of setting the label by hand, which we don't want to do without guidance.claudestarted from a shell that has inheritedCLAUDE_CODE_*variables (hereCLAUDE_CODE_CHILD_SESSIONandCLAUDE_CODE_ENTRYPOINT=sdk-cli) also recordssdk-cli, and it shows "Transcript saving is off — inherited CLAUDE_CODE_CHILD_SESSION marker". Anyone running an A/B from a terminal inside a Claude session should strip these first.Question for Anthropic: is
sdk-cliusage meant to be weighted differently against subscription limits? Is there a supported way for a GUI that wraps the CLI to identify itself, the way the VS Code extension setsclaude-vscode? If the weighting is intentional, please document it so tool authors can tell users what it costs.Peteralm commentedon Oct 28, 2026
Guys, again.
The 5h usage limit by
claude pcan be fixed.If you change the identity to anything else, it's fixed. The bug only applies to sdk-cli
Here's a t3code PR: pingdotgg/t3code#25597
elmariodev commentedon Oct 28, 2026
One concern before people roll it out widely: the fix works with any arbitrary name, so it doesn't fix the bug, it just hides that the requests come from headless "claude -p". "CLAUDE_CODE_ENTRYPOINT" is undocumented, and if the extra 5h weighting for "sdk-cli" is intentional, changing the label could count as getting around usage limits under the Consumer Terms. The real risk is getting your account banned. Could someone from Anthropic confirm whether the "sdk-cli" weighting is a bug or intended, and whether relabeling the entrypoint is allowed? Until then I'd hold off on merging this or setting it in settings.json.
Label the child client so plan usage is not billed as SDK traffic (#15)
feat(daemon): Claude Code 以 memento 身份运行,新会话能在终端 claude --resume 接着用
achapla commentedon Oct 29, 2026
Another reproduction, this time on Windows and with a tool-heavy task: Pro plan, Claude Code 2.1.312,
claude-opus-5-5at medium effort, Windows 11, 2026-10-29 04:12–04:40 UTC (outside US peak hours).claude -p(sdk-cli) used the 5-hour window about 2.1× as fast as the interactive TUI (cli) for the same work.five_hourclaude -p --output-format jsonsdk-cliclicliclaude -p --output-format jsonsdk-cliAverage per run:
-pMethod. One fixed, read-only prompt with 15 named steps, one tool call per step (
Read,Grep,Glob,Bash(wc:*)), over public-domain book chapters and two small code files. So every turn uses a tool, unlike the one-word-reply workloads above. Both modes get the same prompt, the same--allowedToolsand the same folder. Token counts come from the session transcripts (deduplicated by message id + request id). For-p, the transcript totals matchtotal_cost_usd. Interactive cost is estimated from its tokens at the same price per token. Utilization comes from/api/oauth/usage, read before each batch and 60 s after it. The batch order is p, interactive, interactive, p, so neither mode always runs first. The account was otherwise idle.-pwrote about 10k more cache tokens per run, which makes its list-price cost about 6% higher. That cannot explain using 2× the window.Comparing across plans. Our $ per 1% looks low next to the numbers above, but 1% of Pro is a smaller amount than 1% of Max. @elmariodev's Max 5x numbers divided by 5 give $0.21 for SDK and $0.48–0.49 for interactive, almost the same as ours. Across this thread the ratio is stable (about 1.8–2.9×), but the absolute $ per 1% changes with plan and token mix. So the ratio is the number to compare.
Repo to reproduce: https://github.com/achapla/claude-p-limit-bench
pnpm install && pnpm bench run --runs 7 --rounds 2 && pnpm bench report --markdown. It works on Windows, macOS and Linux, and it drives a real interactive TUI through node-pty, so it needs no tmux. The report prints your setup, and there is an issue form for sharing results. The raw data for the table above is indata/.Limits: one account, one plan, one model, outside peak hours only. The weekly reading moved 79% → 84% over all four batches, which is too coarse to compare the modes.
Like the others here: if
sdk-cliusage is meant to count differently, please document it. If not, this looks like a metering bug.Keep plan billing by sending a plain docs pointer (#29)
Set CLAUDE_CODE_ENTRYPOINT to monocode for Claude processes
feat(t3): label T3's Claude sessions as t3-code
harry924 commentedon Oct 30, 2026
Another reproduction, Max 20x, macOS, Claude Code 2.1.312,
claude-opus-5-5, run 2026-10-30 00:40–03:24 ACDT. It adds three things not yet in this thread: a controlled test of the real Claude Desktop app, a CLIProxyAPI test, and a pooled look at the weekly limit.Result: for the same work,
sdk-cliused the 5-hour window about 2.5× as fast as either the TUI (cli) or the Desktop app's Code tab (claude-desktop).API-equivalent spend per 1% of the 5-hour window:
five_hourclaude -p(sdk-cli)cli)claude -p(sdk-cli)claude-desktop)claude -p→ CLIProxyAPI (sdk-cli, passed through)cli)claude -pdirect, control (sdk-cli)Allowing for the meter's 1-point resolution, the ratios are about 2.0–3.4× for the TUI round and 1.8–3.3× for the Desktop round.
Method
DONE. The TUI round also had a few Grep/Glob/wcsteps.-psessions reused each other's prompt cache and TUI sessions didn't, which confounds the comparison if cache writes are weighted differently. With it, both arms wrote about 155k tokens of 1-hour cache per run and made the same number of API calls.rate_limit_event.unifiedWindowsvia a tiny Haiku probe before each phase and 60 s after it.total_cost_usd: $4/M input, $20/M output, $0.20/M cache read, $8/M 1-hour cache write. The fit matches the reported totals to the cent.-pcontrol used--effort high --permission-mode auto. Desktop's Code tab has no Grep/Glob tools, which is why that round only uses Read.CLAUDE*environment variables, and every transcript shows the expectedentrypoint.Desktop:
claude-desktopis metered like the TUI. That fits the earlier VS Code finding: the weighting follows the label category, not whether the session is headless.CLIProxyAPI: this doesn't help Claude Code-based apps. With the proxy's request log on, real
claude -ptraffic went upstream unchanged, withUser-Agent: claude-cli/2.1.312 (external, sdk-cli)andcc_entrypoint=sdk-cli, and drained at thesdk-clirate. The proxy only rewrites requests from non-Claude-Code clients to look likeclaude-cli/... (external, cli). Those were metered like the TUI. Like relabeling withCLAUDE_CODE_ENTRYPOINT, that hides the label rather than fixing anything, and it may not be allowed under the terms.Weekly limit: probably not weighted. Pooled across all four rounds:
seven_daypointssdk-cli(including via the proxy)cli, proxy-relabeledcli,claude-desktop)If the weekly limit had the same ~2.5× weighting, the
sdk-cligroup should have used about 5 of the 10 points. Equal weighting predicts about 3. The meter is coarse, but this fits "the 5-hour window only" much better. On this plan that's roughly $320 of work per 5-hour window through the SDK, versus about $845 interactive, against a weekly total of roughly $4,000 either way.The tests were run with Claude Code's help. Like everyone else here: please confirm whether
sdk-*entrypoints are meant to be weighted differently against the 5-hour window, and if they are, please document it.