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
| Limit | What triggers it |
|---|---|
| Requests per minute | Many small calls in a burst |
| Tokens per minute | Long prompts or long outputs, even at low request counts |
| Concurrent requests | Parallel calls for a list of items |
| Daily quota or spend cap | Sustained use, or a free-tier ceiling |
| Accidental loop | A render effect or retry firing repeatedly |
How to diagnose it
- 1Read the response body — providers state which limit was exceeded.
- 2Check for a Retry-After header and honour it.
- 3Count your requests in the Network tab over ten seconds. A number you cannot explain means a loop.
- 4Estimate token use: prompt plus output, multiplied by calls per minute.
- 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.
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
- 1Repeat the action and count requests — the number should match what you intend.
- 2Confirm retries are spaced, not stacked.
- 3Watch the provider dashboard during a realistic session.
- 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
- 401 vs 403 vs 404 vs 429 vs 500Five status codes that cover most failures in an AI-built app, what each one is really saying, and who has to fix it.
- How APIs work in AI-built applicationsRequests, responses, headers and status codes — explained through the calls an AI-generated app actually makes, and where those calls tend to go wrong.
- How AI agents use toolsThe loop that turns a language model into something that takes actions: tool descriptions, arguments, results, retries, and where supervision belongs.