A single misbehaving script — a runaway integration, an over-eager scraper, a retry loop gone wild — can send thousands of requests a second and bring a service to its knees. Rate limiting is the bouncer that stops that from happening, and HTTP 429 is the polite-but-firm "you've had enough, slow down."
If you build or consume APIs, you'll meet both. Here's how they work — from both sides of the door.
What rate limiting is
Rate limiting caps how many requests a client can make in a given window of time — say, 100 requests per minute. Go over, and further requests are rejected (usually with a 429) until the window resets.
It exists to protect a service from being overwhelmed, whether the cause is malicious or just a bug.
Why services do it
| Reason | What it prevents |
|---|---|
| Stability | One heavy client starving everyone else |
| Abuse | Scrapers, brute-force attacks, spam |
| Cost control | Runaway usage running up infrastructure bills |
| Fairness | Sharing capacity evenly across all users |
Rate limiting is a feature, not hostility. A service that limits you is protecting its reliability — and therefore your experience of it — from whoever's hammering it hardest.
How it works (the token bucket)
The most common mechanism is a token bucket: you get a bucket of tokens that refills at a steady rate. Each request spends a token. Requests are allowed while there are tokens; when the bucket's empty, you're limited until it refills. It's simple and it smoothly allows short bursts while capping the sustained rate.
Limits are usually applied per client — per API key, per user, or per IP — so one noisy neighbour doesn't affect everyone.
Meet HTTP 429
When you exceed a limit, a well-behaved API returns 429 Too Many Requests. Crucially, it often includes headers telling you how to behave:
Retry-After— how long to wait before trying again.X-RateLimit-Remaining/X-RateLimit-Reset— how many requests you have left and when the window resets.
These headers are gold: they let a good client back off exactly as much as needed, instead of guessing.
Handling 429 as a client
If you're calling an API and hitting 429s, don't just retry immediately — that makes it worse. Instead:
- Respect
Retry-After. If the server told you when to come back, do that. - Use exponential backoff with jitter. Wait, then wait longer each retry, with a little randomness so all your clients don't retry in sync.
- Watch the rate-limit headers. Slow down before you hit the wall, using the "remaining" count.
- Cache and batch. The best way to avoid limits is to make fewer requests in the first place.
Implementing it as a service
If you're the one applying limits:
| Do | Why |
|---|---|
| Limit per API key / user, not globally | One client's surge shouldn't hit everyone |
Return 429 with Retry-After | Tell clients exactly how to behave |
| Expose remaining/reset headers | Lets good clients self-regulate |
| Set sane, documented limits | Surprises break integrations |
| Consider burst allowance | Token buckets handle short spikes gracefully |
The bottom line
| In one line | |
|---|---|
| What | A cap on requests per time window. |
| Why | Stability, abuse prevention, cost, fairness. |
| 429 | "Too many requests" — back off, respect Retry-After. |
| As a client | Backoff with jitter; watch the headers; make fewer calls. |
Rate limiting keeps shared services healthy, and 429 is just the system asking you to play nice. Handle it gracefully — back off, respect the headers, and reduce your call volume — and you'll have far more reliable integrations.
If you build APIs, monitor them (and their limits) with the API monitoring guide, and brush up on HTTP status codes.