Design a Rate Limiter
2 min read
Design a Rate Limiter
A rate limiter caps how many requests a client can make in a window — protecting against abuse, runaway clients and cost overruns.
1. Requirements
- Limit per client (API key / user / IP), e.g. 100 req/min.
- Low latency (it's on every request), accurate, works across a distributed fleet.
- Return
429 Too Many Requests+Retry-Afterwhen exceeded.
2. Algorithms
| Algorithm | Idea | Trade-off |
|---|---|---|
| Token bucket | Bucket refills at a rate; each request takes a token | Allows bursts; the go-to default |
| Leaky bucket | Requests drain at a fixed rate | Smooths bursts, adds latency |
| Fixed window | Count per calendar minute | Simple; 2× burst at the boundary |
| Sliding window | Weighted blend of two windows | Good accuracy/memory balance |
3. Distributed design
The counter must be shared — otherwise each server allows the full limit.
Request ─▶ App ─▶ Redis (INCR key, check limit) ─▶ allow / 429
key = "rl:{user}:{window}"
count = INCR key
if count == 1: EXPIRE key 60
if count > limit: return 429
Use a Lua script so "check + increment + set TTL" is atomic, avoiding races.
4. Deep dives
- Where: at the API gateway / edge so bad traffic is rejected early.
- Trade-off: central Redis adds a hop and a dependency — shard it or cache buckets locally.
- Fairness: separate limits per tier (free vs paid).
Takeaway: token bucket + atomic Redis counter is the standard, interview-ready rate limiter.