# OpenAI-compatible API rate limits

> Per-key and per-workspace request budgets for /v1/responses and /v1/chat/completions, the pacing headers, and how a 429 is signalled.

The `/v1` API enforces two independent, fixed-window rate limits:

| Scope | Default | Env var |
|---|---|---|
| Per API key | 60 requests/min | `OCTO_API_RATE_LIMIT_PER_MIN` |
| Per workspace | 600 requests/min | `OCTO_API_WS_RATE_LIMIT_PER_MIN` |

Exceeding **either** limit returns:

| HTTP | `type` / `code` | When | SDK exception |
|---|---|---|---|
| `429` | `rate_limit_error` | Per-key or per-workspace rate limit exceeded | `RateLimitError` |

## Pacing headers

**Every** response — success and `429` alike — carries the current budget so an
agent can pace itself proactively instead of only reacting to a `429`:

| Header | Meaning |
|---|---|
| `X-RateLimit-Limit` | Requests allowed in the window |
| `X-RateLimit-Remaining` | Requests left in the current window (`0` once exceeded) |
| `X-RateLimit-Reset` | Unix time (seconds) when the window resets |
| `Retry-After` | Seconds to wait before retrying (on `429` only) |

The headers report whichever of the two limits (per-key or per-workspace) you
are closest to hitting. Slow down as `X-RateLimit-Remaining` approaches `0`.

## Handling 429s

```python
import time
from openai import RateLimitError

try:
    resp = client.responses.create(model="claude-sonnet-4-6", input="...")
except RateLimitError as e:
    retry_after = int(e.response.headers.get("retry-after", "1"))
    time.sleep(retry_after)
    # retry
```

If you're consistently hitting the per-key limit, spread load across multiple
keys minted for different automated clients (see [Get an API
key](/getting-started/api-keys/)) rather than retrying in a tight loop — the
per-workspace limit still applies across all of a workspace's keys combined.

See [Errors](/openai-api/errors/) for the full error table.