Skip to content
TokenCenter Get API key

Docs › Errors

Errors

Every status and error code the API returns, what caused it and what to do next.

Error format

Errors come back as JSON with an HTTP status and a machine-readable code. Branch on code, not on the message text.

JSON
{
  "error": {
    "code": "insufficient_credits",
    "message": "…"
  }
}

Status codes

StatusCodeWhat happenedWhat to do
400bad_jsonThe body isn’t valid JSON.Fix the JSON and send Content-Type: application/json.
400missing_modelNo model in the chat request.Add a model ID.
400missing_messagesmessages is missing or not an array.Send an array of messages.
400bad_packUnknown pack_id on checkout.Use mini, maker or studio.
400amount_mismatchamount_usd doesn’t match the pack price.Leave amount_usd out, or send the exact price.
400bad_tierFounding tier isn’t paid or verified.Fix the tier.
401bad_keyKey missing, malformed, unknown or revoked.Check the Authorization: Bearer ck_… header. Don’t retry.
402insufficient_creditsYour balance is $0, or it can’t cover the hold for this call.Add credit on Pricing, lower max_tokens, or pick a cheaper model. See holds.
403anonymous_disabledNew-key sign-up is switched off.Try again later.
404not_foundNo such endpoint.Check the path and method.
404model_not_foundThe model ID isn’t in the catalog (chat request or GET /v1/models/{id}).Use an ID from GET /v1/models.
404event_not_foundGET /v1/logs/{id} found nothing among your recent events.Check the event ID.
409already_foundingThe account already holds that Founding tier.Nothing to do.
429key_cap_exhaustedThis key hit its daily spending cap.Wait for 00:00 UTC or raise the cap.
429rate_limit_exceeded_keyMore than 60 requests in a minute on this key.Wait Retry-After seconds (60).
429rate_limit_exceeded_ipMore than 120 requests in a minute from this IP.Wait Retry-After seconds (60).
429anonymous_ip_limit, anonymous_daily_capToo many new keys today.Wait (Retry-After: 3600) or reuse your key.
500internal_errorUnexpected server error.Retry once after a short wait.
502rail_errorThe model provider failed. Nothing was charged; the hold was returned.Retry with backoff, or try another model.
502processor_errorStripe couldn’t create the checkout.Retry shortly.
503no_supplyThe model is listed but its provider isn’t available right now.Try again later or pick another model from GET /v1/models.
503no_processorPayments are temporarily unavailable.Try again later.

Out of credit: 402

Running out of credit always returns 402 insufficient_credits. The call was refused before it reached the model, so nothing was charged:

  • Balance $0: the message reads “Out of credit. Add credit at https://tokenctr.com/pricing.html”.
  • Some credit, but not enough for the hold on a request with a large max_tokens: lower max_tokens or add credit. If you leave max_tokens out, the reply is capped to what your balance can pay for instead of being refused.

The response includes x-cosmos-credit-balance so you can show the user their balance. 429 is only used for rate limits and key spending caps.

Rate limits and retries

Chat completions allow 60 requests per minute per key and 120 per minute per IP address. Over the limit you get 429 with Retry-After: 60.

  • Retry: rate_limit_exceeded_* (after Retry-After), 502 rail_error and 500 (with exponential backoff).
  • Don’t retry: 400, 401, 402, 404, key_cap_exhausted. They’ll fail the same way until something changes.
  • The OpenAI SDKs retry 429 and 5xx twice by default. Set max_retries=0 if you’d rather handle out-of-credit errors yourself.
Python
import os
from openai import OpenAI, APIStatusError

client = OpenAI(base_url="https://api.tokenctr.com/v1", api_key=os.environ["TOKENCENTER_API_KEY"], max_retries=0)
try:
    resp = client.chat.completions.create(model="mistralai/ministral-3b-2512",
        messages=[{"role": "user", "content": "Hi"}], max_tokens=50)
except APIStatusError as err:
    code = (err.body or {}).get("error", {}).get("code") if isinstance(err.body, dict) else None
    if err.status_code == 402 or code == "insufficient_credits":
        print("Out of credit. Top up at https://tokenctr.com/pricing.html")
    else:
        raise

Streaming requests

With "stream": true, a successful call returns 200 with text/event-stream in the OpenAI format: chat.completion.chunk events (role, then the text, then a final chunk with finish_reason and usage) and data: [DONE]. The reply is generated in full and then sent, so the chunks arrive together rather than token by token. Errors on a streaming request are returned as normal JSON with their real status (for example 402), not as an event, so the OpenAI SDKs raise them as usual.