The Token Bucket Pattern: Allowing Bursts While Enforcing Long-Term Rate Limits

2026-07-05

The leaky bucket smooths traffic into a steady drip. But sometimes you want to allow bursts — a user who's been idle for an hour should be able to fire off 10 requests immediately, not be forced to wait one-per-second. That's the token bucket's job.

How it works: A bucket holds up to N tokens. Tokens refill at a steady rate (say, 10/sec). Each request consumes one token. If the bucket is empty, the request is rejected (or queued). If it's full, extra refill tokens are discarded.

Two knobs control behavior:

Real-world example: AWS API Gateway uses token buckets for throttling. A default account gets a burst of 5000 and a steady rate of 10000 req/sec. If you've been quiet, you can hammer the API with 5000 requests in one go. Then you're capped at 10000/sec sustained. Stripe's API works similarly — you get room to retry a batch after a network hiccup without being punished for the burst.

The efficient implementation trick: Don't run a background thread that adds a token every 100ms. Instead, store last_refill_timestamp and tokens. On each request, compute lazily:

tokens = min(capacity, tokens + (now - last_refill) × rate)

Then decrement and update the timestamp. This is O(1) per request, no timers, and works perfectly in Redis with a Lua script for atomicity.

Rule of thumb for sizing: Set capacity to refill_rate × acceptable_burst_seconds. If you allow 100 req/sec sustained and want to tolerate 3-second bursts of double traffic, capacity ≈ 300. Too small and legitimate retry storms get rejected; too large and a bad actor can overwhelm your downstream with one shot.

Token bucket vs leaky bucket: Both enforce the same average rate. The difference is shape. Leaky bucket flattens output — good when the downstream system genuinely cannot handle spikes (a legacy database). Token bucket permits spikes — good when downstream can absorb bursts and you care more about fairness over time than instantaneous smoothness (most user-facing APIs).

One trap: if you share one bucket across all users, a noisy neighbor starves everyone. Bucket per API key or per IP — not per service.

See it in action: Check out Rate Limiter System Design: Token Bucket, Leaky Bucket, Scaling by ByteByteGo to see this theory applied.
Key Takeaway: Token buckets enforce a long-term rate while permitting short bursts — tune capacity for burst tolerance and refill rate for sustained throughput.

All newsletters