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 sliding-window and come from your plan. Each key has its own window (keys minted by an app share one window per subscription). These are the plan values:
| Plan | Requests / min | Requests / hour | Input tokens / min | Output tokens / min | Own MCP servers | Seats |
|---|---|---|---|---|---|---|
| Free | 5 | 150 | 1,500,000 | 20,000 | — | 1 |
| Lite | 10 | 300 | 1,500,000 | 100,000 | — | 1 |
| Student | 20 | 600 | 1,500,000 | 100,000 | 20 | 1 |
| codai Services | 20 | 600 | 1,500,000 | 100,000 | 20 | 1 |
| Pro | 20 | 600 | 1,500,000 | 100,000 | 20 | 1 |
| Builder | 100 | 3,000 | 1,500,000 | 100,000 | Unlimited | 1 |
| Max | 300 | 9,000 | 1,500,000 | 100,000 | Unlimited | 1 |
| Team | 300 | 9,000 | 1,500,000 | 100,000 | Unlimited | 2 |
| Team Max | 300 | 9,000 | 1,500,000 | 100,000 | Unlimited | 1 |
| Enterprise | 300 | 9,000 | 1,500,000 | 100,000 | Unlimited | 20 |
- Requests per hour is a backstop of 30 × the per-minute limit.
- Input tokens per minute is the default for every plan; a key can carry a lower limit set in the hub.
- Own MCP servers is how many MCP servers of your own you can connect; a dash means the plan does not include them.
- The table is generated from the same plan catalog as codai.ro/pricing and
codai.ro/pricing.json.
Each response carries the current state. Example for a Builder key:
x-ratelimit-limit-minute: 100
x-ratelimit-remaining-minute: 97
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. Read the limits from the headers rather than hard-coding them: a per-key limit set in the hub, or a plan change, takes effect without a client change.
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. 1 500 000/min unless the key sets a lower limit. | rate_limit_exceeded |
| Output tokens per minute | Completion tokens, per user across all keys: 20 000/min on Free, 100 000/min on paid plans. | 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 verified tasks per month:
| Plan | Verified tasks per month |
|---|---|
| Free | 5 |
| Starter | 33 |
| Builder | 83 |
| Team | 250 |
Past the allowance, each verified task is billed as overage at €1.20, when your billing allows overage. The €1.20 covers up to €1.20 of model usage for that task; unusually large tasks (above €1.20 of model usage) are billed for the excess at standard token rates, never below 1.2× upstream cost. Details in tasks and spend. When nothing pays for a new task:
- 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, set in EUR in the hub and summed across all keys. The cap is a guardrail, not an invoice: the gateway compares it one-to-one with the upstream cost it measures, which is in USD — hence the -usd header names. 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.
Images
Image generation and editing are billed from the token usage the model reports, at the Vertex rates of the model that served the call — output image tokens, input text and input/reference images. Approximate price of one 1K image with no reference images:
model | ≈ USD per 1K image |
|---|---|
codai-image (default, EU) | $0.074 |
codai-image-fast | $0.034 |
codai-image-pro | $0.134 |
Higher image_size values and reference images cost more. The charge is reported in x-codai-cost-micro-usd, recorded in the receipt, and counts toward the daily and monthly spend caps like any other cost. On prepaid plans the EUR wallet is debited for images too, at that USD cost converted to EUR with a 1.3 service multiplier (≈ €0.083 for a default image). Image calls never start a task, so they do not use your task allowance. A request whose images are all blocked by the safety filter (content_filtered) costs nothing.
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.
Usage-policy check
Agent steps that may break the model providers' usage policies are reviewed before they are sent upstream. A step judged a false positive goes through unchanged. Otherwise the gateway does not return an error: the response is a normal 200 assistant message that tells the agent what legitimate context is missing (whose system, what authorisation, what defensive purpose) or which part to drop. After 3 consecutive flagged steps within 24 hours the message tells the agent to stop pursuing that goal, and further suspicious steps are declined the same way until the window expires; unrelated work is unaffected. These responses carry x-codai-policy (ambiguous, violating, refused, refused-remembered, or cleared for a reviewed false positive) and x-codai-policy-strikes, and cost nothing. Rewording a request to get around the check counts as another strike.
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-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-5", "events": 130, "cost_micro_usd": 1801000 },
{ "upstream_model": "claude-haiku-5-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 100 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 or image model, media response_format other than b64_json or url. |
| 400 | content_filtered | invalid_request_error | The image safety filter blocked every generated image. Not charged. |
| 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. |
| 503 | assets_unavailable | invalid_request_error | response_format: "url" or /v1/assets on a gateway without asset storage. Use b64_json. |
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.
Last updated: