Skip to main content
Troubleshooting7 min readPublished September 20, 2026Updated September 22, 2026

Why is my AI API returning 429?

A 429 means you exceeded a limit: requests per minute, tokens per minute, concurrent requests, or a spend cap. With AI providers the token limit bites first, because one long request can consume a minute's allowance on its own.

The problem

Requests to a model provider fail with 429 Too Many Requests, often in bursts, sometimes while you are the only person using the app.

What it means

The provider accepted your identity and refused the volume. The response headers and body usually name which limit was hit and how long to wait.

Common causes

LimitWhat triggers it
Requests per minuteMany small calls in a burst
Tokens per minuteLong prompts or long outputs, even at low request counts
Concurrent requestsParallel calls for a list of items
Daily quota or spend capSustained use, or a free-tier ceiling
Accidental loopA render effect or retry firing repeatedly

How to diagnose it

  1. 1Read the response body — providers state which limit was exceeded.
  2. 2Check for a Retry-After header and honour it.
  3. 3Count your requests in the Network tab over ten seconds. A number you cannot explain means a loop.
  4. 4Estimate token use: prompt plus output, multiplied by calls per minute.
  5. 5Check the provider dashboard for quota and spend.

How to fix it

  • Retry with exponential backoff and a small random delay, not immediately.
  • Queue work rather than sending it in parallel.
  • Shorten prompts: trim pasted context to what the task needs.
  • Cache identical requests so repeat visitors cost nothing.
  • Move shared calls to the server so one result serves many users.
  • Raise the limit with the provider if the volume is genuine.
Backoff that respects the provider
for (let attempt = 0; attempt < 4; attempt++) {
  const res = await call();
  if (res.status !== 429) return res;
  const retryAfter = Number(res.headers.get("retry-after")) || 2 ** attempt;
  await new Promise((r) => setTimeout(r, retryAfter * 1000 + Math.random() * 300));
}

What AI may have done

  • Written a fetch inside a render effect with an unstable dependency, firing on every render.
  • Mapped over a list with parallel calls instead of a queue.
  • Retried immediately on failure, multiplying the load at the worst moment.
  • Sent the entire document as context when a section would do.

How to verify the fix

  1. 1Repeat the action and count requests — the number should match what you intend.
  2. 2Confirm retries are spaced, not stacked.
  3. 3Watch the provider dashboard during a realistic session.
  4. 4Simulate a burst and confirm the app degrades politely rather than failing hard.

How to prevent it

  • Log every model call with its size while developing.
  • Set a spend alert before you need one.
  • Keep prompts tight; context length is the main cost driver.
  • Show users a clear 'try again shortly' state instead of a silent failure.

Frequently asked questions

Is 429 the same as being banned?
No. It is temporary by definition. Repeated abuse can escalate, which is why honouring Retry-After matters.
Does a bigger plan fix it?
Only if the volume is real. A loop will exhaust any limit you buy.
Should I retry automatically?
Yes, a few times with backoff. Then tell the user rather than retrying forever.

Practice this in MessyDev

Reading it once helps. Doing it once sticks. These are the hands-on parts of MessyDev that cover the same ground.

Keep going