Cap how fast a client can make requests so one user can't overwhelm a service — token bucket, leaky bucket, and sliding-window methods.
Rate limiting caps how many requests a client may make in a window of time, so one user — buggy, abusive, or just popular — can't overwhelm a shared service. When a client exceeds its limit the server throttles it, usually replying HTTP 429 Too Many Requests. The art is choosing an algorithm that allows normal bursts while still enforcing a steady long-run rate.
The token bucket holds up to N tokens (its capacity) and refills at a steady rate of R tokens per second. Each request must spend one token; if the bucket is empty, the request is rejected. A full bucket lets a short burst of up to N requests through instantly, after which the client is smoothed down to the refill rate R.
tokens = min(capacity, tokens + elapsed × rate) from the timestamp of the last refill — so the whole limiter is just two numbers per client (token count + last-seen time) and O(1) work per request.function allow(bucket, now, capacity, rate) {
const elapsed = now - bucket.last;
bucket.tokens = Math.min(capacity, bucket.tokens + elapsed * rate);
bucket.last = now;
if (bucket.tokens >= 1) {
bucket.tokens -= 1;
return true; // allowed
}
return false; // reject -> HTTP 429
}The leaky bucket is the mirror image: requests pour into a queue (the bucket) and leak out at a fixed rate, like water through a hole. It enforces a perfectly smooth *output* rate regardless of how bursty the input is — great for protecting a downstream that needs constant pacing. The trade-off: it does not allow bursts the way a token bucket does, and if the queue overflows, requests are dropped.
Window methods count requests over a rolling time span. The sliding-window log stores a timestamp for every request and, on each new one, counts how many fall inside the last 60 seconds — exact, but the memory grows with request volume. The sliding-window counter approximates it cheaply: it keeps per-bucket counts (e.g. per minute) and blends the current and previous bucket by how far into the window you are — near-exact, with O(1) memory. This also avoids the fixed-window flaw where a client can fire a full quota at 0:59 and another at 1:00, sending double the limit across the boundary.