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: 1790003600reset 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:
| Window | What it counts | Error code |
|---|---|---|
| Requests per minute / per hour | Every /v1/* and /mcp request. | rate_limit_exceeded |
| Input tokens per minute | Prompt 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 minute | Completion 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
429with codequota_exceededand aRetry-Afterpointing at the reset. - Conversations you already started keep working — only brand-new tasks are paused.
- Utility calls on
codai-fastwithX-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.00Crossing 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: 18420Micro-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.
| HTTP | code | type | When |
|---|---|---|---|
| 400 | bad_request | invalid_request_error | Invalid JSON, schema violation (see details), unsupported Responses feature, X-Codai-No-Task on a non-utility alias, unknown audio model. |
| 401 | invalid_api_key | invalid_request_error | Missing, malformed, revoked or unknown key; ephemeral token on the wrong surface. |
| 402 | subscription_inactive | subscription_required | The key's subscription is not active. |
| 403 | forbidden | invalid_request_error | Model not in the key's allowlist; agent mode without a tools-enabled tier; ephemeral token trying to mint tokens. |
| 404 | not_found | invalid_request_error | Unknown route or method. Check the base URL — /v1 vs root is the usual cause. |
| 413 | payload_too_large | invalid_request_error | Body over 12 MB. |
| 429 | rate_limit_exceeded | rate_limit_error | Request, input-token or output-token window exhausted; daily or monthly spend cap reached. Has Retry-After. |
| 429 | quota_exceeded | rate_limit_error | Plan task allowance used up. Has Retry-After pointing at the reset. |
| 502 | upstream_error | upstream_error | The upstream provider failed after the gateway's own retries and failover. Safe to retry. |
| 500 | internal_error | server_error | Gateway 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.