Reading — step 1 of 5
Read
Why Rate Limit?
A rate limiter answers one question, on every request, in microseconds: has this client already had its fair share? If yes, the request is refused before it costs you anything — no database query, no worker thread, no invoice. Every serious API has one, because without it four things reliably happen:
- The buggy client. Someone ships a retry loop with no delay. A single laptop retrying every 10 ms is 100 requests per second of pure waste — forever, until you block it.
- The attacker. Credential stuffing against
/login, scraping against/search, or a plain flood. If handling a request costs you more than sending it costs them, you lose by arithmetic. - The noisy neighbor. One tenant's batch job saturates the database and every other tenant's latency spikes. Fairness does not happen on its own; it is enforced.
- The surprise bill. At $0.001 per serverless invocation, a runaway script is a heart-attack invoice by morning.
The anatomy of a limit
Every limit is three design decisions:
- Identity — who is "the client"? Per-IP (1,000/hour), per-user (100/min), per-API-key (10,000/day, tiered free/pro/enterprise), per-endpoint (
/login= 5/min as brute-force defense), or global (50,000/s protecting total capacity). Production systems stack several of these — that is this course's Tiered Limits lesson. - Budget — how much, per how long? 100 per minute — but may the client spend all 100 in one second? Whether bursts are tolerated is exactly where the algorithms in the next four lessons differ.
- Verdict — what happens on excess? Drop (answer 429 and do no work — the default for APIs), queue (hold the request and serve it late — used in traffic shaping, rarely for interactive HTTP), or throttle (serve, but slowly).
The HTTP contract
HTTP/1.1 429 Too Many Requests
Retry-After: 30
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1716383472
Status 429 Too Many Requests is defined in RFC 6585. Retry-After tells a well-behaved client exactly how long to back off. The X-RateLimit-* trio is a de facto convention — GitHub and Stripe both ship variants, and an IETF draft is standardizing RateLimit headers — that lets clients pace themselves before hitting the wall: watch Remaining approach zero and slow down.
Two traps before you write any code
Retry storms. Clients that treat 429 as a transient error and retry instantly convert a small overload into a synchronized flood — the limiter causes the very traffic it is refusing. The cure lives on both sides: the server always sends Retry-After; the client honors it, with exponential backoff plus jitter so a thousand blocked clients don't all come back in the same millisecond.
Identity is slippery. Per-IP looks easy, but a university NAT or a mobile carrier puts thousands of people behind one address, and X-Forwarded-For is attacker-controlled unless your own proxy overwrites it. Prefer authenticated identity (API key, user id) wherever it exists; keep per-IP limits for the unauthenticated edge.
Your exercise
Draft the limit table for a small API with /login, /search, and /export: choose identity, budget, and verdict for each. Then attack your own numbers the way a reviewer would. At 5/min per IP, /login yields at most 5 × 60 × 24 = 7,200 password guesses per day from one address — down from millions. If /export allows 1,000/min per user, one user can pull your entire dataset every hour; that number fails review. Every limit you keep should survive this back-of-envelope test — the next seven lessons are about enforcing numbers, not choosing them.
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…