Back to blog

guideagents

Running Claude Code on your MirAPI balance

Point Claude Code at MirAPI with three environment variables, add Codex CLI with one TOML block, and let both agents spend from the same prepaid balance.

Coding agents burn tokens fast, and they rarely stay on one model. Running Claude Code against Anthropic, Codex against OpenAI and everything else somewhere third means three accounts, three keys and three invoices to reconcile at the end of the month. MirAPI collapses that into one key and one balance — Claude Code included, because MirAPI serves the Anthropic Messages API natively rather than translating it.

Here is the whole setup.

Claude Code

Claude Code talks to POST /v1/messages, which MirAPI implements directly. Three environment variables and you are done:

Terminal window
export MIRAPI_API_KEY=sk-...
export ANTHROPIC_BASE_URL="https://api.mirapi.ai"
export ANTHROPIC_AUTH_TOKEN="$MIRAPI_API_KEY"
export ANTHROPIC_MODEL="deepseek-chat"

Two details that cost people an afternoon:

  • Do not append /v1 to the base URL. Anthropic clients build the full path themselves, so https://api.mirapi.ai is the entire value. Leave /v1 on the end and the request resolves to /v1/v1/messages, which 404s.
  • Define MIRAPI_API_KEY before ANTHROPIC_AUTH_TOKEN. The token variable expands when the profile is sourced. If the key is not defined yet you export an empty string, and every request comes back 401.

Prefer ANTHROPIC_AUTH_TOKEN over ANTHROPIC_API_KEY: the first sends Authorization: Bearer, the second sends x-api-key. MirAPI accepts both, but if ANTHROPIC_API_KEY still holds a real Anthropic key from an earlier setup, Claude Code may prompt or fall back to Anthropic. Set it to an empty string if you are not sure.

Reload the shell, run claude, and check /status — the base URL and auth token it reports should be MirAPI’s.

Scoping it to one project

If you would rather not touch your shell profile, put the same values in .claude/settings.local.json at the project root:

{
"env": {
"ANTHROPIC_BASE_URL": "https://api.mirapi.ai",
"ANTHROPIC_AUTH_TOKEN": "sk-...",
"ANTHROPIC_MODEL": "deepseek-chat"
}
}

Codex CLI

Codex is not environment-variable driven. It reads TOML and expects the Responses API, so MirAPI goes in as a custom provider:

model = "deepseek-chat"
model_provider = "mirapi"
[model_providers.mirapi]
name = "MirAPI"
base_url = "https://api.mirapi.ai/v1"
env_key = "MIRAPI_API_KEY"
wire_api = "responses"

Note the asymmetry: Codex wants /v1 on the base URL, Claude Code must not have it. That is the difference between a client that builds the path for you and one that appends to whatever you hand it.

To leave your default config untouched, save the block as ~/.codex/mirapi.config.toml and start with codex -p mirapi.

Choosing a model

Both agents take a model name from the catalogue, and GET /v1/models is authoritative — it returns the exact id to pass along with context length and per-million pricing:

Terminal window
curl https://api.mirapi.ai/v1/models -H "Authorization: Bearer $MIRAPI_API_KEY"

Two constraints when picking one:

  • Your key has to be whitelisted for that model, or the request comes back 403.
  • Both agents lean hard on tool calling. A model that cannot call tools will answer your first message and then be useless as an agent.

Keeping a long run from surprising you

  • Cap the key, not your nerves. A MirAPI key can carry a quota cap, a validity period, an IP allowlist and a model whitelist. Give a long-running agent its own key with a quota: worst case it stops, instead of eating the balance.
  • Reasoning tokens bill as output. On a thinking-heavy model the output side dominates the invoice, so the input rate is not the number to plan around.
  • Cache reads cost less than fresh input. Agents replay a large, stable prefix every turn, which is exactly the shape prompt caching rewards — see Prompt caching.
  • Reconcile from the logs. The console billing log, GET /api/usage/token for account totals, and GET /api/log/token for per-request detail all report the same data.

When something breaks

Code What it means What to do
401 Key missing, invalid or unknown Check the variable is set and non-empty
403 Valid key, refused request — balance, whitelist, quota or IP Top up, or lift the restriction on the key
429 Rate limited Back off with jitter (1s → 2s → 4s). No Retry-After is sent and the gateway does not retry for you

One harmless error worth recognising: Claude Code calls /v1/messages/count_tokens during startup and MirAPI returns 404. It is not implemented, and nothing depends on it.

Every error message ends with a request ID. Keep it — that ID is what pins down the exact request and its billing entry in the logs.