Rate Limiting
Rate limiting caps how many requests a source can make in a time window — a first line of defense against DDoS, scraping and brute-force attacks.
Smart Rate Limiting
Combine rate limiting with intelligent bot detection
What is Rate Limiting?
Rate limiting caps how many requests a client may make in a period. It is the simplest effective control against abuse at volume, and it is in front of almost every API worth attacking.
Its usefulness turns entirely on one design choice: what you count per. Get that wrong and you either fail to constrain the attacker or you break legitimate traffic.
The algorithms, and when each is right
| Algorithm | Behaviour | Good for |
|---|---|---|
| Fixed window | N requests per calendar minute | Simplicity; suffers a burst at each boundary — 2N across two seconds |
| Sliding window | N requests across the trailing period | Smooth enforcement; more state to keep |
| Token bucket | Tokens refill steadily, each request spends one | Allowing short bursts within a sustained average. The usual default |
| Leaky bucket | Requests drain at a fixed rate | Smoothing traffic into a downstream system that cannot burst |
Token bucket is the right starting point for most APIs, because real clients are bursty and a limit that forbids bursts rejects normal use.
What to key on
This is where rate limiting succeeds or fails, and the honest ordering is:
- IP address — nearly free, and the weakest. Residential proxy networks give an attacker millions of addresses, so a per-IP limit becomes one request each. Worse, it penalises shared addresses: a university, a corporate VPN, or a mobile carrier's NAT puts thousands of real users behind one address.
- Account or API key — much better, and only available after authentication, which is not where the attacks are.
- Session or device signal — harder to rotate than an address, and needs collecting.
- The business action — the most useful. Five password resets per account per hour is a limit that means something, however the requests arrive.
Prefer limits expressed in terms of the action being abused over limits expressed in terms of the transport.
Distributed attacks and release-day crowds
A rate limit constrains volume per identifier. Against an attacker with a large pool of identifiers it degrades into a mild inconvenience, and distributing the attack is cheap: residential proxy access is sold by the gigabyte.
It also cannot distinguish a burst of real customers from a burst of scripts, which is the case that hurts most — a product release, a ticket on-sale, a flash sale. A limit tight enough to stop the scalpers is tight enough to stop the fans, and that trade is not resolvable by tuning the number.
What resolves it is deciding who rather than how many: establishing whether a person is behind each session, so the limit can be generous for customers and strict for everything else. That is the point of human verification, and proof of work is the variant that charges per attempt rather than counting them.
Practical settings
- Return
429withRetry-After, and publish the limits. Clients that know the rules mostly follow them. - Send
X-RateLimit-Remainingso well-behaved integrations can back off before they are rejected. - Rate limit the expensive endpoints, not everything — authentication, search, export, anything that writes.
- Log what you reject. A rate limiter with no visibility into what it dropped is a limiter you cannot tune.