# Rate limits

kenari limits how fast you can call the API in a few places. This page lists each limit, the error it returns, and how to retry. For the error formats and every other code, see [Errors](/en/docs/errors).

## What each limit returns

| Status | Code | Retry-After | What to do |
| --- | --- | --- | --- |
| 429 | `rate_limit_exceeded` | Yes | Wait for the `Retry-After` seconds, then retry. Space your requests out. |
| 429 | `free_quota_rpm` | Yes | Wait for the `Retry-After` seconds, then retry. |
| 429 | `free_quota_concurrent` | Yes, 5 seconds | Wait for one of your running requests to finish, then retry. |
| 429 | `free_quota_daily` | Yes, until 00:00 UTC | Do not retry today. Use a paid model, top up, or wait for the reset. |
| 429 | `plan_limit_reached` | Yes, 60 seconds, not when the window rolls | Do not retry in a loop. Turn on PAYG or the free fallback, or wait for the quota window to roll. |
| 429 | `upstream_error` | Yes | A provider is rate limiting. Wait for the `Retry-After` seconds, then retry. |
| 429 | `rate_limited` | Yes | Wait for the `Retry-After` seconds, then retry. |
| 429 | `pending_limit_reached` | Yes, 60 seconds, not when the limit clears | Do not retry in a loop. Revoke pending invites, then invite again. |
| 429 | `daily_limit_reached` | Yes, 60 seconds, not when the limit clears | Do not retry in a loop. Wait until the older invites are more than 24 hours old. |
| 503 | `all_providers_failed` | Yes, when providers are paused or busy | Wait for the `Retry-After` seconds, then retry. |

Every `429` carries a `Retry-After` header, a number of seconds. A limit that knows its exact wait sets it, and every other `429` says 60. For the invite limits and the subscription quota windows, 60 seconds is not when the limit clears, so do not retry those in a loop. When a response has no `Retry-After`, use exponential backoff.

The codes in this table are the OpenAI-format error codes, returned by Chat completions, Responses and the other endpoints. [Messages](/en/docs/messages) errors have no `code` field. They carry `error.type`, and several of the limits above arrive there as `rate_limit_error`. On Messages, decide by the HTTP status and the `Retry-After` header instead. See [Errors](/en/docs/errors).

## Account requests per minute

Each account has a per-minute request limit for paid models. It is shared by [Chat completions](/en/docs/chat), [Messages](/en/docs/messages), [Responses](/en/docs/responses) and the [Claude Managed Agents](/en/docs/claude-managed-agents) passthrough. Over the limit, the request fails with `429` and `rate_limit_exceeded`, and `Retry-After` is 60 seconds. The limit is set on your account, so it can differ from one account to another. Requests to `:free` models count against the free-model limits below instead, and the other endpoints do not count against this limit.

## Usage reads

The three [Usage](/en/docs/usage) endpoints share a limit of 60 requests per minute for each account. Over the limit, the request fails with `429` and `rate_limit_exceeded`, and `Retry-After` is 60 seconds. It is counted apart from the account limit above.

## Per-tool key creation

Each login key can create 20 [per-tool keys](/en/docs/account-keys) per minute. Over the limit, the request fails with `429` and `rate_limit_exceeded`, and `Retry-After` is 60 seconds. Listing and revoking keys are not limited.

## Workspace API

Every change made through the [Workspace API](/en/docs/workspace-api) is limited to 60 per minute for each key. Buying, changing and reactivating seats and moving Personal balance are also limited to 10 per minute for each key. Over either limit, the request fails with `429` and `rate_limited`, and a `Retry-After` header says how many seconds to wait. Reads and `POST /v1/workspace/seats/quote` do not count.

Inviting has its own limits for each workspace, the same as in the dashboard: 50 pending invites, and 20 invites created in any 24 hours. Over them, the request fails with `429` and `pending_limit_reached` or `daily_limit_reached`. `Retry-After` says 60 seconds, but these limits clear only when invites are revoked or accepted, or pass 24 hours, so do not retry in a loop.

## Free-model limits

Every account has a per-minute limit and a daily quota for `:free` models. The values depend on your account tier. [Free models](/en/docs/free-models) lists the tiers, and `GET /api/public/pricing` returns the current numbers.

