Skip to main content

Rate limits

Rate limits protect service availability and latency. Requests that exceed a limit return 429 Too Many Requests with a Retry-After header.

What's limited

Requests can be limited for two reasons:

ConditionResponse
Workspace request rate exceeded429 rate_limit_exceeded
Service temporarily at capacity429 service_busy

Your workspace has a sustained request-rate limit. If you need a committed rate or a higher limit, contact us.

Additional constraints:

  • Rate limits are workspace-wide. All API keys in a workspace share the same request-rate limit and credit balance.
  • Short bursts may still be limited. Design clients to handle 429 responses even when average traffic is below the stated sustained rate.
  • Token limits are model-specific. Prompt plus completion must fit the model's 4,096-token context window. Requests over that limit return 400 Bad Request.

When you hit a limit

Over-limit requests fail fast with 429, code rate_limit_exceeded, and a Retry-After header (in seconds):

HTTP/1.1 429 Too Many Requests
Content-Type: application/json
Retry-After: 1

{"error": {"message": "You have exceeded your workspace's request rate. Slow down and retry, or contact support for a higher sustained limit.", "type": "rate_limit_exceeded", "code": "rate_limit_exceeded"}}

A 429 service_busy response indicates temporary service capacity pressure. Handle it in the same way: honor Retry-After and retry with backoff. See Errors for details.

An out-of-credit workspace returns 402 insufficient_quota. Add credit before retrying. See Errors.

Backing off well

Wait at least as long as Retry-After, use exponential backoff with jitter for subsequent attempts, and cap the number of retries:

import random
import time

from openai import APIStatusError


def with_backoff(call, max_attempts=5):
for attempt in range(max_attempts):
try:
return call()
except APIStatusError as err:
if err.status_code not in (429, 502, 503) or attempt == max_attempts - 1:
raise
retry_after = float(err.response.headers.get("retry-after", 1))
# Full jitter: sleep somewhere in [retry_after, retry_after + 2^attempt)
time.sleep(retry_after + random.uniform(0, 2 ** attempt))

For applications with multiple workers, enforce a shared client-side concurrency limit. This is more predictable than independent retry loops in each worker.

Watching your usage

The Console's usage page shows completed requests and token volumes. It does not include throttled requests, so monitor 429 responses in your application logs and metrics.