Building block
Rate limiting
Caps the number of requests from one client over a period of time. It protects the system from overload, abuse and costly bugs in other people's code.
Why limit
One client with a bug in a loop can send thousands of requests a second and take the API down for everyone. Password guessing, scraping the course catalog and signup spam are also stopped by limits.
Limits are set per key: IP, user id, API key, sometimes a specific endpoint. For example, the GitHub REST API allows 5,000 requests per hour for an authenticated user and 60 for an anonymous one. For login, 5 attempts per minute per IP and per account is sensible.
Algorithms
Fixed window counts requests in a window (say, a minute) with INCR and EXPIRE in Redis. Simple, but at the window boundary a client can make double the requests within a few seconds.
Sliding window smooths that boundary by weighting the previous window. Token bucket keeps a «bucket» of N tokens refilled at a constant rate: it allows short bursts up to the bucket size but caps the average rate. Stripe uses a token bucket in its write-up on rate limiters.
Leaky bucket lets requests through at a constant rate and queues or drops the excess. Good for protecting a slow external API with a hard limit.
Telling the client and distributed limits
When the limit is exceeded, return 429 Too Many Requests with a Retry-After header, and in normal responses headers like RateLimit-Remaining. Well behaved clients use them to back off instead of blindly retrying.
If the API runs on 10 copies, an in-memory counter in each gives a limit 10 times higher. So counters live in a shared Redis with atomic operations (a Lua script or INCR), and the check runs at the gateway so a request is rejected before any business logic.
Pros
- Protects from overload and accidental DoS by your own clients.
- Makes password guessing and scraping harder.
- Shares resources fairly between clients and plans.
- Keeps spending on paid external APIs under control.
Cons
- Poorly chosen limits block legitimate users (for example, an office behind one NAT).
- Distributed counters add a Redis dependency and a little latency.
- You need to decide what happens when the counter store is down.
When to choose it
- Any public API or login and signup form.
- An API with plans and quotas for partners.
- In front of expensive operations or paid external services.
Where it is used
- Stripe: four kinds of limiters, including a token bucket in Redis and load shedding (2017).
- GitHub REST API: 5,000 requests per hour for authenticated users.
Tools
- RedisRedis is a fast in-memory data store used for caching, queues, counters, sessions and leaderboards, with rich data structures.
- ValkeyValkey is an open source fork of Redis under the Linux Foundation, compatible with the Redis protocol and licensed under BSD.
- nginxnginx is a fast web server and reverse proxy that also works as a load balancer and HTTP cache.
- TraefikTraefik is a cloud-native reverse proxy and edge router that discovers services in Docker and Kubernetes and configures routes itself.
- CloudflareA global network with CDN, DNS, DDoS protection, plus Workers, R2 and D1 for running apps at the edge.
Related topics
Sources
Logos belong to their owners and are used for identification only. Icons: Simple Icons (CC0), Devicon (MIT).