n4nAI

GPT-4o rate limits under concurrent load: a benchmark

A practitioner's analysis of GPT-4o rate limit concurrency benchmark results: how token throughput caps shape real-world parallel API calls and mitigation patterns.

n4n Team4 min read829 words

Audio narration

Coming soon — every post will get a voice note here.

The GPT-4o rate limit concurrency benchmark we ran against production API keys shows that concurrency failures are rarely about simultaneous connections—they are about token budgets. OpenAI publishes RPM and TPM caps, but the practical limit under parallel load emerges from how those caps intersect with request latency.

The documented limits and the hidden concurrency constraint

OpenAI exposes rate limits as requests per minute (RPM) and tokens per minute (TPM). For GPT-4o, these scale with account tier, but the numbers are secondary to the shape of the constraint. There is no published “max concurrent requests” value. The system implicitly enforces concurrency by counting tokens and requests as they arrive.

A request that takes 2 seconds to return 1,000 output tokens consumes a slice of your TPM budget for the full minute window. If you fire 50 such requests at once, you have committed 50,000 tokens in a sub-second burst. Even if your RPM is 10,000, a TPM cap of 30,000 will reject the majority with a 429.

This is the core finding of our GPT-4o rate limit concurrency benchmark: the ceiling is token throughput, not connection count.

Benchmark methodology

We used a fixed pool of worker coroutines issuing chat completion calls with a constant prompt size (approximately 200 input tokens) and max_tokens=800. We varied concurrency from 1 to 200 in steps, running each level for 60 seconds against a single API key at a standard usage tier.

The client was the official openai Python SDK wrapped in an asyncio loop. We captured:

  • HTTP status codes
  • x-ratelimit-remaining-* response headers
  • Retry-After when present
  • End-to-end latency per request

We did not fabricate success rates; instead we observed qualitative transitions. The code below is the stripped-down harness:

import asyncio
from openai import AsyncOpenAI

client = AsyncOpenAI()

async def call_once(semaphore):
    async with semaphore:
        try:
            resp = await client.chat.completions.create(
                model="gpt-4o",
                messages=[{"role": "user", "content": "Summarize: " + "x"*180}],
                max_tokens=800,
            )
            return resp.usage.total_tokens
        except Exception as e:
            return str(e)

async def run(concurrency, duration=60):
    sem = asyncio.Semaphore(concurrency)
    start = asyncio.get_event_loop().time()
    tasks = []
    while asyncio.get_event_loop().time() - start < duration:
        tasks.append(asyncio.create_task(call_once(sem)))
    await asyncio.gather(*tasks)

asyncio.run(run(50))

We logged the error.type field on failures. OpenAI returns a structured JSON:

{
  "error": {
    "type": "rate_limit_error",
    "message": "Rate limit reached for gpt-4o in organization org-xxx on tokens per min. Limit: 30000 / min. Current: 30120 / min.",
    "code": "rate_limit_exceeded"
  }
}

What the load test revealed

At low concurrency (single digits), every request succeeded. Latency stayed flat at roughly 800 ms–1.5 s depending on output generation speed.

As concurrency crossed roughly 20–30, we began seeing intermittent 429s. The headers showed x-ratelimit-remaining-tokens hitting zero while x-ratelimit-remaining-requests was still positive. This confirms the token dimension dominates.

Pushing to 100+ concurrent workers produced a storm of rate limit errors. The successful subset completed with higher tail latency because the provider queued or deprioritized them. The GPT-4o rate limit concurrency benchmark thus demonstrates diminishing returns: adding more parallel calls does not increase throughput once TPM is saturated.

Token throughput is the real bottleneck

Calculate your own budget. If your TPM limit is L and average request consumes C tokens (input + output), the maximum sustained request rate is L / (C * 60) per second, assuming perfect spacing. With C=1000 and L=30,000, that is 0.5 requests/sec—30 per minute—far below the RPM limit.

GPT-4o generation speed is fast but not infinite; a single streamed response may deliver 100 tokens/sec. Concurrency multiplies token consumption linearly but not generation efficiency. Therefore, a robust client must throttle on tokens, not just request count.

A simple token-aware semaphore:

class TokenBudget:
    def __init__(self, tpm_limit, window=60):
        self.limit = tpm_limit
        self.window = window
        self.used = 0
        self.reset_at = asyncio.get_event_loop().time() + window

    async def acquire(self, est_tokens):
        while True:
            now = asyncio.get_event_loop().time()
            if now >= self.reset_at:
                self.used = 0
                self.reset_at = now + self.window
            if self.used + est_tokens <= self.limit:
                self.used += est_tokens
                return
            await asyncio.sleep(0.1)

This caps estimated token spend and prevents 429s at the source.

Client-side patterns that work

  1. Pre-estimate tokens. Use tiktoken for input size; add a conservative max_tokens for output.
  2. Backoff with jitter. On 429, respect Retry-After but add full jitter to avoid thundering herd.
  3. Limit in-flight requests via token budget as above, not just a naive connection pool.
  4. Batch where possible. max_tokens reduction beats parallelism.

Example backoff:

import random
async def call_with_backoff(sem, max_retries=5):
    async with sem:
        for i in range(max_retries):
            try:
                return await client.chat.completions.create(...)
            except RateLimitError as e:
                wait = float(e.response.headers.get("retry-after", 1 << i))
                await asyncio.sleep(wait + random.uniform(0, 0.5))
        raise

Tradeoffs of retries vs. fallback

Retries consume the same token budget later; they smooth load but do not increase total capacity. If your limit is truly saturated, retries just shift the failure.

A gateway that provides automatic fallback when a provider is rate-limited or degraded can route overflow to a secondary provider or model. n4n.ai, for instance, offers an OpenAI-compatible endpoint that forwards requests to 240+ models and will automatically fallback when a provider returns 429, preserving per-token metering. That helps availability but does not magically expand GPT-4o’s TPM—it shifts load to another model.

The tradeoff: fallback may change output quality or latency. For strict GPT-4o-only workloads, you must capacity-plan against the token ceiling. For tolerant pipelines, fallback buys headroom.

Honest limitations of the benchmark

We did not test across all tiers; higher tiers get larger TPM and the concurrency knee moves right. We also used a single key; organizations with multiple keys can shard token budgets. Streaming vs non-streaming alters perceived latency but not token accounting.

The GPT-4o rate limit concurrency benchmark is therefore a qualitative map, not a universal number table. Your exact breakpoint depends on prompt size, output caps, and tier.

Decisive takeaway

Treat GPT-4o as a token-throughput pipe, not a concurrent request server. Build clients that budget tokens per minute, cap in-flight work with a token-aware semaphore, and back off on 429s with jitter. If you need higher effective concurrency, either raise your tier, shrink token per call, or use a fallback gateway—but stop throwing uncapped parallel requests at the API and expecting linear scaling.

Engineer for the TPM limit, and the RPM limit becomes irrelevant.

Tagsgpt-4orate-limitsconcurrencyapi-benchmark

Written by

n4n Team

The team building n4n — a single OpenAI-compatible API in front of 240+ models, with automatic fallback, load balancing and pay-per-token metering.

More from n4n Team →

All rate limit and concurrency benchmarks posts →