Blog
3 min read

429 Too Many Requests: What It Means and How to Fix It

A 429 Too Many Requests response means you've hit a rate limit: too many requests in too short a time. How to read the Retry-After and rate-limit headers, how to retry properly with backoff, the usual causes (loops, missing caching, shared IPs, AI API limits), and how to add rate limits to your own app.

429 Too Many Requests means you've hit a rate limit. The server is fine; it's just refusing to handle more requests from you until some time has passed.

You'll see it from APIs (OpenAI, Anthropic, GitHub, Stripe), from login pages that block repeated attempts, and from services like Cloudflare protecting a site.

Read the headers first

A well-behaved 429 tells you when to try again:

HTTP/1.1 429 Too Many Requests
Retry-After: 30
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1760000000
  • Retry-After — wait this many seconds (or until this date).
  • X-RateLimit-* (names vary by API) — your limit, what's left, and when it resets.

The fix: wait and retry properly

Retrying immediately in a loop makes things worse — you burn more of the limit and may get blocked for longer. Retry with exponential backoff: wait 1 second, then 2, then 4, plus a little randomness.

async function fetchWithRetry(url, options, attempts = 5) {
  for (let i = 0; i < attempts; i++) {
    const res = await fetch(url, options)
    if (res.status !== 429) return res
    await res.body?.cancel()
    if (i === attempts - 1) break
    const header = res.headers.get('retry-after')
    const retryAfter = header === null ? NaN
      : /^\d+$/.test(header.trim()) ? Number(header) * 1000
      : Date.parse(header) - Date.now()
    const wait = Number.isFinite(retryAfter)
      ? Math.max(0, retryAfter)
      : 2 ** i * 1000 + Math.random() * 500
    await new Promise(r => setTimeout(r, wait))
  }
  throw new Error('Still rate limited after retries')
}

This handles both seconds and an HTTP date in Retry-After. Use it for retryable rate limits; inspect the API's error body first when 429 can also mean a billing or quota failure. Retry writes only when the API guarantees they are safe to repeat, for example with an idempotency key. Most official SDKs (Anthropic, OpenAI, Stripe) already have retry handling. (Exponential Backoff and Jitter)

Why you're hitting the limit

  • A loop or re-render firing requests over and over — classic in React when a useEffect has the wrong dependencies. (React useEffect Explained)
  • No caching: fetching the same data on every page load. (Caching Explained)
  • Doing things one at a time that the API lets you batch.
  • Too much parallelism: firing 200 requests at once with Promise.all.
  • Shared IP: on a shared host or CI runner, other people's traffic counts against the same IP.
  • Your plan's limit is just low. New API accounts often start on low tiers that rise as you spend.

AI APIs: 429 isn't always the same 429

  • Anthropic: a 429 is a rate limit on your organisation's tier; a 529 means the API is overloaded for everyone. (Claude Code 529 Overloaded)
  • OpenAI: a 429 can also mean exhausted credit or a hard spending/usage limit, sometimes reported as insufficient_quota or a more specific billing code. Resolve the billing or limit issue before retrying. (You Exceeded Your Current Quota)

Adding rate limits to your own app

You should send 429s too. Rate-limit logins, signups, password resets and anything that calls a paid AI API, or bots will brute-force accounts and run up your bill. (Implement Rate Limiting, Stop Bots Running Up Your AI Bill)

The summary

  • 429 = rate limited. Respect Retry-After.
  • Retry with exponential backoff, not in a tight loop.
  • Fix the cause: loops, missing caching, too much parallelism, or a low tier.

EasySpawn gives your app a server-side backend where API calls can be cached, queued and rate-limited properly — with Claude Code to add the retry logic. See how it works or join the waitlist.

Related: What Is Rate Limiting? · HTTP Status Codes Explained · Message Queues Explained · Load Testing Your App

Keep reading