Docs
Everything you need to call the gateway: base URL, authentication, endpoints, errors, troubleshooting and limits.
Quick start
- Sign up and verify your email: your first API Key is created for you.
- Top up your Balance (or redeem a Voucher).
- Point your tool or SDK at the base URL below with your API Key.
curl https://api.centrouter.com/v1/chat/completions \
-H "Authorization: Bearer $CENTROUTER_API_KEY" \
-H "Content-Type: application/json" \
-d '{"model": "gpt-6-luna", "messages": [{"role": "user", "content": "Hello"}]}'Base URL
OpenAI-compatible clients use the URL ending in /v1. Anthropic clients (Claude Code, the Anthropic SDK) use the root without /v1; they add /v1/messages themselves.
https://api.centrouter.com/v1
https://api.centrouter.com
Authentication
Send your API Key with every Request. Both headers work on every endpoint; keys start with gsk_. Keep keys secret: anyone with a key spends your Balance.
Authorization: Bearer gsk_… # or x-api-key: gsk_…
Endpoints
Use a model’s ID from the Models or Pricing page. Streaming works on every chat endpoint.
| Endpoint | What it does |
|---|---|
| POST /v1/chat/completions | Chat Completions (OpenAI format), streaming or not. |
| POST /v1/messages | Messages (Anthropic format), used by Claude Code. |
| POST /v1/messages/count_tokens | A free estimate of a Messages request’s input Tokens (about 4 characters per Token), which Claude Code asks for. Charges always use the usage the model reports. |
| POST /v1/responses | Responses (OpenAI format), used by Codex CLI. |
| POST /v1/embeddings | Embeddings for models that support them. |
| GET /v1/models | Every model in the Catalog, with the prices your key is charged and what each supports. |
| GET /v1/balance | Your Balance and the amount held by running Requests. |
Errors
Errors use the format of the endpoint you called: OpenAI endpoints return error.code, Anthropic endpoints return error.type. Every error has a message that says how to fix it. Select a code for its causes and the steps that fix it.
| Code | HTTP | Meaning |
|---|---|---|
| invalid_api_key | 401 | The key is missing, malformed, expired or revoked. Create a new one on the API Keys page. |
| account_blocked | 403 | The account is blocked: the gateway rejects all its API Keys. |
| route_not_found | 404 | No route at that method and path. Check the base URL: OpenAI format ends in /v1, Anthropic format does not. |
| model_not_found | 404 | No visible Catalog model has that ID. List models with GET /v1/models. |
| model_not_allowed | 403 | This key is restricted to other models. Change its model list on the API Keys page. |
| spend_limit_exceeded | 403 | The key reached its daily, monthly or total Spend Limit. Raise the limit or wait for the next period. |
| insufficient_balance | 402 | Your Balance cannot cover this Request. Top up and retry. |
| rate_limit_exceeded | 429 | Too many Requests per minute, or too many at once. Retry after a short wait. |
| invalid_request | 400 | The body is not valid for this endpoint. Check the message for the field. |
| upstream_error | 502 | No model source could answer. You were not charged; retry, and check the Status page. |
| upstream_rate_limited | 429 | Model sources are busy. You were not charged; retry after a short wait. |
| request_too_large | 413 | The body is over 32 MB, usually large images. You were not charged; send smaller or fewer images. |
Once a Request is accepted, its response has an x-request-id header (req_ followed by 32 characters), whether it succeeded, streamed or failed at the model. It is the Request’s ID in Activity, where you can search for it; quote it when you ask for help. Error responses for such a Request carry it in the body too, as request_id. A Request rejected before that (wrong key, blocked account, invalid body, unknown route or model, limits, low Balance) has no ID.
Troubleshooting
Start with the error’s code (OpenAI format: error.code) or type (Anthropic format: error.type), then follow the steps. Each entry has its own link.
Common problems
The tool sent a model ID that is not in the Catalog, often its own built-in default. Tell the tool which Catalog model to use:
- List the IDs you can call:
npx centrouter models. - Claude Code: set both
ANTHROPIC_MODELandANTHROPIC_DEFAULT_HAIKU_MODELto a Catalog ID in~/.claude/settings.json. - Codex CLI: set
modelin~/.codex/config.toml. - OpenCode: set
modeltocentrouter/followed by the ID inopencode.json, and list the ID under the provider’smodels. - Or let the CLI write it:
npx centrouter setup claude --model ID(orcodex,opencode).
A 404 that names a route, not a model, means the base URL is wrong: OpenAI-format tools need https://api.centrouter.com/v1, Anthropic-format tools https://api.centrouter.com.
The gateway did not get a valid API Key from the tool. The error message says whether no key arrived or the key is wrong.
- Claude Code reads the key from
ANTHROPIC_AUTH_TOKENin~/.claude/settings.json.ANTHROPIC_BASE_URLmust behttps://api.centrouter.com, without /v1. - Codex CLI and OpenCode read the key from the
CENTROUTER_API_KEYenvironment variable. Addexport CENTROUTER_API_KEY=gsk_…to your shell profile, then open a new terminal. - Test the key outside the tool:
curl https://api.centrouter.com/v1/models -H "Authorization: Bearer $CENTROUTER_API_KEY" - If the key is lost or revoked, run
npx centrouter loginfor a new one, then run setup again.
A streamed response ends early for one of these reasons:
- The model sent nothing for 2 minutes, so the gateway ended the stream.
- The Request reached the 10-minute limit.
- The connection to the model source broke, or your tool cancelled the Request.
- The answer reached the output limit (
max_tokens). The response then ends normally, with a stop reason that says so.
You are charged only for the Tokens already delivered. Activity lists a stream that broke as Cancelled.
Retry the Request. If answers stop at the output limit, raise max_tokens. If one model keeps cutting off, check the Status page and quote the response’s x-request-id when you ask for help.
While a Request runs, the gateway holds the most it could cost: your prompt, counted generously, plus the whole output limit (max_tokens, or the model’s maximum when you send none). The Balance page shows the amount as “Held for running Requests”.
When the Request ends, you are charged for the Tokens actually used and the rest returns to your available Balance. A drop you see during a Request is mostly a hold, not a charge.
A hold left by a Request that never finished is released automatically within about 20 minutes.
If the charges themselves are higher than you expected, open Activity: each Request shows its Tokens by kind and its cost. Coding agents resend the conversation on every turn, so input Tokens grow as a session gets longer, and cache writes are charged at the cache-write price.
To cap what a key can spend, give it a Spend Limit on the API Keys page.
Error codes
invalid_api_key
HTTP 401Anthropic format: authentication_error
No API Key reached the gateway, or the key sent is mistyped or revoked. The message says which.
How to fix it
- Send the key as
Authorization: Bearer gsk_…orx-api-key: gsk_…. - If your tool reads the key from an environment variable, check that it is set in the shell that starts the tool:
echo $CENTROUTER_API_KEY. - Look at the key on the API Keys page. A revoked key cannot be restored; create a new one.
- Test the key outside your tool:
curl https://api.centrouter.com/v1/models -H "Authorization: Bearer $CENTROUTER_API_KEY"
account_blocked
HTTP 403Anthropic format: permission_error
The account is blocked, so the gateway rejects every one of its API Keys.
How to fix it
- Write to support@centrouter.com to restore access.
- A new API Key does not help: the block is on the account, not on a key.
route_not_found
HTTP 404Anthropic format: not_found_error
The gateway has no route for that method and path. The usual cause is the base URL.
How to fix it
- OpenAI-format clients need the base URL that ends in /v1:
https://api.centrouter.com/v1 - Anthropic-format clients need the root without /v1:
https://api.centrouter.com. With /v1 they call /v1/v1/messages. - Check the method:
/v1/modelsand/v1/balanceare GET, every other endpoint is POST.
model_not_found
HTTP 404Anthropic format: not_found_error
No model in the Catalog has the ID you sent.
How to fix it
- Copy the ID from the Pricing page, or list the IDs with
GET /v1/models. When a Catalog ID is close to yours, the message suggests it. - If you did not choose the ID, your tool sent its own default: set the model in the tool’s config (see “My tool says the model was not found”).
model_not_allowed
HTTP 403Anthropic format: permission_error
The API Key is restricted to a list of models, and this model is not on the list.
How to fix it
- On the API Keys page, open the key’s settings and add the model, or clear the list to allow every model.
- Or send the Request with another key.
- A change reaches the gateway within 15 seconds.
spend_limit_exceeded
HTTP 403Anthropic format: permission_error
The API Key reached its daily, monthly or total Spend Limit; the message says which. Amounts held for running Requests count toward the limit.
How to fix it
- Raise or remove the limit in the key’s settings on the API Keys page.
- Or wait: a daily limit starts again at 00:00 UTC, a monthly limit on the first day of the month (UTC). A total limit never resets.
- Or send the Request with another key.
- Near the limit, a smaller
max_tokensholds less and may still fit.
insufficient_balance
HTTP 402Anthropic format: invalid_request_error
Your available Balance is lower than the most this Request could cost. The message states both amounts.
How to fix it
- Add Balance on the Balance page.
- Send a smaller
max_tokens: the amount needed is the worst case for your prompt and your output limit. - If other Requests are running, wait for them to finish. The amounts held for them are released.
- Check with
GET /v1/balance:availableis what a new Request can use,reservedis held for running ones.
rate_limit_exceeded
HTTP 429Anthropic format: rate_limit_error
The API Key went over its rate limit. By default that is 60 Requests per minute, or 5 running at once.
How to fix it
- Wait the number of seconds in the
Retry-Afterheader, then retry. - Run fewer Requests in parallel, for example fewer sub-agents.
- If you need a higher limit, write to support@centrouter.com.
invalid_request
HTTP 400Anthropic format: invalid_request_error
The body is not valid JSON, a field is missing or wrong for this endpoint, or the model rejected the request. The message names the field or the status. You were not charged.
How to fix it
- Correct the field the message names. Chat Completions and Responses follow the OpenAI format; Messages follows the Anthropic format.
- If the model rejected the request, check that it supports what you sent (images, tools, reasoning: see its tags on the Pricing page) and that the prompt fits its context length.
upstream_error
HTTP 502Anthropic format: api_error
No model source could answer, or the answer could not be read. You were not charged.
How to fix it
- Retry after a moment.
- Check the model on the Status page.
- Try the other format’s endpoint, or another model. A model can be offered in the OpenAI format only, or in the Anthropic format only.
- If it keeps failing, write to support@centrouter.com with the
x-request-idof a failed response.
upstream_rate_limited
HTTP 429Anthropic format: overloaded_error
The model’s sources are limiting requests right now. You were not charged.
How to fix it
- Retry after a short wait, and wait longer between further retries.
- If it continues, use another model for now and check the Status page.
request_too_large
HTTP 413Anthropic format: request_too_large
The request body is over 32 MB, usually because of large images or files. It was refused before reaching a model. You were not charged.
How to fix it
- Send fewer or smaller images (resize or compress them) or split the work over several Requests.
- Images sent as URLs instead of base64 keep the body small.
Spend Limits
Each API Key can have a daily, monthly and total Spend Limit, and a list of models it may call. The lower of a Spend Limit and your Balance wins, so a leaked key can only spend what you allowed. Set them on the API Keys page; periods are UTC days and months.
API features
The gateway passes your request to the model as you wrote it and bills the Tokens the model’s provider reports. These features work as the model’s own API documents them.
- Tool calling
- Send tools as usual (OpenAI tools / tool_choice, Anthropic tools). They pass through unchanged; tool calls and their arguments are output Tokens. Models that support tools carry the Tools tag on the Models page.
- Images (vision)
- Send images as image_url parts (OpenAI) or image blocks (Anthropic) to models with the Vision tag. A request body may be up to 32 MB; image URLs keep it small.
- Prompt caching
- Cache reads cost the cache price on the Models page, far below input. Anthropic-format requests mark what to cache with cache_control; OpenAI-format models cache long repeated prefixes on their own where they support it. Activity shows each Request as input · cache · output.
- Reasoning
- Pass reasoning settings as the model documents them (reasoning_effort, thinking). Reasoning Tokens are part of output and billed as output; Activity shows them separately so you can see what they cost.
- Output limit and the hold
- Without max_tokens a Request may write up to the model’s max output, and the gateway holds enough Balance for that while it runs; the unused part returns as soon as it ends. Each model’s details show that minimum. Set max_tokens to hold less.
- What you are charged
- Tokens × the Sell Price, from the counts the model’s provider reports. Saved compares each Request with the OpenRouter Price. Prompts and replies are never stored, only Tokens and prices.
SDK examples
from openai import OpenAI
client = OpenAI(base_url="https://api.centrouter.com/v1", api_key="YOUR_API_KEY")
reply = client.chat.completions.create(
model="gpt-6-luna",
messages=[{"role": "user", "content": "Hello!"}],
)
print(reply.choices[0].message.content)import OpenAI from 'openai'
const client = new OpenAI({ baseURL: 'https://api.centrouter.com/v1', apiKey: 'YOUR_API_KEY' })
const reply = await client.chat.completions.create({
model: 'gpt-6-luna',
messages: [{ role: 'user', content: 'Hello!' }]
})
console.log(reply.choices[0].message.content)from anthropic import Anthropic
client = Anthropic(base_url="https://api.centrouter.com", api_key="YOUR_API_KEY")
reply = client.messages.create(
model="gpt-6-luna",
max_tokens=1024,
messages=[{"role": "user", "content": "Hello!"}],
)
print(next(block.text for block in reply.content if block.type == "text"))import Anthropic from '@anthropic-ai/sdk'
const client = new Anthropic({ baseURL: 'https://api.centrouter.com', apiKey: 'YOUR_API_KEY' })
const reply = await client.messages.create({
model: 'gpt-6-luna',
max_tokens: 1024,
messages: [{ role: 'user', content: 'Hello!' }]
})
const text = reply.content.find(block => block.type === 'text')
console.log(text?.type === 'text' ? text.text : '')CLI
The CLI logs in through your browser and writes the config for Claude Code, Codex or OpenCode.
npx centrouter login
npx centrouter setup claude npx centrouter setup codex npx centrouter setup opencode
npx centrouter balance npx centrouter models
For coding agents
Point your coding agent at llms.txt: it describes the service, the endpoints and the model list in plain text, with a prompt that lets the agent configure itself.
Open llms.txt