LLD Hub
lldstrategydistributed-systems

How to Design a Rate Limiter | LLD Interview Guide

Design a rate limiter with Token Bucket, Sliding Window, and Fixed Window algorithms. Strategy pattern, distributed state, and burst handling explained.

13 April 2025·8 min read

Practice this problem

Rate Limiter — get AI-scored feedback on your solution

Solve it →

Rate Limiter is a favorite LLD problem at companies that run high-traffic APIs. It tests your knowledge of multiple algorithms, distributed systems, and the Strategy pattern. Here's how to design it in an interview.

Four Algorithms You Must Know

1. Fixed Window Counter

// Count requests in a fixed time window (e.g., 100 req/min)
class FixedWindowLimiter implements RateLimitStrategy {
  check(userId: string): boolean {
    const windowKey = `${userId}:${Math.floor(Date.now() / 60000)}`;
    const count = this.store.increment(windowKey);
    if (count === 1) this.store.expire(windowKey, 60);
    return count <= this.limit;
  }
}

Problem: Boundary burst — user can fire 200 requests at 11:59 + 12:00

2. Sliding Window Log

// Store timestamp of every request in a sorted set
class SlidingWindowLogLimiter implements RateLimitStrategy {
  check(userId: string): boolean {
    const now = Date.now();
    this.store.removeOlderThan(userId, now - 60000);
    const count = this.store.count(userId);
    if (count >= this.limit) return false;
    this.store.add(userId, now);
    return true;
  }
}

3. Token Bucket (supports burst)

class TokenBucketLimiter implements RateLimitStrategy {
  check(userId: string): boolean {
    const bucket = this.getBucket(userId);
    const now = Date.now();
    const elapsed = (now - bucket.lastRefill) / 1000;
    bucket.tokens = Math.min(this.capacity, bucket.tokens + elapsed * this.refillRate);
    bucket.lastRefill = now;
    if (bucket.tokens < 1) return false;
    bucket.tokens -= 1;
    return true;
  }
}

4. Leaky Bucket (smooth output)

Requests queue up; processed at a fixed rate. Excess requests are dropped. Use when you need smooth, predictable throughput.

Strategy Pattern for Algorithm Selection

interface RateLimitStrategy { check(userId: string): boolean; }

class RateLimiter {
  constructor(private strategy: RateLimitStrategy) {}
  isAllowed(userId: string): LimitResult {
    const allowed = this.strategy.check(userId);
    return { allowed, remaining: this.strategy.getRemaining(userId), retryAfter: allowed ? 0 : this.strategy.getRetryAfter(userId) };
  }
}

Layered Limits with Decorator

// Global limit + per-user limit + per-endpoint limit
const limiter = new EndpointLimiter(
  new UserLimiter(
    new GlobalLimiter(baseStrategy, 10000),  // 10k global req/s
    1000                                      // 1k per-user req/s
  ),
  "/api/search",
  100                                         // 100 search req/s
);

Ready to practice?

Submit your solution and get AI-scored feedback on OOP, SOLID principles, design patterns, and code quality.

Solve Rate Limiter