statusline: expose per-model weekly rate limits (Opus/Sonnet/Fable) to the status line command
Feature request
Expose the per-model weekly rate limits (Opus / Sonnet / Fable) to the status line command's stdin JSON, so custom status lines can display the same meters that /status shows.
Current behavior
The status line JSON only ever includes two entries under rate_limits:
"rate_limits": {
"five_hour": { "used_percentage": ..., "resets_at": ... },
"seven_day": { "used_percentage": ..., "resets_at": ... }
}
Internally, Claude Code already tracks more than this. The rate-limit object is populated from the anthropic-ratelimit-unified-* response headers into these keys:
five_hourseven_dayseven_day_overage_included← labeled "Fable 5 limit" in the UIoverage
and the UI additionally distinguishes seven_day_opus ("Opus limit") and seven_day_sonnet ("Sonnet limit").
But the function that assembles the status line payload spreads only five_hour and seven_day, dropping the model-specific weekly limits (observed in 2.1.199, minified, paraphrased):
rate_limits = {
...v.five_hour && { five_hour: { used_percentage: v.five_hour.utilization * 100, resets_at: v.five_hour.resets_at } },
...v.seven_day && { seven_day: { used_percentage: v.seven_day.utilization * 100, resets_at: v.seven_day.resets_at } },
// seven_day_opus / seven_day_sonnet / seven_day_overage_included are not forwarded
};
So /status can show "Current week (Fable) — 60% used, resets …", but a custom status line has no way to read that number. It isn't persisted to disk anywhere either — the only source is the in-memory header parse.
Requested behavior
Forward the model-specific weekly limits to the status line JSON as well, using the same { used_percentage, resets_at } shape, e.g.:
"rate_limits": {
"five_hour": { "used_percentage": ..., "resets_at": ... },
"seven_day": { "used_percentage": ..., "resets_at": ... },
"seven_day_opus": { "used_percentage": ..., "resets_at": ... },
"seven_day_sonnet": { "used_percentage": ..., "resets_at": ... },
"seven_day_overage_included": { "used_percentage": ..., "resets_at": ... }
}
Each key would be present only when the corresponding limit is active, mirroring the existing conditional-spread pattern. This looks like a one-line change in the payload builder (the data is already in the object being spread from).
Why
Fable 5 has a dedicated weekly limit that's separate from the general 7-day window. Users who track their usage in a custom status line currently can't surface it, even though the client already has the value and shows it in /status.
Environment
- Claude Code
2.1.199 - Status line configured via
settings.json→statusLine.command
Showing cached comments. Read the full discussion on GitHub ↗
3 Comments
Coming over from #77846 (closed as a duplicate of this one), so the verified findings from that thread live in one place.
The
7d_oiheader does ship in production, but only on Fable responses. That's theseven_day_overage_includedkey this issue asks for. From @d-mato's independent probing (d-mato/claude-usage-bar#2):7d_oi0.65 next to7d0.36 on the same response, still there after the 2026-07-20 billing transition, and matching theweekly_scopedFable meter from the usage endpoint. Haiku and Opus responses don't carry it.So the fix proposed here is real and cheap, but conditional: a session only sees
7d_oiwhile it's actually sending Fable requests. The question it can't answer is the one most people are asking: how much Fable headroom is left before I switch.Claude Code's other source doesn't have that gap. The
get_usagecontrol request returns the shape statusLine would need (verified on 2.1.218 and 2.1.220):display_namecomes from the server, so there's no model list to keep in sync client side. And forwarding it doesn't have to mean a fetch at render time: Claude Code already caches the same numbers in~/.claude.json(cachedUsageUtilization,limits[]entries withkind: "weekly_scoped", plus afetchedAtMsto age them out).Meanwhile the ecosystem is working around it. claude-hud renders
model_scopedin full (jarrodwatts/claude-hud#669) and reads it from aget_usage-fed snapshot (#690, shipped in v0.7.0), with acachedUsageUtilizationfallback open in #720. claude-usage-bar switched its whole data source toget_usagein v0.5.0. The headless VPS setup on #77846 and the desk meters on #81015 are burning a second credential, or a throwaway Haiku call, just to read a percentage. All of it retires the day stdin carries the scoped windows.Still reproduces on 2.1.259 (win32, Max). Adding the current code-level state, because the statusline payload builder has been touched since the 2.1.199 snapshot in the issue body —
model_scopedjust wasn't carried along with it.1.
model_scopedis now a declared field on the other surface, not merely an observed responseThe status/SDK schema in 2.1.259 defines it inline alongside the other windows:
This promotes @kuk1song's
get_usageobservation from 2.1.218/2.1.220 into a committed shape, and it settles the "which models, and who keeps that list in sync" question: the server supplies both the label and the allowlist filter, so there is nothing model-specific to maintain client side.2. The projection from
limits[]tomodel_scopedalready exists in-processSame build, the function returning
{ session, subscription_type, rate_limits_available, rate_limits, behaviors }builds and attaches it (minified, paraphrased):So the ask needs no new fetch, no new cache, and no new schema — the mapping is written, guarded, and already running.
3. The statusline builder is a separate function that reconstructs
rate_limitsfrom scratchThree branches, no fourth.
spend_limitis the new one relative to the 2.1.199 snapshot above — this is not a frozen code path. The two surfaces have simply diverged: one declares and populatesmodel_scoped, the other assembles a parallel object that structurally cannot contain it.4. Shape of the fix
Reuse the existing projection and widen the emit guard:
Consumers are already written against exactly this key. claude-hud parses
rate_limits.model_scopedstraight from stdin (parseScopedWindows) and renders it with no client-side model list — any non-emptydisplay_nameis accepted, so "Fable 5.1" and any future bucket need zero plugin changes. Today that path is simply unreachable from stdin, and the plugin falls back to the externalget_usage-fed snapshot @kuk1song described.Repro on 2.1.259
/usagerenders "Current week (Fable) — 69% used, resets Sep 3, 9:59pm" while the statusline payload for that same session carriesrate_limitswith onlyfive_hourandseven_day. The all-models weekly meter appears in the status line; the Fable one — the binding constraint — is absent.On 2.1.260 (macOS) this account gets only
five_hourandseven_dayin thestatusline
rate_limits-- nomodel_scoped, and noseven_day_opusorseven_day_sonneteither -- while/usageshows the Fable weekly bar for thesame session. That is what the three-branch builder above predicts: the object
is rebuilt from scratch, so it can only carry the keys those branches spread.
Worth flagging because #88137 says the per-model keys are already delivered, and
proposes adding
seven_day_fablebeside them. If the builder has always hadthree branches, there is nothing there to extend.
Small note for consumers: the schema says
utilizationand an ISOresets_at,but stdin sends
used_percentageand an epoch.