Claude statusline: server-truth quota vs local context tokens

The JSON Claude Code hands your statusLine script knows the local context window and nothing else. The float that 429s your next prompt lives on a different host, behind a different cookie, in a different JSON tree. Here is how to put both on one line.

Direct answer (verified 2026-05-22)

No, not out of the box. Claude Code pipes a JSON object over stdin containing only model, workspace, cost, and context_window.used_percentage. Server-truth quota lives at claude.ai/api/organizations/{org}/usage under a separate cookie scope. To render server-truth in the bar, your statusLine script has to shell out to a process that polls that endpoint, like claude-meter --json, and concatenate its output. A working recipe is below.

Step 1: see what the statusline is actually given

Claude Code's statusLine is a command you write that reads JSON from stdin and prints one line of text. The JSON it hands you on each tick looks like this:

stdin → ~/.claude/statusline.sh

Note what is missing. No five_hour. No seven_day. No resets_at. The CLI cannot give you those because the API key it was started with does not see them, and the cookies that do are on a different host. The bar at the bottom of the terminal is a window into the client; it has to stay that way unless your script reaches further.

Step 2: see what server-truth actually carries

The float Anthropic rate-limits on is published at claude.ai/api/organizations/{org}/usage, the same endpoint claude.ai/settings/usage renders its bars from. claude-meter --json polls that endpoint through your existing browser session and prints the snapshot. This is the shape:

$ claude-meter --json

Step 3: the bridge in one bash script

The default recipe everyone copies prints model + context %. The server-aware version shells out once per tick to pull the 5h and 7d utilization. Both scripts run in the same statusLine slot; the only difference is the four lines in the middle:

Default statusLine script vs server-aware statusLine script

#!/usr/bin/env bash
# ~/.claude/statusline.sh:the default-ish recipe everyone copies
# Reads stdin JSON, prints model + context %.

input=$(cat)
model=$(echo "$input" | jq -r '.model.display_name')
ctx=$(echo "$input" | jq -r '.context_window.used_percentage // 0')

printf "[%s] %.0f%% ctx" "$model" "$ctx"

# Renders something like:  [Sonnet 4.6] 44% ctx
# Tells you nothing about server quota. Will not save you from a 429.
-83% extra lines for the bridge

The 2>/dev/null and the if [ -n "$server" ] guard matter: if claude-meter is not installed, the menu-bar app is paused, or your claude.ai session has expired, the script falls through to the local fragment instead of leaving a stale stub on the bar.

Step 4: point Claude Code at the script

One block in ~/.claude/settings.json tells Claude Code to invoke your script as a command, pipe the JSON over stdin, and render whatever single line it prints back:

~/.claude/settings.json

Restart Claude Code. The bar updates within a tick. If the line is blank, run the script by hand with echo '{"model":{"display_name":"Sonnet"},"context_window":{"used_percentage":10}}' | ~/.claude/statusline.sh to find the failure.

What the bar looks like once it works

Same Claude account, two configurations, same wall clock:

Before vs after, one refactor session

What each surface can and cannot tell you

The bridge is not "claude-meter is better than the stdin JSON". They measure different things. The default statusline reads what the CLI can see; the shell-out reads what the rate limiter checks. You need both in the same line:

