Expose rate-limit reset times, balance, plan as status_line tokens

Open 💬 10 comments Opened May 22, 2026 by NavinAgrawal
💡 Likely answer: A maintainer (github-actions[bot], contributor) responded on this thread — see the highlighted reply below.

Summary

The CLI status_line exposes five-hour-limit and weekly-limit as percentages only. The underlying rate-limit data is richer — account/rateLimits/read returns resetsAt, windowDurationMins, credits.balance, planType — but those fields aren't reachable via status_line tokens.

Missing tokens (as of 0.133.0)

  1. five-hour-limit-reset — formatted local time of primary.resetsAt
  2. weekly-limit-reset — formatted local time of secondary.resetsAt
  3. credits-balancecredits.balance (e.g. $766.76) when credits.hasCredits && !credits.unlimited
  4. plan-typeplanType (e.g. prolite)
  5. (optional) rate-limit-window-primary / -secondarywindowDurationMins formatted

Use case

Mirrors Claude Code's statusline which shows percentage + reset time. Without reset times the bare percentage is hard to act on — "5h 80%" doesn't tell you whether you have 10 minutes or 3 hours of breathing room. With it:

... · 5h 80% @11:36pm · weekly 83% @5/27 5:40am · ...

Workaround

Spawn codex app-server from outside, JSON-RPC handshake, call account/rateLimits/read, parse, render. Works but adds ~3-5s subprocess startup per query and requires users to build their own caching layer. The data is already there inside the running CLI; surfacing it as a token is a small change.

Verified data shape (0.133.0)

account/rateLimits/read returns:

{
  "rateLimits": {
    "limitId": "codex",
    "primary": { "usedPercent": 25, "windowDurationMins": 300, "resetsAt": 1779459394 },
    "secondary": { "usedPercent": 18, "windowDurationMins": 10080, "resetsAt": 1779826837 },
    "credits": { "hasCredits": true, "unlimited": false, "balance": "766.76..." },
    "planType": "prolite",
    "rateLimitReachedType": null
  },
  "rateLimitsByLimitId": { ... }
}

Acceptance

Adding these to the existing config:

status_line = ["model-with-reasoning", "codex-version", "context-remaining",
               "five-hour-limit", "five-hour-limit-reset",
               "weekly-limit", "weekly-limit-reset",
               "credits-balance", "project", "git-branch"]

Produces:

gpt-5.5 high · 0.133.0 · Context 28% left · 5h 25% @11:36pm · weekly 18% @5/27 5:40am · $766.76 · self-service-workbench · main

Thanks for the great CLI.

View original on GitHub ↗

10 Comments

github-actions[bot] contributor · 1 month ago

Potential duplicates detected. Please review them and close your issue if it is a duplicate.

  • #23450

Powered by Codex Action

NavinAgrawal · 1 month ago

Two related UX observations from building a workaround:

1. TUI rate-limit display doesn't auto-refresh

The StatusRateLimitState in the TUI updates only when:

  • A model response arrives (TokenCount events carry rate_limits)
  • Session start / resume (explicit one-shot read)
  • An account/rateLimits/updated notification fires

There's no periodic poll. For long-running idle sessions, the displayed values freeze at whatever was last seen until the user sends a new prompt.

Concrete impact: open a Codex session at noon when 5h is at 50%, work for 4 hours, idle for 1 hour. The 5h window has now rolled past your earlier usage and you genuinely have ~95% headroom again, but the TUI keeps showing 50% until you next prompt.

Would be helpful if either (a) the TUI polled account/rateLimits/read on a slow cadence (e.g. every 5 minutes when idle), or (b) the status_line tokens proposed in the issue body got auto-refreshed independently of TokenCount events.

2. usedPercent vs displayed-remaining naming mismatch

The API field is RateLimitWindow.usedPercent (in account/rateLimits/read and TokenCountEvent). The TUI display shows 100 - usedPercent ("5h 94%" when 6% is used).

This is a footgun for anyone integrating against the data. Either:

  • Add a remainingPercent field to the API response so integrators don't have to do the conversion (and don't display the same number with opposite meaning by accident), or
  • Rename the existing field to make the semantic explicit (e.g. windowUsedPercent).

Burned about an hour debugging an apparent mismatch between an external integration showing usedPercent and the TUI showing remaining; both were live and correct, just inversely framed.

Thanks again.

drogers0 · 1 month ago

On the usedPercent vs displayed-remaining footgun, which is a real one: I built aistat, and calling account/rateLimits/read from outside the CLI I hit the same trap, so it emits both fields rather than picking one.

It surfaces most of what you listed — both reset times (primary/secondary.resetsAt), the plan (planType), and used + remaining percentages, recomputed against now each call. Two caveats: it doesn't expose credits.balance today, and the window duration shows up as the window label (five_hour/seven_day) rather than a raw windowDurationMins. Human view:

