Rate limits and usage tiers

OpenAI API · Advanced · 10 min

At scale, limits shape your design: queues, batching and caching all exist partly because of them.

OpenAI limits how much each organisation and project can send. Limits are measured in several ways, and whichever fills first stops you:

  • RPM / RPD: requests per minute or per day.
  • TPM / TPD: tokens per minute or per day.
  • Others for images and audio.

Limits differ per model, and some model families share one. They rise with your usage tier, which goes up automatically as your total paid usage grows. The limits page in the dashboard shows current values.

Every response carries headers that tell you where you stand: x-ratelimit-remaining-requests, x-ratelimit-remaining-tokens and matching -reset- headers. Read them with client.responses.with_raw_response.create(...) if you want to slow down before you hit the wall.

When you do get a 429 or a 503:

  • Respect Retry-After when present, as a minimum wait.
  • Otherwise use exponential backoff with jitter, a small random extra delay, so many clients don't retry at the same instant.
  • Cap both the number of attempts and the total time.
  • Failed requests still count against your per-minute limit, so hammering makes it worse.

The SDK already does basic retries. For steady high volume, add a queue that limits concurrency, move non-urgent work to Batch or flex, and cache prompt prefixes. When growing traffic sharply, ramp up gradually.

Example

import random, time
import openai
from openai import OpenAI

client = OpenAI(max_retries=0)          # we handle retries ourselves here

def create_with_backoff(max_attempts=5, **kwargs):
    for attempt in range(max_attempts):
        try:
            raw = client.responses.with_raw_response.create(**kwargs)
            print("tokens left:", raw.headers.get("x-ratelimit-remaining-tokens"))
            return raw.parse()
        except (openai.RateLimitError, openai.InternalServerError) as e:
            wait = float(e.response.headers.get("retry-after") or 2 ** attempt)
            time.sleep(wait + random.uniform(0, 1))    # jitter
    raise RuntimeError("gave up after retries")

r = create_with_backoff(model="gpt-6-luna", input="ping")

Remember

  • Limits: requests and tokens per minute/day, per model, by tier
  • Read x-ratelimit-* headers; honour Retry-After
  • Backoff with jitter and a cap; shift bulk work to Batch or flex

Check yourself

  1. Why add random jitter to retry delays?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: developers.openai.com/api/docs/guides/rate-limits

Builds on

Unlocks

Nothing builds on this yet.