Expose rate_limits.model_scoped (per-model weekly windows, e.g. Fable) in statusLine stdin
Feature request
Include the model_scoped array in the rate_limits object passed to statusLine scripts via stdin.
Context
Since v2.1.80 the statusLine JSON includes rate_limits.five_hour / rate_limits.seven_day, which made plan-level usage visible to custom statuslines. However, model-scoped weekly windows (e.g. the Fable weekly quota that /usage displays as its own limit) are not included, so statusline tools cannot show how much of a premium-model allocation remains.
Claude Code already has this data. The internal status schema (visible in the bundled binary) defines:
model_scoped: array({
display_name: string, // "Server-supplied label for the model bucket (e.g. 'Fable')."
utilization: number|null, // 0-1 fraction of the weekly window
resets_at: string|null, // ISO-8601
})
and it is already attached to rate_limits on another internal status surface (... rate_limits: i !== undefined && i.length > 0 ? {...n, model_scoped: i} : n ...). The statusLine payload builder, however, assembles only the two generic windows:
k = { ...x.five_hour && {five_hour: {...}}, ...x.seven_day && {seven_day: {...}} }
Proposed change
Extend the statusLine rate_limits assembly to also project model_scoped (same additive semantics as the internal schema: present only when the server returns per-model windows and the list is non-empty):
"rate_limits": {
"five_hour": { "used_percentage": 33, "resets_at": 1784115000 },
"seven_day": { "used_percentage": 21, "resets_at": 1784613600 },
"model_scoped": [
{ "display_name": "Fable", "utilization": 0.38, "resets_at": "2026-07-21T06:00:00Z" }
]
}
No new API calls or backend work — the projection already exists in-process; this is serializing it into one more consumer's payload.
Why it matters
- Users on Claude 5 plans have a separate weekly Fable allocation; today the only way to see it is opening
/usageinteractively. - Statusline tools currently have no safe way to get this: the only alternative is reading the Claude Code OAuth token from the credential store and calling the usage API out-of-band, which is the wrong trust boundary for a statusline script and not portable across platforms. Exposing the field via stdin removes any incentive for that pattern.
- This is additive and backwards-compatible — existing consumers ignore unknown fields.
Environment
- Claude Code v2.1.205 / v2.1.210 (macOS arm64) — verified
model_scopedabsent from statusLine stdin on both, while the internal schema and projection exist in the binary.
Showing cached comments. Read the full discussion on GitHub ↗
6 Comments
I have fixed this in my status-line implementation by reading cost from transcripts. You can check out the code here: https://github.com/krayong/ccsidekick.
+1
Interested in showing Fable limits in my statusbar
Adding a data point from v2.1.215 — there may be a lower-cost path to the same outcome than
model_scoped.model_scopedis projected from theGET /api/oauth/usageresponse, so wiring it into statusLine stdin couples that payload to a network fetch. But there's a second, header-derived bucket that needs no fetch at all:uhu()builds rate-limit state directly fromanthropic-ratelimit-unified-{5h,7d,7d_oi,overage}-utilizationresponse headers, givingseven_day_overage_includedfrom7d_oi. In this build's label map that key renders as "Fable 5 limit":That bucket already lives in the same in-memory state the statusLine builder reads —
pNbcallsmqr()and then narrows to two keys. So forwardingseven_day_overage_included(andseven_day_opus/seven_day_sonnet) is a one-line widening of an existing projection, with no new network dependency and no reachability/latency failure mode.model_scopedis still the richer, server-labeled answer — this is just the version that's free today.Building on @wayne-bartnick's point above, I can confirm the data half from the other side.
I ran the
get_usagecontrol request on 2.1.218 and it already returns exactly the shape this issue asks for, allowlist filtering included:So on the endpoint side the schema and labeling are done — it's purely that the statusLine payload builder narrows to
five_hour/seven_dayand drops the rest of the object it already holds.The one thing I couldn't verify locally is Wayne's cheaper path: is
anthropic-ratelimit-unified-7d_oi-utilizationactually present on production responses? The header-fallback code only maps5h/7d, which hints it might not be — and if the7d_oiheader isn't emitted, the free "widen the projection by one line" option doesn't exist and statusLine would have to source this from the usage endpoint instead. That distinction seems like the crux of how big this change is, so it'd be great to get a definitive answer from the team.Either way the outcome is the same for users: on Claude 5 plans the scoped window is often the binding limit long before the shared ones (mine currently reads Fable 95% vs weekly 51%), and statusLine plugins are blind to it. claude-hud already ships rendering for
rate_limits.model_scoped(since 0.5.0), so the moment stdin carries it, it lights up with zero config.Until then, I put up a stopgap that renders these windows from an externally fed snapshot, using the
get_usageoutput above as a zero-token feeder: jarrodwatts/claude-hud#690. It defers to stdin whenevermodel_scopedappears there, so forwarding it upstream stays the real fix.Follow-up: the
7d_oiquestion from my last comment now has a production answer, from independent header probing by @d-mato (full data in d-mato/claude-usage-bar#2):anthropic-ratelimit-unified-7d_oi-utilizationis emitted in production, but only on responses to Fable-model requests; Haiku and Opus responses don't carry it (verified 2026-07-17:7d_oi0.65 vs7d0.36 on the same response, still present after the 2026-07-20 billing transition).weekly_scopedFable meter from the usage endpoint (both 65%), so the header andmodel_scopedread the same server-side meter.For this issue, that means the header-derived path is real but conditional: a session only receives
7d_oiwhile it's actually sending Fable requests. A session running any other model never sees the header, and that's exactly the "how much Fable headroom is left before I switch?" case. So widening the header projection would only light up mid-Fable-session, whilemodel_scopedcovers every session. That still points to forwardingmodel_scoped, as this issue asks.One more ecosystem data point: d-mato/claude-usage-bar switched its sole usage source to the
get_usagecontrol request (v0.5.0, 2026-07-25) after finding it in this thread, joining tools already relying on this undocumented surface (e.g. jimsimon/trouve#47, typings in community Go/Rust SDKs). Forwardingmodel_scopedin statusLine stdin (or an official surface like #78476) would let all of them retire that dependency.+1 — another data point, from a Max (5x) user.
I maintain a custom statusline that keeps usage visible inline:
Both windows come straight from
rate_limits.five_hourandrate_limits.seven_day, and they work well — the gap is that they're the only two windows in the payload.On my plan, claude.ai → Settings → Usage shows a third weekly bar for Fable, tracked separately from "All models" with its own reset. Those two numbers are meaningfully different for me right now (All models 54%, Fable 44%), so the aggregate
seven_dayvalue doesn't tell me how much Fable headroom I actually have — which is exactly the number I want before starting a long session.I dumped my live statusline payload on v2.1.220 to confirm: only
five_hourandseven_day, no per-model breakdown.If
rate_limits.model_scoped(or any per-model shape) landed, wiring it into my statusline would be a one-line change. Without it, the only route is querying the usage endpoint with an OAuth token pulled from the keychain — which I don't want running on every statusline refresh.One flag for triage: #77453 requested the same thing and was closed as "completed" on 2026-07-28, but the field still isn't in the payload. That closure was by the issue author rather than a ship, so it'd be unfortunate if this got deduped against it.