Claude Code tracks your 5-hour session and 7-day weekly rate limits internally, and it exposes those numbers via its own /usage command. But there's a lesser-known mechanism — statusLineHook — that lets you capture that data automatically without polling an API or running a command. Here's how it works.
Claude Code reads ~/.claude/settings.json on startup. One of its config keys is statusLineHook — a shell command string that Claude Code runs every time it updates its status line. The usage data is passed as an environment variable: HOOK_STATUS_LINE.
The value of HOOK_STATUS_LINE is a JSON string with rate-limit and session information. A typical value looks like:
{
"sessionUsagePct": 12.4,
"weeklyUsagePct": 67.1,
"contextUsagePct": 8.2,
"model": "claude-sonnet-4-6",
"costUSD": 1.24,
"sessionResetAt": "2026-06-11T18:42:00Z",
"weeklyResetAt": "2026-06-14T00:00:00Z"
}
These are the same numbers Claude Code renders when you run /usage — straight from its internal rate-limit state, updated in real time as each prompt is processed.
To capture this data, add a statusLineHook to ~/.claude/settings.json. The simplest possible hook writes the JSON to a file:
{
"statusLineHook": "printf '%s' \"$HOOK_STATUS_LINE\" > ~/.claude/usage.json"
}
After saving and starting a new Claude Code session, the file ~/.claude/usage.json will be updated every time Claude Code processes a prompt. You can read it with:
cat ~/.claude/usage.json | jq .
jq '.sessionUsagePct' ~/.claude/usage.json # session %
jq '.weeklyUsagePct' ~/.claude/usage.json # weekly %
The typical approach to monitoring Claude Code usage is to call the Anthropic usage API. That requires an API key, makes network requests, and runs the risk of your monitoring tool contributing to its own rate limits.
The hook approach has none of these downsides:
You can verify there's no network activity from any hook-based tool with:
nettop -p Headroom # or whatever your tool is named
If you already have a statusLineHook configured, Claude Code respects it — the hook field is a string, so you'd need to chain commands with && or ;. Tools that install hooks responsibly should check for an existing hook first and append to it rather than overwriting.
settings.json before writing, and if a statusLineHook already exists, append your command with ; your-command rather than replacing the existing hook. Claude Code runs the hook as a shell command, so chaining with ; (or && if you want short-circuit behavior) works correctly.
The hook mechanism unlocks a class of tools that were previously impractical because they required polling:
costUSD across sessions with a simple append-and-sumA minimal tmux statusbar segment, for example:
# In .tmux.conf:
set -g status-right '#(jq -r '"'"'if .sessionUsagePct then "CC (.sessionUsagePct | round)%·(.weeklyUsagePct | round)%" else "" end'"'"' ~/.claude/usage.json 2>/dev/null)'
CC 12%·67% — color-coded as limits approach.
Free, MIT, zero config.
brew install --cask patwalls/tap/headroom
Fields you can count on in HOOK_STATUS_LINE:
sessionUsagePct // float, 0–100, 5h session window
weeklyUsagePct // float, 0–100, 7d weekly window
contextUsagePct // float, 0–100, current context fill
model // string, active model name
costUSD // float, session cost if tracked by your plan
sessionResetAt // ISO 8601, when the 5h window resets
weeklyResetAt // ISO 8601, when the 7d window resets
Not all fields are present in every update — check for existence before using. costUSD may be 0 for plans that don't expose per-session cost.
The Headroom app is open-source and shows a complete reference implementation of the hook + file-read approach: github.com/patwalls/headroom.
The relevant files are app/Sources/Headroom/Hook.swift (installs the hook) and app/Sources/Headroom/Usage.swift (reads and parses the JSON).