Post

Rate Limiting Algorithms Compared

Introduction

Rate limiting is a core control for API reliability and abuse prevention. The algorithm you choose determines fairness, burst tolerance, and operational complexity. This guide compares the most common algorithms and when to use them.

Fixed Window Counter

A fixed window counter increments requests inside a fixed interval.

  • Simple and memory efficient.
  • Allows bursty traffic at window boundaries.
  • Best for coarse limits where precision is not critical.

Sliding Window Log

The sliding window log stores timestamps for every request.

  • Precise enforcement without boundary spikes.
  • High memory usage under heavy load.
  • Best for smaller scale or low request volumes.

Sliding Window Counter

A sliding window counter approximates the log by using two buckets.

  • Lower memory than full logs.
  • Smooths boundary spikes with weighted buckets.
  • Suitable for distributed systems with shared counters.

Token Bucket

Token bucket allows bursts while enforcing a steady refill rate.

  • Great for APIs that allow short bursts.
  • Enforces average rate over time.
  • Commonly used for public APIs and gateway limits.

Leaky Bucket

Leaky bucket enforces a strict outflow rate.

  • Prevents bursts entirely.
  • Useful when downstream systems cannot handle spikes.
  • Often implemented as a queue with fixed drain rate.

Trade-off Matrix

AlgorithmBurst SupportPrecisionMemory CostTypical Use
Fixed WindowHigh at boundariesLowLowSimple quotas
Sliding LogLowHighHighSmall systems
Sliding CounterMediumMediumMediumDistributed limits
Token BucketHighMediumLowPublic APIs
Leaky BucketLowMediumLowBackpressure control

JavaScript Example: Token Bucket

The following JavaScript example implements a token bucket for a single key.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class TokenBucket {
  constructor(ratePerSecond, capacity) {
    this.ratePerSecond = ratePerSecond;
    this.capacity = capacity;
    this.tokens = capacity;
    this.lastRefill = Date.now();
  }

  allowRequest() {
    const now = Date.now();
    const elapsed = (now - this.lastRefill) / 1000;
    const refill = elapsed * this.ratePerSecond;

    this.tokens = Math.min(this.capacity, this.tokens + refill);
    this.lastRefill = now;

    if (this.tokens >= 1) {
      this.tokens -= 1;
      return true;
    }

    return false;
  }
}

Conclusion

Pick the algorithm that matches your burst tolerance and enforcement precision. Many high-scale systems mix token bucket at the edge with sliding counters at the service layer to balance fairness and performance.

This post is licensed under CC BY 4.0 by the author.