codai docs
Gateway

Limits & pricing

Rate limits, spend caps, task caps, the cost receipt and the error contract.

codai bills per verified successful task, not per token — the plan comparison is on codai.ro/pricing and your live spend is in hub.codai.ro. This page covers what the gateway enforces on every request and how it tells you when you hit a wall.

Rate limits

Limits are per user, across all of that user's keys, and sliding-window. Each response carries the current state:

x-ratelimit-limit-minute: 120
x-ratelimit-remaining-minute: 117
x-ratelimit-reset-minute: 1790000060
x-ratelimit-limit-hour: 3000
x-ratelimit-remaining-hour: 2951
x-ratelimit-reset-hour: 1790003600

reset values are Unix timestamps in seconds. The numeric limits come from your plan; read them from the headers rather than hard-coding them.

Three windows are enforced:

WindowWhat it countsError code
Requests per minute / per hourEvery /v1/* and /mcp request.rate_limit_exceeded
Input tokens per minutePrompt tokens actually recorded, with cache writes at 25 % weight and cache reads excluded entirely. Default 1 500 000/min unless the plan says otherwise.rate_limit_exceeded
Output tokens per minuteCompletion tokens, per user.rate_limit_exceeded

Because cache reads are free for the token window, keeping prompt caching on is also how you stay under the limit in long agent sessions.

Honour Retry-After

On every 429 the gateway sets Retry-After to the exact number of seconds until the oldest entry ages out of the window — not a flat 60. Clients that honour it find a free slot on the first retry; clients that ignore it hammer the gateway and stay blocked. The SDKs retry 429 and 5xx with backoff automatically.

Task caps

A task starts on a fresh user turn; tool-result continuations and follow-ups inside the same conversation do not start new ones. Plans include a number of tasks per day (or per month on the free tier). When you exhaust them:

  • The gateway returns 429 with code quota_exceeded and a Retry-After pointing at the reset.
  • Conversations you already started keep working — only brand-new tasks are paused.
  • Utility calls on codai-fast with X-Codai-No-Task: 1, embeddings, audio and realtime never count.

quota_exceeded is deliberately a 429, not a 403: IDE clients treat it as "back off and retry later" instead of retrying every few seconds.

Spend caps

Accounts may carry a daily and a monthly spend cap in USD, summed across all keys. When enabled, each response reports where you stand:

x-codai-spend-day-usd: 3.42
x-codai-spend-day-cap-usd: 20.00
x-codai-spend-month-usd: 41.90
x-codai-spend-month-cap-usd: 300.00

Crossing a cap returns 429 rate_limit_exceeded with a message naming the window (daily caps reset at midnight UTC, monthly on the 1st UTC). Set per-key budgets in the hub under Keys — the key's budget is a floor under the account cap, never above it.

Per-request ceiling

A single top-level request — including any best-of candidates, judge calls and sub-agent delegations the gateway spawns for it — aborts early with a partial answer once its running cost crosses 5 USD. Plain chat never gets near this; it exists so a runaway agent loop cannot bill an unbounded amount in one call.

Body size

Request bodies on /v1/* are capped at 12 MB. Larger bodies get 413 with code payload_too_large. Audio uploads are separately capped at 25 MB on /v1/audio/transcriptions.

Cost receipt

Every chat or messages response carries the cost of that request when it can be known before the first byte:

x-codai-upstream: claude-opus-5
x-codai-cost-micro-usd: 18420

Micro-USD are integers — 18420 is $0.01842. On native streams the count is not known at header time, so you get x-codai-cost-estimate: pending instead and the exact figure lands in the receipt endpoint:

# last 24 hours (default)
curl https://ai.codai.ro/v1/receipt -H "Authorization: Bearer $CODAI_API_KEY"

# one session
curl "https://ai.codai.ro/v1/receipt?session_id=my-project" -H "Authorization: Bearer $CODAI_API_KEY"

# since a timestamp
curl "https://ai.codai.ro/v1/receipt?since=2026-09-01T00:00:00Z" -H "Authorization: Bearer $CODAI_API_KEY"
{
  "events": 142,
  "tasks": 9,
  "cost_micro_usd_total": 1834200,
  "cost_usd": 1.8342,
  "prompt_tokens": 2210034,
  "completion_tokens": 41890,
  "cached_read_tokens": 1988001,
  "cache_write_tokens": 120433,
  "by_upstream": [
    { "upstream_model": "claude-opus-5", "events": 130, "cost_micro_usd": 1801000 },
    { "upstream_model": "claude-haiku-4-5", "events": 12, "cost_micro_usd": 33200 }
  ],
  "window": { "from": "2026-09-22T09:00:00.000Z", "to": "2026-09-23T09:00:00.000Z", "session_id": null }
}

tasks is the number of billable task starts in the window. session_id matches either the X-Codai-Session-Id you sent or the key the gateway derived for header-less clients. Token counts are the real upstream figures — parsed from message_delta.usage on Anthropic streams and from the final usage chunk on OpenAI streams — not the headline number a client might display.

Prices per upstream model are shown in the hub next to each usage row; the gateway has no separate pricing endpoint.

Error contract

Errors use the OpenAI envelope on every surface, so an OpenAI-compatible client parses them unchanged:

{
  "error": {
    "message": "You're sending requests a little too fast — the limit is 120 requests per minute …",
    "type": "rate_limit_error",
    "code": "rate_limit_exceeded"
  }
}

code is stable and machine-readable; message is for humans and may change. Validation failures add a details object with the field paths that failed. Upstream provider names are scrubbed from messages — you see what failed, never which cloud served it.

HTTPcodetypeWhen
400bad_requestinvalid_request_errorInvalid JSON, schema violation (see details), unsupported Responses feature, X-Codai-No-Task on a non-utility alias, unknown audio model.
401invalid_api_keyinvalid_request_errorMissing, malformed, revoked or unknown key; ephemeral token on the wrong surface.
402subscription_inactivesubscription_requiredThe key's subscription is not active.
403forbiddeninvalid_request_errorModel not in the key's allowlist; agent mode without a tools-enabled tier; ephemeral token trying to mint tokens.
404not_foundinvalid_request_errorUnknown route or method. Check the base URL — /v1 vs root is the usual cause.
413payload_too_largeinvalid_request_errorBody over 12 MB.
429rate_limit_exceededrate_limit_errorRequest, input-token or output-token window exhausted; daily or monthly spend cap reached. Has Retry-After.
429quota_exceededrate_limit_errorPlan task allowance used up. Has Retry-After pointing at the reset.
502upstream_errorupstream_errorThe upstream provider failed after the gateway's own retries and failover. Safe to retry.
500internal_errorserver_errorGateway fault. Reported automatically; safe to retry.

Mid-stream failures cannot change the status code (headers are already sent); the gateway closes the stream with a clean stop frame and records the turn as stalled. Retry the request — prompt caching makes it cheap.

What is recorded on failure

Failed requests are recorded too, with the error code and is_task_start = 0 — a failed attempt never bills a task, and any task consumption reserved before dispatch is refunded.

On this page