Codex CLI
Local-file provider for the OpenAI Codex CLI. Reads session logs, auth state, and config to show today's activity, plan info, and rate-limit windows.
At a glance
- Provider ID —
codex - Detection —
~/.codexdirectory on disk - Auth — token stored in
~/.codex/auth.jsonby the Codex CLI; no env var needed - Type — coding agent
- Tracks:
- Latest session: tokens, model, client
- Daily session counts
- Model and client breakdowns
- Rate-limit windows (primary and secondary)
- Individual credit usage versus the current monthly limit
- Credit burn rate and projected runout time
- Plan and version
- Patch stats
Setup
Auto-detection
OpenUsage registers the provider once Codex has written data under ~/.codex/. Run the Codex CLI at least once to create it.
Detection needs ~/.codex/sessions/ or ~/.codex/auth.json; the codex binary is optional. The telemetry daemon runs under launchd or systemd with a minimal PATH, so OpenUsage also looks for codex in common per-user install directories (~/.local/bin, ~/.npm-global/bin, ~/.nvm/versions/node/*/bin, ~/.volta/bin, ~/.bun/bin, pnpm and Linuxbrew prefixes). Without the binary, quota comes from the live usage endpoint instead of codex app-server.
Manual configuration
{
"accounts": [
{
"id": "codex",
"provider": "codex",
"extra": {
"config_dir": "~/.codex",
"sessions_dir": "~/.codex/sessions"
}
}
]
}
Override config_dir and sessions_dir only if the CLI uses non-default paths.
Data sources & how each metric is computed
Codex has three data paths, checked in this order:
- Codex CLI app-server —
codex app-server --stdioreadsaccount/rateLimits/readbefore the session scan. It supplies current-account quota windows, plan and credit data, including individual monthly credit limits when available. - Live ChatGPT usage endpoint — an authenticated GET, attempted only when the app-server returns no usable quota window and
auth.jsoncontains an access token. - Local files — JSONL session transcripts and auth/config metadata under
~/.codex/. The latest usable session supplies token activity and fallback quota windows when neither live source supplies a usable window.
A live quota window replaces session quota windows. If the app-server returns only a weekly window for the main pool, OpenUsage reports the missing 5h window instead of filling it from an older session. Other metered pools show only windows they actually report. Session quota fallback is labeled as session data and may be older than a live response.
The base URL for the live endpoint is, in order: acct.BaseURL → extra.chatgpt_base_url → the value parsed from ~/.codex/config.toml (chatgpt_base_url) → https://chatgpt.com/backend-api. The path is /wham/usage for chatgpt.com/backend-api and /api/codex/usage otherwise.
Latest session
- Source: the newest usable
~/.codex/sessions/**/*.jsonl, ordered by the rollout filename timestamp (mtime is a fallback for other names). The provider skips recent stub files withouttoken_countevents and parses the trailing turn'sInfo.TotalTokenUsagefor tokens, plusmodelandclientfrom the same payload. - Transform: tokens stored as
latest_session_tokens, model/client stored underRaw["latest_session_model"]andRaw["latest_session_client"].
Daily / model / client breakdowns
- Source: the same JSONL files, scanned per poll (with mtime + size caching to skip unchanged files).
- Transform: each turn becomes a usage record. Records are aggregated by model, by client, and by day. Outputs:
sessions_today— distinct sessions with at least one turn whose timestamp falls in today (local time).- Per-model rows with input/output/cached token totals.
- Per-client rows with the same totals plus session count.
How the model is resolved
The model credited to each turn is resolved in this order, first match wins:
model/model_idon thetoken_countevent itself (per-turn override).model/model_idon theturn_contextline, if present.model/model_idin thesession_metaheader.base_instructions.provenance.modelin thesession_metaheader.
Step 4 matters on Codex CLI 0.147.0 and later, which stopped writing an
explicit model to the session header and no longer emits turn_context lines
at all. Without it every turn falls through to the unknown bucket, which
also zeroes that bucket's cost.
:::note Turns that report only a token total
Some Codex clients emit token_count events with total_tokens populated but
input_tokens and output_tokens both zero. Those turns still contribute to
model_<name>_total_tokens, but no cost is derived for them — input and output
price differently, so a total alone cannot be converted to spend.
:::
Rate-limit windows (rate_limit_primary, rate_limit_secondary)
- Source:
primary,secondary, andrateLimitsByLimitIdfrom the app-server, then the HTTP usage response if the RPC has no valid windows, then the selected local session. Each window reports a used percentage, duration, and optional reset time. - Transform:
Used = used_percent,Remaining = 100 - Used,Limit = 100. The main pool keepsrate_limit_primaryandrate_limit_secondary; other pools userate_limit_<id>_<slot>. Reset times populateResets[…]. The compatibility aliasesplan_auto_percent_used,plan_api_percent_used, andplan_percent_usedremain available.
Credit balance
- Source:
credits.balance(orcredits.has_creditsboolean) from the app-server, HTTP fallback, or local session. - Transform: stored as a metric
Remainingin USD.unlimited=trueis reflected as a special attribute.
Individual credits and forecast
- Source:
individualLimitfrom the Codex CLI app-serveraccount/rateLimits/readresponse. The response provides the current-periodlimit, cumulativeusedcredits (or a remaining percentage), and the nextresetsAttimestamp. - Transform:
codex_credit_limitcontains used/remaining/total credits, whilecodex_credit_percent_useddrives the primary dashboard gauge. - Forecast: when the next monthly reset is available, OpenUsage infers the preceding calendar-month boundary and calculates the average burn rate from cumulative current-period usage divided by elapsed time since that boundary. The dashboard shows the reset countdown and projected percentage at reset. Without a usable reset timestamp, it falls back to successive observed quota samples.
- Forecast source is recorded as
inferred_period_startorobserved_usageso the estimate is distinguishable from authoritative quota data.
Plan, version, account email
- Source:
plan_typefrom the app-server, HTTP fallback, or local session; email from HTTP or auth metadata; CLI version from~/.codex/version.json; account ID fromauth.json(tokens.account_idor top-levelaccount_id). - Transform: each stored as a snapshot attribute.
Patch stats
- Source: scanning JSONL turns for tool-call entries that look like file edits.
- Transform: aggregated counts of patches/files-changed.
Auth status
- Source: HTTP status code when the fallback call is attempted.
- Transform:
401/403is recorded as a diagnostic. Without usable local or app-server data, the snapshot reports authentication required; otherwise the local-data path stays available.
What's NOT tracked
- Per-token spend in dollars from local sessions. Codex sessions don't carry pricing — only token counts. The credit balance is the only $ figure, and it comes from the live endpoint.
- Hook-driven real-time events without the integration. Install the
codexintegration (see Daemon integrations) for per-turn events.
:::note Cost values hidden by default on Plus / Pro / Team / Enterprise
On a ChatGPT subscription plan (Plus, Pro, Team, Enterprise) the dollar number is misleading — usage is governed by rate-limit windows, not by per-call pricing. OpenUsage hides cost columns by default whenever the live plan_type reports a subscription tier; rate-limit windows, sessions, and tokens stay visible. Override with dashboard.hide_costs or the c keystroke.
:::
How fresh is the data?
- Polling: every 30 s by default. JSONL files are re-parsed when their mtime/size changes; otherwise served from cache.
- Hook (when integration is installed): real-time per turn.
API endpoints used
- Optional live usage endpoint:
GET https://chatgpt.com/backend-api/wham/usage(default), orGET <base>/api/codex/usagefor non-ChatGPT bases.- Headers:
Authorization: Bearer <auth.json access_token>andChatGPT-Account-Id: <account_id>when available.
- Preferred local CLI quota endpoint:
codex app-server --stdio, using the JSON-RPC handshake followed byaccount/rateLimits/read.
Files read
~/.codex/sessions/**/*.jsonl— session transcripts~/.codex/auth.json— auth token (tokens.access_token,tokens.account_id)~/.codex/config.toml— CLI configuration (chatgpt_base_urlif set)~/.codex/version.json— installed version
Caveats
- Individual credit usage and the forecast require authenticated Codex quota data from the live endpoint or CLI app-server; offline sessions still show local activity.
- Rate-limit windows are reported by the API and may differ from documented limits during quota changes.
- The monthly period start is inferred from the next reset because Codex reports the reset boundary but not an explicit start timestamp.
- The provider has hooks-style integration with the daemon: see Daemon integrations.
Troubleshooting
- No quota windows — authenticate with
codex loginand check that the installed CLI supportscodex app-server --stdio. Local session windows remain available when live requests fail. - No credit usage or forecast —
~/.codex/auth.jsonis missing or expired, or the CLI app-server quota request failed. Re-authenticate with the Codex CLI and wait for the next daemon poll. - Codex listed by
openusage detectbut missing from the dashboard (older versions): the daemon could not find thecodexbinary on its servicePATH. Upgrade, or setbinaryon a manualcodexaccount to the full path fromwhich codex. - Sessions missing — confirm
sessions_dirmatches the path Codex writes to.
Related
- OpenAI — direct API rate limits for the underlying models
- Claude Code — sibling local-file coding-agent provider