Rate Limits
Each plan allows a number of requests per hour. How they are counted, which headers show the count, and how to handle a 429.
Each account can make a set number of requests per clock hour (UTC). All its API keys share the count, and it resets on the hour.
| Plan | Requests per hour |
|---|---|
| Starter | 100 |
| Professional | 500 |
| Enterprise | 2,000 |
Every request with a valid key counts, including requests that end in an error. Requests from the local MCP server count too. The hosted MCP server and webhook deliveries don't.
Successful responses carry three headers.
| Header | Type | Meaning |
|---|---|---|
X-RateLimit-Limit | integer | Requests allowed per hour on your plan |
X-RateLimit-Remaining | integer | Requests left this hour |
X-RateLimit-Reset | Unix timestamp | When the count resets: the next full hour, in seconds |
Over the limit you get 429 rate_limited with the same headers and Retry-After, the seconds until the next hour.
{ "error": { "code": "rate_limited", "message": "Rate limit exceeded (100 requests per hour for starter plan)" } }Other 429 responses
| Response | Cause | What to do |
|---|---|---|
429 rate_limited, message OpenAI rate limit exceeded. Please try again later. | The AI model is busy. Only on the AI generation endpoints, without headers. | Wait a few seconds and retry |
429 ai_limit_exceeded | Your AI credits are used up for the month or the day, or the spending cap is reached. See Tier Access. | Don't retry. Check GET /user/profile. |
429 with { "error": "Rate limit exceeded", "message": "…" } | A request without an Authorization header. These are limited to 100 per 15 minutes per IP address. | Send your key |
Handling a 429
Read error.code before you retry. Only rate_limited clears by waiting.
const res = await fetch(url, { headers });
if (res.status === 429) {
const { error } = await res.json();
if (error?.code === "ai_limit_exceeded") throw new Error(error.message);
const wait = Number(res.headers.get("Retry-After") ?? 5);
await new Promise((r) => setTimeout(r, wait * 1000));
// send the same request again
}To use fewer requests, let webhooks tell you when a call is ready instead of polling GET /conversations.