Featurestdin JSON (local context)claude-meter --json (server-truth)
Sourcestdin JSON Claude Code pipes to your script (context_window.used_percentage)claude.ai/api/organizations/{org}/usage (server JSON)
Numbers exposedcontext tokens used / max onlyfive_hour, seven_day, seven_day_sonnet, seven_day_opus
Sees attachments + tool calls + browser chats?no (only the current process's local tokens)yes (already weighted in the float)
Knows about peak-hour multiplier?noyes (applied server-side before utilization is returned)
Knows about reset time?noyes (resets_at on each window)
Predicts a 429 before it lands?neveryes
Update cadenceevery Claude Code render tickonce per shell-out (~60s recommended via claude-meter cache)

The one fact the rest of the internet keeps glossing over

Most statusline guides for Claude Code copy the same jq -r '"[\(.model.display_name)] \(.context_window.used_percentage // 0)% context"' one-liner. That number cannot, by construction, see your 5-hour bucket. The CLI process holds an API key for api.anthropic.com; the consumer-plan quota lives behind a session cookie on claude.ai. Two hosts, two cookies, two JSON trees. To bridge them, exactly one shell-out per tick has to cross the gap, and that shell-out belongs in the same script that already prints the local fragment.

Want me to wire this into your own statusline live?

Twenty minutes. Bring your shell of choice and a claude.ai tab. We open settings.json, drop in the script, and watch the bar pick up server-truth on your account.

Frequently asked

Why can't the Claude Code statusline show server quota by default?

Because the JSON Claude Code pipes to your statusLine script over stdin is computed entirely inside the local CLI process. The shape is { model, workspace, cost, context_window } with context_window.used_percentage being the only utilization field, and that is the local 200K context window, not the server-side rolling 5-hour bucket. Claude Code itself only talks to api.anthropic.com (or the Claude Code endpoint) with the API key it was started with. The consumer-plan quota lives on claude.ai under a different cookie scope. Nothing inside the CLI's render loop has the cookies, the URL, or the JSON path it would need to surface that float. The status line is a window into the client; to bridge it to the server, the script has to shell out.

What does claude-meter --json actually return?

An array of UsageSnapshot rows, one per detected Claude account, defined in main.rs at lines 8-10 (the --json flag) and printed at line 87. Each row carries account_email, org_uuid, the browser the cookies came from, and a usage object with five Window children: five_hour, seven_day, seven_day_sonnet, seven_day_opus, plus an extra_usage object for metered overage. Each Window is { utilization: f64, resets_at: ISO timestamp }. The values are read from claude.ai/api/organizations/{org}/usage, the same endpoint that powers the bars at claude.ai/settings/usage. Sourcing the data takes one HTTPS request per minute on the menu-bar polling cadence; the CLI itself reads the cached snapshot, so the shell-out per status-line tick is cheap.

How do I wire this up end-to-end on a fresh machine?

Three commands and one config edit. (1) `brew install --cask m13v/tap/claude-meter` to install the menu-bar app, then visit claude.ai once so the browser extension forwards your session. (2) Create ~/.claude/statusline.sh with the server-aware script in the recipe section above and `chmod +x` it. (3) Edit ~/.claude/settings.json to add a statusLine block of type 'command' pointing at the script. Restart Claude Code. The bar will read `[Sonnet 4.6] 44% ctx • 17%/5h 22%/7d-opus` on a quiet account and climb visibly as the rolling window fills.

Won't a shell-out on every render tick make my terminal sluggish?

Not in practice. claude-meter caches the last server snapshot on disk between invocations because the polling cadence is 60 seconds, not per-tick. A `claude-meter --json` call when the cache is warm returns in roughly 25-40 ms on a current MacBook, well under the threshold where you'd notice latency in the status line. If you want belt-and-suspenders, wrap the call in a 200 ms timeout and fail open: print the local fragment when the bridge is unreachable. The script in the recipe section already does this.

Why not just sum tokens from ~/.claude/projects/<session>.jsonl and pretend that's quota?

Because the rate-limit float is weighted in five ways the JSONL cannot see. Peak-hour multiplier fills the 5-hour bucket faster during US Pacific weekday midday hours. Attachments carry a per-PDF cost on top of their token count. Tool calls (code execution, browsing) carry server cost on top of tokens. Per-model weight makes Opus burn the bucket faster than Sonnet for the same byte count. Browser-chat usage on claude.ai counts against the same account but never lands in the JSONL. A statusline that reads only the local sum will say 5% while the next prompt 429s. The point of surfacing five_hour.utilization is to read the function the rate limiter is actually computing.

Does this work on Windows or Linux?

The browser extension does. The macOS menu-bar app does not (the README is explicit: macOS 12+). On Windows or Linux the extension still polls /api/organizations/{org}/usage from your Chrome/Brave/Edge/Arc profile and exposes the snapshot via the chrome.action API. To pipe it into a statusline, a small companion script can read the extension's exposed snapshot (or call the endpoint directly with your own cookie store) and emit the same string the bash recipe above prints. The bridge contract is identical; only the source process changes.

Does it work with Starship, tmux, fish prompt, Fig?

Yes, same pattern. Any prompt or status surface that can run a command and embed its stdout works. In Starship, add a custom module with `command = 'claude-meter --json | jq -r ...'` and set a sane `format`. In tmux, set `status-right` to a similar shell-out. The Claude Code statusLine is just the most useful target because it renders the local context % right next to where the server-truth float belongs.

If I already run ccusage, do I need this?

ccusage and claude-meter answer two different questions, so they coexist. ccusage walks ~/.claude/projects/*.jsonl and gives you cost attribution per project: which directory tree burned how many tokens this week. The server JSON has no idea where on disk your code lives. claude-meter reads the float the rate limiter checks: will my next prompt 429. Heavy users run both, usually with ccusage in a terminal split for spend reporting and claude-meter feeding the statusline (or menu bar) for live quota.

How did this page land for you?

React to reveal totals

Comments ()

Leave a comment to see what others are saying.

Public and anonymous. No signup.