Module 8 · Lesson 1 of 4
Why limit at all?
Abuse, cost, fairness, and protecting yourself from your own clients.
At 10:00 the comedy special goes on sale. At 10:00:02 one scalper's script is requesting the booking page 20,000 times a second from a few hundred IP addresses, and requesting a login OTP for a fresh phone number on every attempt. Real fans see spinners. Gatepass's SMS bill for the morning is larger than the ticket revenue. Nothing is broken; the system is simply doing everything it's asked.
A server says yes to everyone
Without limits, a server's capacity goes to whoever asks fastest. That's fine when everyone asks at human speed. It stops being fine the moment one client asks at machine speed, because that client gets most of the capacity, and the cost of serving it lands on you and on everyone else.
A rate limiter sits in front of an endpoint and answers one question per request: has this client already used its share? If so, it rejects the request cheaply, before it touches the database, the payment provider or the SMS gateway.
Four reasons to limit
1. Abuse. Credential stuffing tries thousands of stolen passwords against the login endpoint. Scrapers copy your whole event catalogue. Scalper bots grab seats faster than any person can tap. A limit doesn't stop a determined attacker, but it raises their cost from one machine to thousands, which removes most of them.
2. Cost. Some endpoints spend real money per call. Every OTP is an SMS that costs Gatepass about ₹0.20. A bot requesting 20,000 OTPs a second costs ₹4,000 a second. This kind of attack, often called SMS pumping, is common precisely because the victim pays. Limits on expensive endpoints are a budget control as much as a security one.
3. Fairness. During an on-sale, ten thousand fans share one booking system. If one client can send a hundred times more requests than a person, they get a hundred times more chances at the best seats. A per-user limit keeps capacity spread across people instead of across scripts.
4. Protection from yourself. The most frequent source of overload isn't an attacker; it's your own code. A mobile release with a bug that refreshes the seat map in a tight loop. A partner's integration that retries without backoff. An internal batch job that calls the API flat out. A limit turns those bugs from an outage into an error message for the client that caused them.
A rate limit decides who gets your capacity when there isn't enough for everyone who's asking.
What a limit looks like
A limit is a number of requests, per period, per key: “5 OTP requests per phone number per hour”, “10 booking attempts per user per minute”, “100 requests per second per API key”. Gatepass's starting set:
| Endpoint | Limit | Why |
|---|---|---|
| POST /otp | 3 per phone per 10 min, 20 per IP per hour | Each one costs an SMS |
| POST /login | 5 failures per account per 15 min | Password guessing |
| POST /bookings | 10 per user per minute | Fairness during on-sales |
| GET /events/:id/seats | 5 per second per user | Runaway clients |
| Partner API | 100 per second per key | Contracted capacity |
Notice that the cheap read endpoints get generous limits, and the expensive or sensitive ones get tight limits. Limits are per endpoint because the cost per request is per endpoint.
Saying no, politely
When a client is over its limit, reply with 429 Too Many Requests, and tell it when to come back:
HTTP/1.1 429 Too Many Requests
Retry-After: 20
RateLimit-Limit: 10
RateLimit-Remaining: 0
RateLimit-Reset: 20
Retry-After lets well-behaved clients wait the right amount instead of hammering. The RateLimit-* headers let them slow down before they hit the wall. For a person in the app, translate it into words: “Too many attempts. Try again in 20 seconds.” Don't return a 500 or silently drop the request; a clear 429 is how good clients learn to behave.
Rejecting must be cheap. A limiter that runs a database query to decide whether to reject has just moved the overload one step earlier. Limiters keep their counters in memory or in Redis, and run as early as possible: at the load balancer or API gateway, or as the first middleware in the app.
What limits don't do
A rate limiter is one layer, not a defense on its own:
- It doesn't stop distributed attacks. A botnet with 100,000 IP addresses sends one request each. Per-IP limits never trigger. You also need bot detection, CAPTCHAs on sensitive flows, and network-level DDoS protection from your CDN.
- It doesn't add capacity. If 50,000 real fans each send one booking request a second, every limit is satisfied and the system is still overloaded. That's a job for queues (a virtual waiting room) and load shedding, which rejects work based on the server's own health rather than the client's behaviour.
- It can hurt real users. Set the login limit too low and a family sharing a phone gets locked out. Limits need monitoring: how many
429s, for whom, and on which endpoint.
Check yourself
Which Gatepass endpoint most needs a tight limit, even though it gets the least traffic?
Key takeaways
- Without limits, capacity goes to whoever asks fastest, and their costs land on you and everyone else.
- Limit for four reasons: abuse, cost, fairness, and protection from your own buggy clients.
- A limit is requests per period per key, set per endpoint according to what each request costs.
- Reject with
429andRetry-After, and make the rejection cheap: in memory or Redis, as early as possible. - Rate limits complement, not replace, bot detection, DDoS protection, waiting rooms and load shedding.
Finished reading?
Mark it complete to tick it off in the course.