Public, no-auth, always-cached aggregator for AI service usage. Polls Claude / Codex / Z.ai / OpenRouter / OpenAI in the background and serves the latest snapshot from memory — every request is O(0) network.
GET /api/usage
Returns per-provider:
data— provider-specific shape (utilization %, reset times, credits, etc.)fetchedAt— when this snapshot was last refreshederror— null on successintervalSec— current polling interval (self-tunes)nextFetchAt— when the next refresh fires
Codex usage exposes semantic five_hour and seven_day windows. Either may be
null when OpenAI is not reporting that limit. The deprecated primary and
secondary fields remain as compatibility aliases for five_hour and
seven_day; they do not mirror OpenAI's variable raw slot positions.
Each provider runs an independent Poller:
- starts at
POLL_TARGET_SECONDS(default 2.5 min) - after 3 consecutive successes, walks toward
POLL_FLOOR_SECONDS(1 min) - on 429: honors
Retry-After(or doubles) - on other errors: doubles, capped at
POLL_CEILING_SECONDS(10 min)
API keys load from the shared /home/node/workspace/.env on startup. Override
the path with SHARED_ENV_FILE. Existing process.env values win, so Dokploy
env vars still override the file.
| Provider | Source |
|---|---|
| Claude Max | /home/node/auth/.claude/.credentials.json (OAuth, auto-refreshes) |
| Claude Max #2 (optional) | /home/node/auth/.claude2/.credentials.json (OAuth, auto-refreshes) |
| Codex | /home/node/auth/.codex/auth.json (OAuth, refreshes on 401) |
| Z.ai | ZAI_API_KEY env |
| OpenRouter | OPENROUTER_API_KEY env |
| OpenAI | OPENAI_ADMIN_KEY env |
OAuth credentials live in the dedicated usage-api-auth Docker volume mounted
at /home/node/auth. During migration, the entrypoint seeds an empty volume
from the former ai-sessions-date bind mounts:
-v $HOME/.claude:/home/node/.claude
-v $HOME/.claude2:/home/node/.claude2
-v $HOME/.codex:/home/node/.codex
After the initial copy, Usage API refreshes and atomically rewrites its own credential files without sharing token state with other containers.
A second Claude account is opt-in. At startup the server checks
~/.claude2/.credentials.json (override with CLAUDE2_CREDENTIALS_PATH):
- File exists (or env var set) → a
claude2poller runs and/api/usageincludes aclaude2key right afterclaude, same shape. The dashboard shows a "claude #2" card and "Claude #2 …" history series. - Unconfigured → the
claude2key is entirely absent from the response (not null, not an error) and nothing about a second account appears in the UI. CLAUDE2_CREDENTIALS_PATHexplicitly set → the key is always present, so a bad path/mount surfaces as a visible error entry instead of silently disappearing.CLAUDE2_ENABLED=false(or0/no/off) → hard kill switch: the account is fully disabled and absent even when its credentials file exists. Use this to turn the second account off from the environment without removing the file.
npm install
cp .env.example .env # edit
npm run dev
curl http://localhost:3000/api/usageApp: usage-api
Domain: usage.etdofresh.com
Named volumes:
usage-api-authmounted at/home/node/authfor OAuth credentialsusage-api-datamounted at/home/node/datafor usage history
The main Usage API image includes the Claude and Codex CLIs. Open the Usage API application's Dokploy terminal and use:
usage-auth status
usage-auth claude
usage-auth claude2
usage-auth codexThe helper reads and writes the existing usage-api-auth volume. The Codex
command uses device-code authentication for the headless container.