Search topic
claude code statusline usage limits
Practical outcome
A tested status line script for rate_limits, resets_at, cost and context, in bash+jq or Node.
Quick answer
statusLine.command in ~/.claude/settings.json at it. The fields you want are rate_limits.five_hour.used_percentage and rate_limits.seven_day.used_percentage, each with a resets_at Unix timestamp, plus cost.total_cost_usd and context_window.used_percentage. The rate-limit fields only exist for Claude Pro, Max and Ultra subscribers, or behind a Claude apps gateway with a spend limit. They also only show up after the first API response in a session. π¦Claude Code's status line is a row at the bottom of the interface. It runs any command you configure and prints whatever that command writes to stdout. It runs locally and uses no API tokens. Everything below is based on Anthropic's status line documentation as fetched on November 8, 2026. Both scripts were run on that date, on macOS with bash 3.2, jq 1.7.1 and Node 24, against sample JSON built from the documented schema. The output shown is what they printed.
Session JSON
model, cost, context, rate_limits
Your script
reads stdin, prints stdout
Status line
shown above the footer
Which status line fields hold the limits?
Each window can be missing on its own. Claude Code removes a window once its resets_at has passed. So a script that assumes rate_limits is always there will print junk or crash on an API key, on a fresh session, and right after a reset. Both scripts below simply leave out a segment whose field is missing.
cost.total_cost_usd is not your subscription bill
Claude Code works out the cost on your machine at API list price, unless amodelPricing table is set. On Pro, Max or Ultra you pay a flat price, so read it as βwhat this session would cost on the APIβ. It is still a good way to compare sessions with each other.The bash and jq script
This one makes a single jq call. jq's strflocaltime turns the reset timestamps into your local time, so the script behaves the same on macOS and Linux and never needs the two different date flags. Each limit gets a π’ below 50%, a π‘ from 50% and a π΄ from 80%. The pipe-separated read keeps empty fields in place, which a tab separator would merge.
#!/usr/bin/env bash
# ~/.claude/statusline.sh: plan limits, session cost and context for Claude Code.
# Needs jq 1.6 or newer. Claude Code pipes the session JSON to stdin.
input=$(cat)
IFS='|' read -r MODEL CTX COST FIVE FIVE_AT WEEK WEEK_AT <<<"$(jq -r '
def pct: if . == null then "" else round | tostring end;
def at(f): if . == null then "" else strflocaltime(f) end;
[ (.model.display_name // "Claude"),
(.context_window.used_percentage | pct),
(.cost.total_cost_usd // 0 | tostring),
(.rate_limits.five_hour.used_percentage | pct),
(.rate_limits.five_hour.resets_at | at("%H:%M")),
(.rate_limits.seven_day.used_percentage | pct),
(.rate_limits.seven_day.resets_at | at("%a %H:%M")) ] | join("|")' <<<"$input")"
dot() { # traffic light for a 0-100 percentage
if [ "$1" -ge 80 ]; then printf 'π΄'; elif [ "$1" -ge 50 ]; then printf 'π‘'; else printf 'π’'; fi
}
LINE="[$MODEL] ctx ${CTX:-0}% | $(LC_NUMERIC=C printf '$%.2f' "$COST")"
[ -n "$FIVE" ] && LINE="$LINE | 5h $(dot "$FIVE") $FIVE% resets $FIVE_AT"
[ -n "$WEEK" ] && LINE="$LINE | 7d $(dot "$WEEK") $WEEK% resets $WEEK_AT"
echo "$LINE"- 1
Save and make it executable
Save the file as~/.claude/statusline.sh, then runchmod +x ~/.claude/statusline.sh. Install jq first if you don't have it. - 2
Register it in settings
Add thestatusLineblock below to~/.claude/settings.json. Claude Code reloads settings and runs the new command straight away. - 3
Send one message
The limit segments appear after the first API response of the session, not at startup.
{
"statusLine": {
"type": "command",
"command": "~/.claude/statusline.sh"
}
}Test it before Claude Code runs it
Anthropic recommends testing with mock input. This sample copies the values from the documentation's full JSON example, trimmed to the fields the script reads. TZ=UTC pins the reset times so you can compare. Leave it off to get your own local time.
{"session_id":"abc123","model":{"id":"claude-opus-5-5","display_name":"Opus"},
"workspace":{"current_dir":"/current/working/directory"},
"cost":{"total_cost_usd":0.01234,"total_duration_ms":45000},
"context_window":{"context_window_size":200000,"used_percentage":8,"remaining_percentage":92},
"rate_limits":{"five_hour":{"used_percentage":23.5,"resets_at":1738425600},
"seven_day":{"used_percentage":41.2,"resets_at":1738857600}}}$ TZ=UTC ~/.claude/statusline.sh < sample.json[Opus] ctx 8% | $0.01 | 5h π’ 24% resets 16:00 | 7d π’ 41% resets Thu 16:00$ TZ=UTC ~/.claude/statusline.sh < heavy.json[Opus] ctx 67% | $4.88 | 5h π΄ 86% resets 16:00 | 7d π‘ 55% resets Thu 16:00$ ~/.claude/statusline.sh < apikey.json[Sonnet] ctx 0% | $0.40
heavy.json is the same shape with higher numbers (86.1% and 55%, $4.876, 67.4% context). apikey.json has no rate_limits object and a null context percentage, the way an API-key session looks before its first reply. 23.5% rounds to 24%. If you would rather never overstate, swap round for floor.
The Node version, no jq needed
Use this if you don't want jq, or on Windows. There Claude Code runs status line commands through Git Bash when it is installed, otherwise PowerShell, and Anthropic says to write paths with forward slashes. Save it as ~/.claude/statusline.mjs, run chmod +x on it, and set "command": "~/.claude/statusline.mjs". On Windows, use "node C:/Users/you/.claude/statusline.mjs".
#!/usr/bin/env node
// ~/.claude/statusline.mjs: same output as statusline.sh, no jq needed.
let raw = "";
process.stdin.on("data", (chunk) => (raw += chunk));
process.stdin.on("end", () => {
const d = JSON.parse(raw);
const pad = (n) => String(n).padStart(2, "0");
const time = (s) => { const t = new Date(s * 1000); return `${pad(t.getHours())}:${pad(t.getMinutes())}`; };
const day = (s) => new Date(s * 1000).toLocaleDateString("en-US", { weekday: "short" });
const dot = (p) => (p >= 80 ? "π΄" : p >= 50 ? "π‘" : "π’");
const model = d.model?.display_name ?? "Claude";
const ctx = Math.round(d.context_window?.used_percentage ?? 0);
const cost = (d.cost?.total_cost_usd ?? 0).toFixed(2);
const parts = [`[${model}] ctx ${ctx}%`, `$${cost}`];
const five = d.rate_limits?.five_hour;
if (five?.used_percentage != null) {
const p = Math.round(five.used_percentage);
parts.push(`5h ${dot(p)} ${p}% resets ${time(five.resets_at)}`);
}
const week = d.rate_limits?.seven_day;
if (week?.used_percentage != null) {
const p = Math.round(week.used_percentage);
parts.push(`7d ${dot(p)} ${p}% resets ${day(week.resets_at)} ${time(week.resets_at)}`);
}
console.log(parts.join(" | "));
});Against the same three files, the Node script printed exactly the same three lines as the bash version. Both scripts exit with code 0, which matters: Claude Code only shows a status line after the script exits successfully. A script that fails, or prints nothing, leaves the row blank.
When does the status line refresh?
The script runs when a session starts or resumes, and again after each new assistant message, when /compact finishes, when the permission or vim mode changes, and when a rate-limit window in the last data reaches its resets_at. Updates are debounced by 300 ms. Because these scripts show the clock time of the reset rather than a countdown, they never go stale while you are idle. If you change them to print βresets in 42mβ, also set refreshInterval (in seconds) so the countdown keeps moving.
Will I run out before the reset?
A percentage alone doesn't tell you if you are on track. Compare it with how much of the window has gone by. For the 5-hour window, the time already used is five hours minus the time until resets_at:
Example: 60% used with two hours left means three hours have passed, so 60 Γ 5 Γ· 3 = 100%. You are on course to hit the limit right at the reset. Treat it as a rough guide only, because the cost of each message depends on model, context size and effort.
Status line, /usage or /context?
That is why /context can read a little higher than your status line until the next reply arrives. You can also skip writing a script: type /statusline followed by a description, such as βshow 5-hour and weekly usage with reset timesβ, and Claude Code generates one in ~/.claude/. Run /statusline delete to remove it, or delete the statusLine key. If the row stays blank, accept the workspace trust prompt, or run claude --debug to see your script's stderr.
Where whoburnedmore fits
The status line shows the present: how full the current window is and when it resets. It forgets everything at the next reset. whoburnedmore does the other half. It is the history: tokens and estimated cost per day, by tool and model, across Claude Code and the other agents it reads. It does not read your live limits or remaining quota. Use the status line to pace today, and the daily history to see which days and models filled last week's window. Run it with the whoburnedmore CLI command, npx whoburnedmore, or keep it fully offline with npx whoburnedmore --local. For how the windows themselves work, see Claude Code rate limits explained. For what to do once you hit one, read usage limit reached. β±οΈ
Sources checked November 8, 2026: Claude Code: customize your status line, Claude Code commands reference (/usage, /statusline), and local test runs of both scripts with bash 3.2.57, jq 1.7.1 and Node 24.13.
Related guides
Claude Code Rate Limits, Explained
The weekly and 5-hour caps demystified β plus how to see your real utilization.
Claude Code Usage Limit Reached? Here's What to Do
Locked out mid-session? See what you used and when the limit resets.
Why Your Claude Code Context Window Fills Up
What eats your context window β and how token totals reveal it.