Usage Tracking: Provider Data, Quota Windows, and Token Fallback
This page explains how Neura Market fetches provider usage and quota data directly from each provider's endpoint. It covers quota window normalization, token attribution, and session-level fallback for developers integrating usage tracking.
Read this when
- You are wiring provider usage/quota surfaces
- You need to explain usage tracking behavior or auth requirements
What it is
- Provider usage and quota data is fetched directly from each provider's usage endpoint. There are no estimated provider charges; only provider-reported plan names, quota windows, balances, spending, budgets, daily cost history, token/model attribution, or account state summaries are used.
- Quota window output is presented in a human-readable format normalized to
X% left, even when a provider reports consumed quota, remaining quota, or only raw counts. For providers without resettable quota windows, provider summary text is shown instead, such as a balance. - Session-level
/statusand thesession_statustool fall back to the session's transcript log when the live session snapshot lacks token or model data. This fallback fills in missing token and cache counters, can recover the active runtime model label, and prefers the larger prompt-oriented total when session metadata is missing or smaller thantotalTokensFresh !== true, zero, or below the transcript-derived value. Nonzero live values always take precedence over the fallback.
Where it shows up
/statusin chats: a status card showing session tokens and estimated cost (API key models only). Provider usage appears for the current model provider when available, as a normalizedX% leftwindow or provider summary text./usage off|tokens|fullin chats: a per-response usage footer./usage costin chats: a local cost summary aggregated from OpenClaw session logs.- CLI:
openclaw status --usageprints a full per-provider usage and quota breakdown. - CLI:
openclaw models statuslists OAuth and token auth profiles and displays a usage-window summary next to each provider that has one. - Control UI: Usage shows provider plan and billing cards above OpenClaw's session-derived token and estimated cost analysis. Anthropic and OpenAI Admin API credentials add provider-reported today, 7-day, and 30-day spend, daily trends, token totals, top models, and cost categories.
- Control UI: the chat composer's context ring popover shows plan usage for subscription providers, including per-window bars (5-hour, weekly, model-scoped) with reset times, the provider plan when known (for example
Max (20x)), and extra usage credits. Sessions billed through a plan hide per-token dollar estimates; API-billed sessions keepEst. costand the cost-by-type breakdown. Claude Code CLI (claude-cli) setups reuse the same Anthropic subscription usage. - macOS menu bar: a root "Usage" section appears below Context when provider usage snapshots are available. See Menu bar.
openclaw channels list no longer prints provider usage; it directs users to openclaw status or openclaw models list instead.
Anthropic and OpenAI cost history
Subscription quota and API billing represent different provider surfaces:
- Anthropic subscription and setup credentials continue to show Claude quota windows and optional extra usage budgets. Set
ANTHROPIC_ADMIN_KEYorANTHROPIC_ADMIN_API_KEYto display organization Usage and Cost API history instead. An Anthropic provider credential beginning withsk-ant-adminis detected automatically. - OpenAI ChatGPT and Codex OAuth continues to show plan, quota windows, and credit balance. Set
OPENAI_ADMIN_KEYto show organization cost and completions usage history instead; optionally setOPENAI_PROJECT_IDto scope it to one project. OpenClaw never sends inference credentials fromOPENAI_API_KEY, provider config, or auth profiles to organization APIs because those keys may belong to custom endpoints.
Admin credentials take precedence because they provide actual organization billing. OpenClaw does not combine these provider-reported totals with its local session estimates; the two sections intentionally answer different questions.
Default usage footer mode
/usage off|tokens|full sets the footer for a session and is remembered for that
session. messages.responseUsage seeds that mode for sessions that have not
chosen one, so the footer can be on by default without typing /usage each time.
Set one mode for every channel, or a per-channel map with a default fallback:
{
"messages": {
"responseUsage": "tokens",
// or: { "default": "off", "discord": "full" }
},
}
Accepted values: "off", "tokens", "full", and the legacy alias "on" (treated as "tokens").
Three distinct session states
A session's responseUsage field has three representable states, each with
different semantics:
| State | Stored value | Effective mode |
|---|---|---|
| Unset / inherit | undefined (absent) | Falls through to messages.responseUsage config default, then off. |
| Explicit off | "off" (stored) | Always off, a non-off config default cannot re-enable the footer. |
| Explicit on | "tokens" or "full" (stored) | That mode, regardless of config default. |
Precedence
Effective mode = session override → channel config entry → default → off.
An explicit /usage off is persisted as the literal value "off" in the
session, not the same as "unset." A non-off messages.responseUsage
default cannot turn the footer back on once the user has explicitly disabled it.
Resetting vs. turning off
/usage offpermanently disables the footer, and a configured non-off default cannot override this setting./usage reset(also known asdefault,inherit,inherited,clear,unpin) removes the session override. After that, the session inherits the effective config default (messages.responseUsage). When no default is configured, the footer remains off.- A complete session reset (
/resetor/new) or a session rollover preserves the explicit usage-mode preference, so the user's display choice continues across session rollovers. Only/usage reset(and its aliases) removes the override.
Toggle behavior
/usage without arguments toggles through: off → tokens → full → off. The cycle begins from the effective current mode (the session override, or the config default if unset), so it always matches what the user currently sees in the footer.
Config
With no configuration, the previous behavior applies (footer off until /usage). Use /usage reset to clear a session override and return to inheriting the configured default.
Custom /usage full footer
/usage tokens always produces a basic Usage: X in / Y out line (plus cache and estimated-cost suffixes when available). Only /usage full generates the richer footer described below.
/usage full displays a built-in compact footer that includes model, reasoning, fast/slow, context window, and cost when those fields are available. No template file is needed for this built-in footer.
messages.usageTemplate is intended solely for advanced custom layouts. Its value is a JSON file path (supports ~) or an inline object, and it replaces the built-in footer when valid. A file path is monitored and reloaded live on change.
{
"messages": {
"usageTemplate": "~/.openclaw/usage-footer.json"
}
}
Missing or empty templates silently fall back to the built-in footer. Unreadable or invalid configured templates (bad JSON, or a shape with no renderable output pieces) also fall back to the built-in footer and produce an operator warning.
Begin custom templates from the built-in shape, then modify the parts you want to change:
{
"schema": "openclaw.usageBar.v1",
"scales": {
"braille": "⠐⡀⡄⡆⡇⣇⣧⣷⣿",
"block": "░▏▎▍▌▋▊▉█",
"shade": "░▒▓█",
"moon": "🌑🌘🌗🌖🌕",
"level": "▁▂▃▄▅▆▇█",
"weather": ["🥶", "☁️", "🌥", "⛅️", "🌤", "☀️"],
"plants": ["", "🍂", "🌱", "☘️", "🍀", "🌿"],
"moons6": ["🌑", "🌚", "🌘", "🌗", "🌖", "🌝"],
},
"aliases": {
"models": {
"claude-opus-4-6": "opus46",
"claude-opus-4-8": "opus48",
"claude-sonnet-4-6": "sonnet46",
"claude-haiku-4-5": "haiku45",
"gpt-5.5": "gpt5.5",
},
"reasoning": {
"off": "🌑",
"minimal": "🌚",
"low": "🌘",
"medium": "🌗",
"high": "🌕",
"xhigh": "🌝",
},
},
"output": {
"sep": "",
"default": [
{ "text": "{model.provider}{identity.emoji|🤖}{model.display_name|alias:models}" },
{ "map": "model.is_fallback", "cases": { "true": "🔄" } },
{ "map": "model.is_override", "cases": { "true": "📌" } },
{ "when": "model.reasoning", "text": "{model.reasoning|alias:reasoning}" },
{ "map": "state.fast_mode", "cases": { "true": "⚡️", "false": "🐌" } },
{
"when": "context.max_tokens",
"text": " | 📚[{context.pct_used|meter:5:braille}]{context.max_tokens|num}",
},
{ "when": "cost.turn_usd", "text": " 💰{cost.turn_usd|fixed:4}" },
],
"surfaces": {
"discord": [
{ "text": "-# -\n" },
{ "text": "-# {model.provider}{identity.emoji|🤖}{model.display_name|alias:models}" },
{ "map": "model.is_fallback", "cases": { "true": "🔄" } },
{ "map": "model.is_override", "cases": { "true": "📌" } },
{ "when": "model.reasoning", "text": "{model.reasoning|alias:reasoning}" },
{ "map": "state.fast_mode", "cases": { "true": "⚡️", "false": "🐌" } },
{
"when": "context.max_tokens",
"text": " | 📚[{context.pct_used|meter:5:braille}]{context.max_tokens|num}",
},
{ "when": "cost.turn_usd", "text": " 💰{cost.turn_usd|fixed:4}" },
],
},
},
}
Shape
{
"schema": "openclaw.usageBar.v1",
"scales": { "<name>": "low-to-high glyphs" }, // string (1 glyph/char) or array
"aliases": { "<table>": { "<value>": "<label>" } },
"output": {
"sep": "", // joins surviving pieces
"default": [/* pieces */], // fallback for any surface
"surfaces": {
"discord": [/* pieces */],
"telegram": [/* pieces */],
},
},
}
Each surface consists of an ordered list of pieces; the engine renders each one, discards empties, and joins the survivors with sep. A surface with no entry uses output.default.
Contract Paths
A piece reads values from the per-turn contract by dot-path. Missing values are empty (so a when guard or a |fallback keeps the piece clean).
| Path | Meaning |
|---|---|
surface | channel identifier (discord, telegram, etc.) |
agentId / chat_type | owner agent identifier / type of chat surface |
model.id / model.display_name / model.provider | model identifier / display label / provider identifier |
model.actual, model.resolved_ref | actual provider/model reference used for this turn |
model.requested | provider/model reference that was requested before any fallback |
model.reasoning | effort level (ranging from off to xhigh) |
model.is_fallback / model.is_override | boolean: whether a fallback was applied / whether the model was pinned |
model.override_source / model.auth_mode | override source label / credential mode (oauth, api-key, token, mixed, aws-sdk, unknown) |
state.fast_mode | boolean: fast versus slow |
state.compactions | compaction count for this session |
context.max_tokens / context.used_tokens / context.pct_used | window budget / tokens occupied / percentage used (0-100) |
usage.input_tokens / usage.output_tokens / usage.total_tokens | cumulative turn totals |
usage.cache_read_tokens / usage.cache_write_tokens | cache-read and cache-write tokens for this turn |
usage.has_tokens / usage.has_split_tokens / usage.has_total_only_tokens | token display guards |
usage.cache_hit_pct | cache-read proportion of total prompt tokens |
usage.last.input_tokens / usage.last.output_tokens / usage.last.cache_hit_pct | applies only to the final model call (also includes cache_read_tokens, cache_write_tokens, total_tokens) |
cost.turn_usd / cost.available | estimated cost per turn / whether a cost table was resolved |
timing.duration_ms | wall-clock duration of the turn |
identity.name / identity.emoji / identity.avatar | agent identity name / emoji / avatar |
session.id | session identifier |
(Provider rate-limit windows are excluded from this contract. No array-valued path currently exists, so an each segment has nothing to iterate over.)
Verbs
Pass a value through verbs from left to right. Any segment that is not a verb acts as the fallback.
| Verb | Effect | Example |
|---|---|---|
num | compact count | 272000 -> 272k |
fixed:N | N decimals (0..100, default 2) | 0.0377 |
dur | seconds to duration | 14820 -> 4h07m |
pct | append % | 96 -> 96% |
inv | 100 - x | for used to remaining |
alias:TABLE | lookup in aliases, echo if unlisted | medium -> 🌗 |
meter:W:SCALE | W-cell glyph bar over a 0-100 value | [⣿⣿⠐⠐⠐] (meter:1 = one glyph) |
Only a full decimal integer between 0 and 100 is valid for fixed:N. Any invalid precision argument causes the interpolation to become empty.
For meter:W:SCALE, only a full decimal integer width from 1 through 100 is accepted. Leaving the width blank applies the default of 5 (meter::braille); invalid widths result in an empty interpolation.
Piece forms
{ "text": "📚 {context.max_tokens|num}" }: literal plus interpolation.{ "when": "<path>", "text": "..." }: render only when the path evaluates to truthy.{ "map": "<path>", "cases": { "true": "⚡", "false": "🐌" } }: map a value to a glyph (a_defaultclause handles values without a match).{ "each": "<array-path>", "item": "{label}" }: iterate over an array-valued path (no current contract path is an array).
Example
{
"schema": "openclaw.usageBar.v1",
"scales": { "braille": "⠐⡀⡄⡆⡇⣇⣧⣷⣿" },
"aliases": { "reasoning": { "medium": "🌗", "high": "🌕" } },
"output": {
"surfaces": {
"discord": [
{ "text": "{model.display_name}" },
{ "when": "model.reasoning", "text": " {model.reasoning|alias:reasoning}" },
{ "map": "state.fast_mode", "cases": { "true": " ⚡", "false": " 🐌" } },
{
"when": "context.max_tokens",
"text": " | 📚 [{context.pct_used|meter:5:braille}]{context.max_tokens|num}",
},
],
},
},
}
produces output such as claude-sonnet-4-6 🌗 🐌 | 📚 [⣿⣿⣿⣿⣧]272k.
Providers + credentials
Usage remains hidden when no usable provider usage authentication can be determined. OpenClaw automatically finds enabled provider plugins that declare contracts.usageProviders and implement both resolveUsageAuth and fetchUsageSnapshot; there is no separate core provider allowlist. The static contract keeps discovery scoped without requiring every provider plugin to be imported. Each plugin is responsible for its own upstream endpoint and response mapping. The shared snapshot keeps plan names, quota windows, balances, spend, and budgets provider-neutral for CLI, app, and Control UI consumers.
- Anthropic (Claude): Auth profiles hold OAuth tokens. When the OAuth token is missing
user:profilescope, the system falls back to aclaude.aiweb session (CLAUDE_AI_SESSION_KEY,CLAUDE_WEB_SESSION_KEY, or asessionKey=cookie inCLAUDE_WEB_COOKIE) if configured. Model-scoped limits and any enabled extra-usage monthly spend or budgets are included whenever Anthropic reports them. An explicit Anthropic Admin API key, or an auto-detectedsk-ant-admin...provider profile, instead presents 30-day organization cost and Messages API history. - ClawRouter: Uses an API key (
CLAWROUTER_API_KEY). Displays a monthly budget window and typed USD budget when set up; otherwise shows aggregate spend along with a request, token, and cost summary. - DeepSeek: API key from environment, config, or auth store (
DEEPSEEK_API_KEY). Shows each currency balance as reported by the provider. - GitHub Copilot: OAuth tokens stored in auth profiles.
- Gemini CLI: OAuth tokens stored in auth profiles.
- MiniMax: API key or a MiniMax OAuth auth profile. OpenClaw treats
minimax,minimax-cn, andminimax-portalas the same MiniMax quota surface, prefers a stored MiniMax OAuth when present, and otherwise falls back toMINIMAX_CODE_PLAN_KEY,MINIMAX_CODING_API_KEY, orMINIMAX_API_KEY. Usage polling derives the Coding Plan host frommodels.providers.minimax-portal.baseUrlormodels.providers.minimax.baseUrlwhen set, and otherwise uses the MiniMax CN host. MiniMax's rawusage_percent/usagePercentfields represent remaining quota, so OpenClaw inverts them before display; count-based fields take priority when present.- Window labels come from provider hours or minutes fields when available, then
fall back to the
start_time/end_timespan. - If the coding-plan endpoint returns
model_remains, OpenClaw prefers the chat-model entry, derives the window label from timestamps when explicitwindow_hours/window_minutesfields are absent, and includes the model name in the plan label.
- Window labels come from provider hours or minutes fields when available, then
fall back to the
- OpenAI (Codex/ChatGPT plan): Auth profiles hold OAuth tokens (the
ChatGPT-Account-Idheader is sent when an account id exists). Shows the ChatGPT plan, resettable Codex windows, and a credit balance when reported. Credits stay as provider credits; OpenClaw does not label them as dollars.OPENAI_ADMIN_KEYadds 30-day organization cost and completions-usage history when the key has Usage Dashboard access. Inference credentials are never forwarded to organization APIs. - OpenRouter: API key or OAuth-backed API key (
OPENROUTER_API_KEYor an auth profile). Merges the account credits endpoint with the key quota endpoint, so account balance, spend, key budget, and daily, weekly, or monthly usage appear when the credential can access them. Either endpoint can independently enrich the snapshot. - Venice: API key from environment, config, or auth store (
VENICE_API_KEY). Shows USD and DIEM balances along with DIEM epoch allocation usage when reported. - Xiaomi MiMo: Two separate usage surfaces. Pay-as-you-go uses an API key
(
XIAOMI_API_KEY); the Token Plan uses a different key (XIAOMI_TOKEN_PLAN_API_KEY). Neither currently reports quota windows. - z.ai: API key from environment, config, or auth store (
ZAI_API_KEYorZ_AI_API_KEY).