02 — Codex usage API
Documents Codex quota endpoints, authentication, response fields, and client-derived presentation semantics.
Summary
Codex exposes quota windows and reset metadata through authenticated usage surfaces; presentation labels and projections remain client-derived.
Question and scope
(1) Which official OpenAI/ChatGPT endpoints expose Codex rate-limit/usage windows (5h/weekly, percent used, resets) for ChatGPT plans (Plus/Pro, "Pro 20x"-style)? (2) Does an official endpoint expose "Codex Spark" windows and with what fields? (3) Does an official endpoint expose "Limit Reset Credits" (count + per-credit reset/expiry data)? (4) What auth do these endpoints need (ChatGPT OAuth token, scopes, on-disk location — location/type only)? (5) Which CodexBar-style UI elements have NO direct API source and must be computed client-side (deficit %, "Runs out in X")?
This chapter informs jackin-desktop.
Method
Method: web + reference read of github.com/openai/codex (shallow clone, commit 74e9d7efc416b1cb9f3ad10c70a91afbcb6d6a29, 2026-07-23).
Verification cutoff: 2026-07-24 Clean-room honored: CodexBar/OpenUsage source NOT read; CodexBar README prose cited as concept evidence only. No embedded instructions were encountered in any fetched web or repo content.
Findings
Q1 — Endpoints exposing Codex rate-limit/usage windows
- Primary usage endpoint:
GET {base}/wham/usagewhen the base URL contains/backend-api(ChatGPT path style), elseGET {base}/api/codex/usage(Codex API path style) —codex-rs/backend-client/src/client/rate_limit_resets.rslines 82–83, https://github.com/openai/codex @74e9d7e(confidence: HIGH) - Default ChatGPT base URL is
https://chatgpt.com/backend-api/(config keychatgpt_base_url), so the effective first-party URL ishttps://chatgpt.com/backend-api/wham/usage—codex-rs/core/src/config/mod.rsline 4064–4066; path-style switch on/backend-apiincodex-rs/backend-client/src/client.rslines 118–122 (confidence: HIGH) - Response payload (
RateLimitStatusPayload):plan_type,rate_limit { allowed, limit_reached, primary_window, secondary_window },credits,spend_control,additional_rate_limits[],rate_limit_reached_type—codex-rs/codex-backend-openapi-models/src/models/rate_limit_status_payload.rs(OpenAPI-generated client models for "codex-backend") (confidence: HIGH) - Window fields (
RateLimitWindowSnapshot):used_percent,limit_window_seconds,reset_after_seconds,reset_at(unix seconds) —codex-rs/codex-backend-openapi-models/src/models/rate_limit_window_snapshot.rs(confidence: HIGH) - 5h/weekly semantics are NOT flagged in the payload; the client derives labels from window duration: ~300 min → "5h", ~10 080 min → "weekly" (also daily/monthly/annual buckets) —
codex-rs/tui/src/chatwidget/rate_limits.rslines 77–102 (confidence: HIGH) - Official docs confirm the model: "The usage limits for local messages and cloud chats share a five-hour window. Additional weekly limits may apply"; tiers listed include Plus, Pro 5x, Pro 20x; in-session check via
/status; web dashboard athttps://chatgpt.com/codex/settings/usage— https://developers.openai.com/codex/pricing (308-redirects to https://learn.chatgpt.com/docs/pricing.md), fetched 2026-07-24; dashboard URL also hardcoded incodex-rs/tui/src/status/card.rsline 56 (confidence: HIGH) - Second delivery channel (per-turn, not poll): rate-limit snapshots ride on turn responses from
https://chatgpt.com/backend-api/codex/responsesas response headers:x-codex-primary-used-percent,x-codex-primary-window-minutes,x-codex-primary-reset-at,x-codex-secondary-*(same trio), per-limit familiesx-<limit-id>-*plusx-<limit-id>-limit-name,x-codex-credits-{has-credits,unlimited,balance},x-codex-rate-limit-reached-type,x-codex-promo-message; over websocket, event typecodex.rate_limitscarries{plan_type, rate_limits{primary,secondary}, credits, metered_limit_name}—codex-rs/codex-api/src/rate_limits.rs(whole file),codex-rs/codex-api/src/endpoint/responses_websocket.rslines 748–749, responses URL incodex-rs/http-client/src/chatgpt_cloudflare_cookies.rsline 130 (confidence: HIGH) - First-party client plumbing (not HTTP, JSON-RPC to the local app-server):
account/rateLimits/read,account/usage/read, notificationaccount/rateLimits/updated—codex-rs/app-server-protocol/src/protocol/common.rslines 1053–1070, 1706 (confidence: HIGH) plan_typeenum values:guest, free, go, plus, pro, prolite, free_workspace, team, self_serve_business_usage_based, business, enterprise_cbp_usage_based, education, quorum, k12, enterprise, edu, unknown—codex-rs/codex-backend-openapi-models/src/models/rate_limit_status_payload.rsPlanTypeenum (confidence: HIGH). The enum distinguishesproandprolitebut carries no multiplier label; first-party Codex renders "Pro" / "Pro Lite", sopro↔"Pro 20x" andprolite↔"Pro 5x" are marketing-level inferences (chapter 08).- No public REST usage API is documented on developers.openai.com; docs point only to
/statusand the web dashboard. The/backend-api/wham/*endpoints are known solely from the first-party client source (confidence: HIGH for the absence in docs)
Q2 — Codex Spark windows
- Spark = GPT-5.3-Codex-Spark, research preview, ChatGPT Pro only: "Because it runs on specialized low-latency hardware, usage is governed by a separate usage limit that may adjust based on demand" — https://developers.openai.com/codex/pricing (fetched 2026-07-24); announcement page https://openai.com/index/introducing-gpt-5-3-codex-spark/ exists but returned HTTP 403 to non-browser fetch (confidence: HIGH for the pricing-doc quote)
- No Spark-specific endpoint exists in the first-party client. Spark limits arrive inside the same
/wham/usagepayload viaadditional_rate_limits[], each entry{ limit_name, metered_feature, rate_limit { allowed, limit_reached, primary_window, secondary_window } }— i.e. the identical window shape (used_percent,limit_window_seconds,reset_after_seconds,reset_at) per named limit —codex-rs/codex-backend-openapi-models/src/models/additional_rate_limit_details.rs; mapping incodex-rs/backend-client/src/client.rslines 526–557 (default limit keyedlimit_id="codex", additional limits keyed bymetered_featurewithlimit_namecarried for display) (confidence: HIGH) - The TUI renders a named additional limit as
"<name> limit"with its own 5h (300 min) primary and weekly (10 080 min) secondary windows; the repo's own fixture useslimit_name: "spark"with exactly those two windows and asserts "spark limit" / "5h limit" / "Weekly limit" output —codex-rs/tui/src/status/tests.rslines 1725–1775 (confidence: HIGH for the mechanism; MED for"spark"being the exact live server string, since it appears only in a test fixture) - On the streaming channel, Spark-family limits would surface as an
x-<limit-id>-*header family and/orcodex.rate_limitswebsocket events keyed bymetered_limit_name—codex-rs/codex-api/src/rate_limits.rsparse_all_rate_limits/parse_rate_limit_event(confidence: HIGH for mechanism) - Contradiction in the wild: users report Spark usage draining or being blocked by the general Codex quota despite the separate meter — https://github.com/openai/codex/issues/20122, https://github.com/openai/codex/issues/23150 (confidence: MED — issue reports, not verified behavior)
Q3 — Limit Reset Credits
- Yes — three first-party endpoints (same path-style switch as usage):
GET {base}/wham/usagealso returns top-levelrate_limit_reset_credits { available_count }alongside the rate-limit payload —codex-rs/backend-client/src/types.rslines 22–25, 50–55 (confidence: HIGH)GET {base}/wham/rate-limit-reset-credits(Codex style:{base}/api/codex/rate-limit-reset-credits) returns{ credits: [ { id, reset_type, status, granted_at, expires_at, title, description } ], available_count }—codex-rs/backend-client/src/client/rate_limit_resets.rslines 38–43, 88–97;codex-rs/backend-client/src/types.rslines 27–42 (confidence: HIGH)POST {base}/wham/rate-limit-reset-credits/consumewith JSON body{ redeem_request_id, credit_id? }(redeem_request_id = caller-generated idempotency key, UUID recommended) returns{ code: reset | nothing_to_reset | no_credit | already_redeemed, windows_reset }—codex-rs/backend-client/src/client/rate_limit_resets.rslines 15–20, 45–113;codex-rs/backend-client/src/types.rslines 84–98 (confidence: HIGH)
- Per-credit fields carry timestamps, not countdowns:
granted_atand nullableexpires_at(unix seconds in the app-server projection; "null if it does not expire"); creditstatusenumavailable | redeeming | redeemed | unknown; any "expires in X" countdown is client-computed —codex-rs/app-server-protocol/src/protocol/v2/account.rslines 302–353 (confidence: HIGH) - App-server surface at HEAD:
account/rateLimits/read→rateLimitResetCredits { availableCount, credits[]? }(detail rows fetched from the list endpoint with a 5 s timeout and merged;nullcredits = summary only; "The backend may cap this list"), andaccount/rateLimitResetCredit/consume { idempotencyKey, creditId? }—codex-rs/app-server/src/request_processors/account_processor/rate_limit_resets.rslines 1–30,codex-rs/app-server-protocol/src/protocol/common.rslines 1059–1063 (confidence: HIGH) - Lineage: PR "feat(app-server): expose rate-limit reset credits" added the summary+consume surface — https://github.com/openai/codex/pull/28143; issue asking for per-credit detail exposure states that as of
rust-v0.142.0onlyavailableCount+ consume existed and callsGET /backend-api/wham/rate-limit-reset-creditsa "private backend endpoint" that third-party tools were already hitting — https://github.com/openai/codex/issues/29618 (confidence: HIGH that these are the first-party statements) - Distinct concept, do not conflate:
creditsin the usage payload (CreditStatusDetails { has_credits, unlimited, balance, approx_local_messages, approx_cloud_messages }) is the pay-per-use credit balance, not reset credits —codex-rs/codex-backend-openapi-models/src/models/credit_status_details.rs; the pricing doc separately describes "banked rate-limit reset" grants (e.g. referral rewards) — https://developers.openai.com/codex/pricing (confidence: HIGH)
Q4 — Authentication
- All
/wham/*calls use the ChatGPT OAuth access token asAuthorization: Bearer <JWT>, plus aChatGPT-Account-Idheader when a workspace/account id is set —codex-rs/backend-client/src/client.rslines 226–231 (header injection) and line 1139–1142 (test asserting Bearer + account-id header) (confidence: HIGH) - Token location on disk (location/type only):
$CODEX_HOME/auth.json(default~/.codex/auth.json), or the OS credential store depending oncli_auth_credentials_store = file | keyring | auto; structureAuthDotJson { auth_mode, OPENAI_API_KEY, tokens { id_token, access_token (JWT), refresh_token, account_id }, last_refresh, agent_identity, personal_access_token, bedrock_api_key }—codex-rs/login/src/auth/storage.rslines 38–61, 150–151;codex-rs/login/src/token_data.rslines 11–25; official doc: "Codex caches login details locally in a plaintext file at~/.codex/auth.jsonor in your OS-specific credential store" — https://developers.openai.com/codex/auth (redirects to https://learn.chatgpt.com/docs/auth), fetched 2026-07-24 (confidence: HIGH) - OAuth flow:
codex loginbrowser PKCE flow (S256) againstauth.openai.com; client idapp_EMoamEEZ73f0CkXaXp7hrann(env overrideCODEX_APP_SERVER_LOGIN_CLIENT_ID); scopes requested:openid profile email offline_access api.connectors.read api.connectors.invoke; refresh athttps://auth.openai.com/oauth/token, revoke athttps://auth.openai.com/oauth/revoke; tokens auto-refresh during use —codex-rs/login/src/auth/manager.rslines 191–195, 1448–1454;codex-rs/login/src/server.rslines 555–580 (confidence: HIGH) - API-key login exists as an alternative (
codex login --with-api-key), but the ChatGPT-plan quota endpoints above are the ChatGPT-token path — https://learn.chatgpt.com/docs/auth (confidence: HIGH)
Q5 — UI elements with no direct API source (client-side computation required)
- Neither
/wham/usagepayload nor thex-codex-*header family carries burn rate, pace, deficit, projection, or any historical series: the complete per-window surface isused_percent,limit_window_seconds,reset_after_seconds,reset_at—codex-rs/codex-backend-openapi-models/src/models/rate_limit_window_snapshot.rs,codex-rs/codex-api/src/rate_limits.rs(confidence: HIGH — exhaustive absence in first-party models) - Deficit % (used % ahead of elapsed-window %): must be computed client-side; elapsed fraction is derivable from
(limit_window_seconds − reset_after_seconds) / limit_window_seconds, so a single snapshot suffices arithmetically, but no API field provides it (confidence: HIGH for the absence; derivation is arithmetic on HIGH-confidence fields) - "Runs out in X" projection: no API field; requires client-side extrapolation from successive
used_percentsamples (the API is a point-in-time snapshot with no rate) (confidence: HIGH for the absence) - No usage-history/time-series endpoint exists anywhere in the first-party client (
backend-clienthas no such route) — https://github.com/openai/codex @74e9d7e(confidence: HIGH for absence) - Per-credit "expires in" countdown: API gives
expires_attimestamps only; countdown rendering is client-side (confidence: HIGH) - CodexBar README prose corroborates provider windows and reset countdowns, but its additional enrichments are not sourced from the token-authenticated usage API — https://github.com/steipete/codexbar README, fetched 2026-07-24 (confidence: MED — README prose only)
Dead ends and contradictions
help.openai.comarticle 11369540 ("Using Codex with your ChatGPT plan") andopenai.com/index/introducing-gpt-5-3-codex-spark/both return HTTP 403 to non-browser fetch — content not directly verified; the developers.openai.com pricing doc (which 308-redirects tolearn.chatgpt.com/docs/pricing.md) was used as the primary doc instead.developers.openai.com/codex/*pages permanently redirect tolearn.chatgpt.com/docs/*— the canonical docs host has moved; cite either, they are the same content.- No officially documented public REST API for Codex ChatGPT-plan usage exists; the docs only offer
/statusand the web dashboard. Every endpoint above is established from first-party client source, and openai/codex issue #29618 explicitly characterizes the reset-credits list endpoint as "private backend" — official support status for third-party callers is not granted anywhere found. - Spark contradiction: docs state a separate Spark usage limit, but issues #20122 and #23150 report Spark being blocked by / draining the general Codex quota. Both cannot be simultaneously true as steady-state behavior; unresolved upstream.
"spark"aslimit_nameappears only in a TUI test fixture, not in any request/response constant — ruled out as a confirmed server string.plan_typecarries no multiplier labels ("5x"/"20x" absent from the whole openai/codex repo) — a multiplier display requires an out-of-bandpro/prolite→marketing-name mapping (chapter 08).- The
x-codex-*header /codex.rate_limitswebsocket channel only fires on actual turn requests to/backend-api/codex/responses— ruled out as a passive polling source for a menu-bar app (polling/wham/usageis the passive path). - openai/codex
docs/directory is now stub files pointing at developers.openai.com — no in-repo usage-limits documentation to cite.
Implications for jackin❯
Use first-party Codex client models for bounded windows and reset credits, while treating marketing labels, projections, and private backend stability as derived or uncertain.
Limitations and unknowns
- Exact live
limit_name/metered_featurestrings the server emits for Spark (and any other additional limits) in/wham/usageresponses — needs an operator-authenticated session (real ChatGPT Pro token or browser network inspection on chatgpt.com/codex/settings/usage) to observe. - Whether
/wham/usageand the reset-credits endpoints require theChatGPT-Account-Idheader for personal (non-workspace) accounts, or only when a workspace is selected — needs live traffic inspection. - Whether/at what size the backend caps the
credits[]list fromGET /wham/rate-limit-reset-credits(protocol doc says "may cap") — needs live observation. - Official 30-day expiry for banked resets: third-party posts claim 30 days; the authoritative per-credit source is the
expires_atfield; the help-center article that would confirm the policy number returned 403 — an operator browser session could verify. - Whether OpenAI intends to bless
/backend-api/wham/*for third-party clients (issue #29618 asks; no answer found as of 2026-07-24).