- **Per minute.** Over the limit, the request fails with `429` and `free_quota_rpm`, and `Retry-After` says how many seconds remain until the limit window resets.
- **At the same time.** A free model can limit how many of your free requests run at once. Over the limit, the request fails with `429` and `free_quota_concurrent`, and `Retry-After` is 5 seconds. The limit clears when one of your running requests finishes, so wait for one, then retry. A model that has this limit shows it on its page in the dashboard.
- **Per day.** The daily quota counts successful requests and resets at 00:00 UTC. Over the quota, the request fails with `429` and `free_quota_daily`, and `Retry-After` is the number of seconds until the reset. Topping up or subscribing raises the quota.
- **Bursts.** Free requests that arrive close together are spaced out and served one after another, not rejected. A sustained flood is rejected with `429` and `rate_limit_exceeded`.
- **Token budget.** A free model can carry a total token budget. When it is used up, that model returns `429` and `rate_limit_exceeded` until the budget window rolls.

A successful free-model response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining` and `X-RateLimit-Reset` when the model has a per-minute limit. `X-RateLimit-Reset` is a Unix timestamp in seconds.

## Subscription quota windows

A subscription covers its models with quota windows. When a window is used up and neither PAYG nor the free fallback is on, requests to covered models fail with `429` and `plan_limit_reached`. `Retry-After` says 60 seconds, which is not when the window rolls. The `message` of this error is in Indonesian, so branch on `code` (on Messages, on the status and `error.type`). Turn on PAYG or the free fallback in your subscription settings, or wait for the window to roll. See [Subscriptions](/en/docs/subscriptions).

## Music concurrency

Each account can run two music generations at the same time. A third request fails with `429` and `rate_limit_exceeded`, and `Retry-After` is set to 60 seconds. Retry once one of your generations finishes. See [Music](/en/docs/music).

## Provider limits and outages

When a model's provider rate limits kenari, you get `429` and `upstream_error`, with a `Retry-After` header. When a provider times out or fails, you get `503` and `upstream_error`. When every provider for a model is temporarily paused or at capacity, you get `503` and `all_providers_failed`, with a `Retry-After` header. A route or a BYOK fallback can move the request to another provider before you see any of these. See [Routing and routes](/en/docs/routing) and [Providers](/en/docs/byok).

## Shared keys

A shared API key is paced as a whole, so the people using a share cannot use up the owner's account limit. Each client IP address is also limited to 30 requests per minute on one shared key, on `/v1` and on the MCP server alike. Over that limit, the response is `429` with the plain-text body `too many requests, try again shortly`, not JSON. See [Authentication and API keys](/en/docs/authentication).

## Retry safely

On Messages, apply the same rules by HTTP status, because there is no `code`. Retry only errors that can clear on their own: `429` with `rate_limit_exceeded`, `free_quota_rpm`, `free_quota_concurrent` or `upstream_error`, and `503`. Do not retry `free_quota_daily`, `plan_limit_reached` or any `402`, because they stay the same until you change something.

1. If the response has a `Retry-After` header, wait that many seconds.
2. Otherwise wait with exponential backoff and jitter, for example 1, 2, 4 and 8 seconds, each plus a random fraction of a second.
3. Stop after a few attempts and surface the error.

The official OpenAI and Anthropic SDKs already retry `429` and `5xx` responses a couple of times by default and honor `Retry-After`. Raise their retry count with `max_retries` if that is enough. This example shows the logic when you want control over it.

```python
import os
import random
import time

from openai import APIStatusError, OpenAI

client = OpenAI(
    base_url="https://kenari.id/v1",
    api_key=os.environ["KENARI_API_KEY"],
    max_retries=0,
)

RETRYABLE = {"rate_limit_exceeded", "free_quota_rpm", "free_quota_concurrent", "upstream_error", "all_providers_failed"}


def chat(messages, model="step-3-7-flash:free", attempts=5):
    for attempt in range(attempts):
        try:
            return client.chat.completions.create(model=model, messages=messages)
        except APIStatusError as error:
            retryable = error.status_code in (429, 503) and error.code in RETRYABLE
            if not retryable or attempt == attempts - 1:
                raise
            retry_after = error.response.headers.get("retry-after")
            delay = float(retry_after) if retry_after else min(30, 2**attempt)
            time.sleep(delay + random.random())


reply = chat([{"role": "user", "content": "Hello!"}])
print(reply.choices[0].message.content)
```

Do not retry a request that failed in the middle of a stream by resuming it. Send it again as a new request.

## See also

- [Errors](/en/docs/errors)
- [Free models](/en/docs/free-models)
- [Subscriptions](/en/docs/subscriptions)
- [Usage](/en/docs/usage)
- [Streaming](/en/docs/streaming)
