Skip to main content
System DesignInterview Prep

Design a Rate Limiter: System Design Interview Walkthrough

A step-by-step answer to the rate limiter system design question: requirements, the four main algorithms and their trade-offs, a distributed Redis design, and the follow-up questions interviewers ask.

InterviewPilot TeamOctober 17, 20265 min read

The rate limiter is one of the most asked system design questions because it's small enough to finish in 45 minutes yet touches algorithms, distributed state and failure handling. For a broader framework, see the system design cheat sheet.

1. Clarify requirements

  • Limit by user, IP, API key or endpoint?
  • Hard (reject) or soft (throttle/queue)?
  • Single server or distributed?
  • Scale: requests per second, number of distinct clients?
  • Should rules be configurable (e.g., 100/min for free, 1000/min for paid)?

2. The algorithms

AlgorithmHow it worksProsCons
Fixed windowCounter per window, reset each periodSimple, cheapUp to 2x burst at window boundaries
Sliding window logStore timestamps, count those in the last windowAccurateMemory heavy
Sliding window counterWeighted blend of current and previous window countsGood accuracy, low memoryApproximate
Token bucketBucket refills at a fixed rate; each request takes a tokenAllows bursts, simpleNeeds tuning of size and rate
Leaky bucketQueue drained at a constant rateSmooth outputBursts are delayed or dropped

Pick token bucket for most APIs and justify it.

3. High-level design

Client -> API Gateway / Middleware -> Rate Limiter -> Service
                                          |
                                      Redis (counters)
                                          |
                                     Rules config
  • Key: ratelimit:{client_id}:{endpoint}
  • On each request, load rule, update the counter, allow or reject.
  • Rejected requests return 429 with Retry-After and optionally X-RateLimit-Remaining.

4. Making it correct when distributed

The classic bug is a race: two servers read the counter, both see room, both increment. Fixes:

  • Atomic Redis operations: INCR with EXPIRE, or a Lua script that reads and updates in one step.
  • Sharding by key so a client's counter lives on one Redis node.
  • Local + global: servers keep local counters and sync periodically, trading accuracy for fewer calls.

5. Failure and edge cases

  • Redis down: fail open (allow traffic, risk overload) or fail closed (block, risk outage). Most public APIs fail open with alerts.
  • Clock skew: use Redis time, not server time.
  • Hot keys: a single abusive client concentrating load on one shard.
  • Race on rule changes, IPv6 and NAT (many users behind one IP).

6. Follow-ups interviewers ask

  • How would you support different limits per plan?
  • How do you rate limit by both user and IP?
  • How would you avoid the limiter becoming a bottleneck?
  • How do you test it under load?

Practise with the other questions in 12 common system design interview questions.

Worked example: token bucket with Redis

A token bucket stores two values per key: the current token count and the last refill time. On each request:

  1. Read both values.
  2. Compute tokens to add: elapsed time multiplied by the refill rate, capped at the bucket size.
  3. If at least one token is available, subtract one and allow the request. Otherwise reject.
  4. Write the new count and timestamp.

Steps 1 to 4 must be one atomic operation, so implement them as a Lua script executed by Redis. Set an expiry on the key slightly longer than the time to refill a full bucket, so idle clients do not accumulate keys.

Sizing the numbers

Say you serve 10 million daily active users, each with a bucket entry of about 100 bytes: that is roughly 1 GB of state, easily held by one Redis node, and sharded across a few for throughput and failover. The load is the request rate, not the storage. At 50,000 requests per second, one Lua call per request is within what a small Redis cluster handles, but add a local in-memory pre-check if you want to cut calls.

Choosing limits

DimensionExampleNotes
Per user100 requests per minuteFair use
Per IP1,000 per minuteCatches unauthenticated abuse; beware shared NAT
Per endpoint5 per minute for loginProtects sensitive routes
Per planFree 60, Paid 600Config-driven from a rules store
GlobalTotal backend capacityProtects against surges

What to return to the client

  • 429 Too Many Requests
  • Retry-After with seconds until the next token
  • Optional headers: remaining tokens and reset time, so well-behaved clients can back off

Where the limiter sits

  • API gateway: one place for all services; the common answer
  • Service middleware: finer control, duplicated logic
  • Client SDK hints: cooperative only, never trust alone

Failure modes to discuss

FailureChoice
Redis unavailableFail open for public APIs (with alerts), fail closed for sensitive ones like login
Network partitionLocal fallback counters with generous limits
Clock driftUse the store's clock
Sudden hot keyReplicate reads or shard a user's counters by sub-key

Interview tips

Lead with clarifying questions, state the algorithm and why, then draw the architecture. Mention that you would test with a load generator that simulates bursts at window boundaries, which exposes fixed-window flaws.

Frequently asked questions

Was this article useful?

Discussion

No comments yet. Been through an interview like this? Your experience could help the next candidate.

You’re posting anonymously — it appears after a quick review. Sign in to post under your name instantly.
0/2000

Bring backup into your next round.

Install InterviewPilot, upload your resume, and walk in with structured answers a keystroke away — visible to you and nobody else.