Expose rate_limits.model_scoped (per-model weekly windows, e.g. Fable) in statusLine stdin

Status Closed — duplicate
Maintainer reply None cached
Activity 8 comments · opened Jul 15, 2026 · closed Aug 20, 2026

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 /usage interactively.
  • 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_scoped absent from statusLine stdin on both, while the internal schema and projection exist in the binary.

View original on GitHub ↗

6 Comments

krayong · 1 month ago

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.

hibukki · 1 month ago

+1
Interested in showing Fable limits in my statusbar

wayne-bartnick · 1 month ago

Adding a data point from v2.1.215 — there may be a lower-cost path to the same outcome than model_scoped.

model_scoped is projected from the GET /api/oauth/usage response, 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 from anthropic-ratelimit-unified-{5h,7d,7d_oi,overage}-utilization response headers, giving seven_day_overage_included from 7d_oi. In this build's label map that key renders as "Fable 5 limit":

{five_hour:"session limit", seven_day:"weekly limit", seven_day_opus:"Opus limit",
 seven_day_sonnet:"Sonnet limit", seven_day_overage_included:"Fable 5 limit", ...}

That bucket already lives in the same in-memory state the statusLine builder reads — pNb calls mqr() and then narrows to two keys. So forwarding seven_day_overage_included (and seven_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_scoped is still the richer, server-labeled answer — this is just the version that's free today.

kuk1song · 1 month ago

Building on @wayne-bartnick's point above, I can confirm the data half from the other side.

I ran the get_usage control request on 2.1.218 and it already returns exactly the shape this issue asks for, allowlist filtering included:

"model_scoped": [{ "display_name": "Fable", "utilization": 95, "resets_at": "2026-07-27T11:00:00.000000+00:00" }]

So on the endpoint side the schema and labeling are done — it's purely that the statusLine payload builder narrows to five_hour / seven_day and 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-utilization actually present on production responses? The header-fallback code only maps 5h / 7d, which hints it might not be — and if the 7d_oi header 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_usage output above as a zero-token feeder: jarrodwatts/claude-hud#690. It defers to stdin whenever model_scoped appears there, so forwarding it upstream stays the real fix.

kuk1song · 1 month ago

Follow-up: the 7d_oi question 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-utilization is emitted in production, but only on responses to Fable-model requests; Haiku and Opus responses don't carry it (verified 2026-07-17: 7d_oi 0.65 vs 7d 0.36 on the same response, still present after the 2026-07-20 billing transition).
  • Its value matched the weekly_scoped Fable meter from the usage endpoint (both 65%), so the header and model_scoped read the same server-side meter.

For this issue, that means the header-derived path is real but conditional: a session only receives 7d_oi while 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, while model_scoped covers every session. That still points to forwarding model_scoped, as this issue asks.

One more ecosystem data point: d-mato/claude-usage-bar switched its sole usage source to the get_usage control 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). Forwarding model_scoped in statusLine stdin (or an official surface like #78476) would let all of them retire that dependency.

scottyah · 1 month ago

+1 — another data point, from a Max (5x) user.

I maintain a custom statusline that keeps usage visible inline:

user@host:myproject [Opus 5 (1M context)] 4% · [sesh:57% 1h49m] [wk:54% 4d15h]

Both windows come straight from rate_limits.five_hour and rate_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_day value 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_hour and seven_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.

Showing cached comments. Read the full discussion on GitHub ↗