$ aistat -h
Claude usage
- personal@example.com (active) [Max 5x]
  - 5-hour: 92.0% (resets in 4h 53m)
  - 7-day: 71.0% (resets in 2d 5h)
- work@example.com [Max 20x]
  - 5-hour: 4.0% (resets in 4h 12m)
  - 7-day: 12.0% (resets in 5d 9h)

Codex usage
- personal@example.com (active) [Pro]
  - 5-hour: 25.0% (resets in 5h 16m)
  - 7-day: 18.0% (resets in 4d 18h)

and the full JSON:

$ aistat usage codex
{
  "checked_at": "2026-05-29T18:21:04+00:00",
  "providers": {
    "codex": {
      "accounts": [
        {"email": "you@example.com", "plan": "Pro", "active": true,
         "limits": {
           "five_hour": {"used_percent": 25, "remaining_percent": 75, "resets_at": "2026-05-29T23:36:34+00:00", "reset_after_seconds": 18930},
           "seven_day": {"used_percent": 18, "remaining_percent": 82, "resets_at": "2026-06-03T05:40:37+00:00", "reset_after_seconds": 472773}
         }}
      ]
    }
  }
}

reset_after_seconds sidesteps the "freeze at last seen value" problem — recomputed against now on every call, so any polling cadence gives a fresh "in N minutes" string. 90s file cache makes aggressive statusline refresh safe. Same binary covers Claude/Copilot.

https://github.com/drogers0/aistat

Jasonzh8 · 1 month ago

Adding another user data point: the current statusline can show 5h 60% left · weekly 84% left, but it still does not show the reset timestamp for either window. That makes the percentages hard to act on because 60% left is very different if the 5h window resets in 10 minutes versus several hours. This is a pretty important usability gap for terminal-first users; the Claude Code ecosystem already has many statusline plugins that show usage together with reset timing, so users coming from that workflow expect this information to be visible directly in the CLI footer. Adding the reset time next to each existing limit token, for example 5h 60% left (resets 14:30) and weekly 84% left (resets Mon 03:00), would make the current tokens much more actionable.

zhangchitc · 1 month ago

+1 to @Jasonzh8 ’s point. The current statusline percentages are useful, but without reset timing they’re hard to act on.

tristan666666 · 1 month ago

Adding one macOS-side data point: I built Agent Island because I wanted the same reset/usage information visible without keeping a Codex TUI focused.

https://github.com/tristan666666/agent-island

It reads Claude/Codex usage + reset state and puts it in the MacBook notch. It also shows per-session state (running, waiting for you, stuck) and can auto-resume a selected long session when it can continue.

Still +1 for native status_line tokens. External UI/CLI workarounds are useful, but the source of truth should be easy to surface from the running Codex client.

m13v · 1 month ago

the token split reads clean in config but it leaks the moment the 5h window rolls between two status_line refreshes. five-hour-limit and five-hour-limit-reset get rendered on separate ticks, so you can briefly show 80% next to a resetsAt that already fired, which is exactly the case the reset time was supposed to fix. they both come off one rateLimits/read snapshot, so the fix is reading usedPercent and resetsAt from the same fetch and rendering them as one unit, not two independent tokens that can desync.

m13v · 1 month ago

we hit the same desync reading claude's quota off the same endpoint settings/usage renders, ended up pinning percent and resetsAt to one fetch: https://claude-meter.com/r/kqh8mw3f

tristan666666 · 29 days ago

@m13v agreed. For usage/reset display, separate tokens are nice for composability but dangerous if they are evaluated independently.

The safer API shape is probably a compound token or structured statusline field rendered from one rateLimits/read snapshot, for example:

  • five-hour-limit-status -> { usedPercent, remainingPercent, resetsAt, resetAfterSeconds, windowDurationMins }
  • weekly-limit-status -> same shape

Then the renderer can choose text format, but the data stays atomic. It also lets clients avoid the "reset just fired but percent is from the previous window" problem by comparing checked_at / resetsAt from the same sample.

For external monitors, this same rule matters: percent, reset time, and plan/window metadata need to be pinned to one fetch, cached together, and invalidated together.

chuckatbcs · 8 days ago

Adding my voice as a paying ChatGPT subscriber. The official Codex pricing documentation still says local messages and cloud tasks share a five-hour window, with additional weekly limits potentially applying. However, the current /status output does not show the five-hour reset time, so users cannot tell when that included allowance refreshes or reliably distinguish the five-hour and weekly limits.

Please restore clear visibility of both quota windows in /status and the status line, including:

  • five-hour usage remaining and exact reset time
  • weekly usage remaining and exact reset time
  • plan type and any purchased-credit balance
  • a clear indication of which limit is currently binding

If the five-hour allowance has been removed, reduced, or replaced by a weekly-only system for any accounts, OpenAI should state that plainly and notify affected subscribers before the change. Hiding or omitting reset information materially reduces transparency and the value of a paid subscription.