AI InterviewSearch…Search problems, topics, or pages…Ctrl KGet started
Skip to content
SD notes

System Design · Note 11 of 22

Rate Limiting

Intermediate · 6 min read · updated 29 October 2026

Quick answer

What is rate limiting in system design?

Rate limiting caps how many requests a client may make in a period, such as 100 requests a minute per API key. Requests over the limit are refused, usually with HTTP 429 Too Many Requests. It protects servers from overload and abuse, keeps one client from starving others, and is built with algorithms such as token bucket, leaky bucket, fixed window and sliding window.

On this page
  1. Why limit
  2. Token bucket
  3. Leaky bucket
  4. Fixed window counter
  5. Sliding window
  6. Comparing them
  7. Where it runs
  8. Interview questions
  9. Common questions

A rate limiter decides, per client, whether a request may go through now or must be refused.

Why limit

  • Stops overload: one client cannot use all the capacity.
  • Fairness: every user, key or tenant gets a share.
  • Abuse and cost: slows brute-force logins and scraping, and caps spend on paid downstream APIs.
  • Key by user ID, API key, IP address (for anonymous traffic) or endpoint — often several limits at once.

Token bucket

  • A bucket holds up to capacity tokens and gains tokens at a fixed refill rate.
  • Each request takes one token; no token → 429.
  • Allows bursts up to the capacity, then holds the client to the refill rate.
  • Two numbers per client (tokens, last refill time) — cheap to store.
  • The most common choice for public APIs.
Token bucket: a burst spends the tokens, then requests wait for refills

The bucket holds up to 4 tokens and gains 1 a second. Every request must take a token; a request that finds the bucket empty is refused with 429.

0:01 / 0:21Step 1 of 8
Transcript · 8 steps

Leaky bucket

  • Requests enter a fixed-size queue; the server takes them out at a constant rate.
  • A full queue drops new requests.
  • Output is perfectly smooth — good for protecting a fragile downstream.
  • A burst waits in line, so it adds latency, and old requests can block new ones.
Leaky bucket: a queue drained at a constant rate

Requests join a queue of 3 slots, and each tick the server takes exactly one from its head, however fast they arrive. Three requests are about to arrive.

0:01 / 0:18Step 1 of 7
Transcript · 7 steps

Fixed window counter

  • One counter per client per window (say per minute); reset when the window changes.
  • Simplest to build: one atomic increment with an expiry.
  • Boundary burst: the full limit at the end of one window plus the full limit at the start of the next.
Fixed window: twice the limit gets through across a window edge

A fixed window keeps one counter per minute, limit 5, and resets it when the clock crosses into the next minute.

0:01 / 0:18Step 1 of 7
Transcript · 7 steps

Sliding window

  • Sliding log: store each accepted request's timestamp; count those in the last window. Exact, but memory grows with the limit.
  • Sliding counter: keep this window's and the previous window's counts and weight the previous one by how much of it still overlaps. Close to exact with two numbers.
Sliding window: the limit holds over every 60 seconds

A sliding window log keeps the time of every request it let through, and admits a new one only if fewer than 5 of those fall in the last 60 seconds.

0:01 / 0:22Step 1 of 8
Transcript · 8 steps

Comparing them

BurstsMemory per clientAccuracy
Token bucketUp to capacity2 numbersGood
Leaky bucketQueued, smoothedQueueGood
Fixed window2× at the edge1 counterWeak at edges
Sliding logNone1 timestamp per requestExact
Sliding counterSmall2 countersApproximate

Where it runs

  • API gateway or load balancer: one place for every service, before the work starts.
  • In the service: for limits that need business data (per plan, per tenant).
  • Distributed counters in Redis: every server reads and writes the same count. Make the check-and-increment atomic (INCR plus EXPIRE, or a Lua script) so two servers cannot both admit the last request.
  • Fail open or closed: if the counter store is down, decide whether to let traffic through or refuse it.

Tell the client what happened and when to retry:

HTTP
Copy
HTTP/1.1 429 Too Many Requests
Retry-After: 30
Content-Type: application/json

{"error": "rate limit exceeded, retry in 30 seconds"}

Interview questions

  • Which algorithm would you choose for a public API? Token bucket: it allows short bursts, holds the long-run rate, and needs two numbers per client.
  • Why is a fixed window not enough? Its reset lets up to twice the limit through around a window edge; a sliding window removes that.
  • How do you limit across ten API servers? Shared counters in Redis with atomic updates, or a gateway that sees all traffic.
  • What does the client get when limited? A 429 with Retry-After, so well-behaved clients back off instead of hammering.

Next: Microservices and API Gateway · Before this: Message Queues

Common questions

Why do we need rate limiting?

Without a limit, one buggy script, scraper or attacker can send enough requests to slow or crash a service for everyone. A limit caps each client's share, protects expensive resources such as databases and paid third-party APIs, and blunts brute-force login attempts.

What is the difference between token bucket and leaky bucket?

A token bucket lets a burst through up to its capacity, then holds clients to the refill rate. A leaky bucket queues requests and sends them on at a constant rate, so the output is smooth but a burst waits or is dropped when the queue is full.

What is the problem with a fixed window counter?

Counters reset at the window edge, so a client can send the full limit at the end of one window and the full limit again at the start of the next. That lets twice the limit through within a few seconds, which a sliding window prevents.

How do you rate limit across many servers?

Keep the counters in a shared store such as Redis, so every server sees the same count for a client. An atomic increment with an expiry, or a small Lua script for token bucket, avoids two servers both admitting the last allowed request.

What HTTP status code is used for rate limiting?

429 Too Many Requests. A good response also carries a Retry-After header saying how many seconds to wait, and many APIs send headers with the limit, the requests remaining and when the window resets, so clients can slow down before they are refused.

Rate Limiting in System Design, Animated

Rate limiting for system design interviews: token bucket, leaky bucket, fixed and sliding windows, Redis counters and 429, shown as looping animations.

  • Subject: System Design
  • Level: Intermediate
  • Reading time: 6 min
  • Updated: 2026-10-29

What is rate limiting in system design?

Rate limiting caps how many requests a client may make in a period, such as 100 requests a minute per API key. Requests over the limit are refused, usually with HTTP 429 Too Many Requests. It protects servers from overload and abuse, keeps one client from starving others, and is built with algorithms such as token bucket, leaky bucket, fixed window and sliding window.

A rate limiter decides, per client, whether a request may go through now or must be refused.

Why limit

  • Stops overload: one client cannot use all the capacity.
  • Fairness: every user, key or tenant gets a share.
  • Abuse and cost: slows brute-force logins and scraping, and caps spend on paid downstream APIs.
  • Key by user ID, API key, IP address (for anonymous traffic) or endpoint — often several limits at once.

Token bucket

  • A bucket holds up to capacity tokens and gains tokens at a fixed refill rate.
  • Each request takes one token; no token → 429.
  • Allows bursts up to the capacity, then holds the client to the refill rate.
  • Two numbers per client (tokens, last refill time) — cheap to store.
  • The most common choice for public APIs.
bucket 0/4+1 token/srequests in each second1s2002002002s2002004294293s4s2005s2002006s200
Token bucket: a burst spends the tokens, then requests wait for refills.
  1. The bucket holds up to 4 tokens and gains 1 a second. Every request must take a token; a request that finds the bucket empty is refused with 429.
  2. Second 1: the bucket is already full, so the refill is lost; 3 requests arrive and take 3 tokens, leaving 1.
  3. Second 2: a refill brings it to 2; 4 requests arrive: 2 take the last tokens and 2 get 429 Too Many Requests.
  4. Second 3: a refill brings it to 1; no request arrives, so the token is saved for the next burst.
  5. Second 4: a refill brings it to 2; 1 request arrives and takes a token, leaving 1.
  6. Second 5: a refill brings it to 2; 2 requests arrive and take 2 tokens, leaving 0.
  7. Second 6: a refill brings it to 1; 1 request arrives and takes a token, leaving 0.
  8. Over 6 seconds 9 requests passed and 2 were refused: a burst may spend the whole bucket at once, but the long-run rate is held to 1 a second.

Leaky bucket

  • Requests enter a fixed-size queue; the server takes them out at a constant rate.
  • A full queue drops new requests.
  • Output is perfectly smooth — good for protecting a fragile downstream.
  • A burst waits in line, so it adds latency, and old requests can block new ones.
ClientsAPI serverserved 5queue · 3 slotsdropped756
Leaky bucket: a queue drained at a constant rate.
  1. Requests join a queue of 3 slots, and each tick the server takes exactly one from its head, however fast they arrive. Three requests are about to arrive.
  2. Tick 1: the queue was empty, so the server takes nothing yet; requests 1, 2, 3 join the queue.
  3. Tick 2: request 1 leaks out to the server; request 4 joins the queue, 5 and 6 find it full and are dropped.
  4. Tick 3: nothing arrives and request 2 leaks out to the server, still one a tick.
  5. Tick 4: request 3 leaks out to the server; request 7 joins the queue.
  6. Tick 5: nothing arrives and request 4 leaks out to the server, still one a tick.
  7. Tick 6: request 7 leaks out and the queue is empty. 5 requests were served at a steady one a tick and 2 were dropped — the burst was smoothed, not passed on.

Fixed window counter

  • One counter per client per window (say per minute); reset when the window changes.
  • Simplest to build: one atomic increment with an expiry.
  • Boundary burst: the full limit at the end of one window plus the full limit at the start of the next.
window 1: 5/5window 2: 5/50s30s60s90s120s10 passed in 40 s
Fixed window: twice the limit gets through across a window edge.
  1. A fixed window keeps one counter per minute, limit 5, and resets it when the clock crosses into the next minute.
  2. Requests at 40, 44, 48 s fall in window 1; its counter is 3 of 5, so all pass.
  3. Requests at 52 and 56 s fill window 1: its counter reaches 5/5.
  4. Requests at 64, 68, 72 s fall in window 2; its counter is 3 of 5, so all pass — the counter started again from zero at 60 s.
  5. Requests at 76 and 80 s fill window 2: its counter reaches 5/5, though the last five came only seconds after window 1 filled.
  6. Requests at 88, 101, 106 s find window 2 already at 5/5 and are refused with 429.
  7. The boundary burst: 10 requests passed between 40 s and 80 s, twice the limit inside one minute, because the burst straddled the reset.

Sliding window

  • Sliding log: store each accepted request's timestamp; count those in the last window. Exact, but memory grows with the limit.
  • Sliding counter: keep this window's and the previous window's counts and weight the previous one by how much of it still overlaps. Close to exact with two numbers.
0s30s60s90s120sbusiest minute: 5
Sliding window: the limit holds over every 60 seconds.
  1. A sliding window log keeps the time of every request it let through, and admits a new one only if fewer than 5 of those fall in the last 60 seconds.
  2. Requests at 40, 44, 48 s each find fewer than 5 timestamps in the last 60 seconds, so all pass and are logged.
  3. At 52 and 56 s the log reaches 5 timestamps inside the window: the limit, with every one of them still counted.
  4. At 64 s the window reaches back to 4 s and already holds 5. There is no reset to exploit, so this request gets 429.
  5. Requests at 68, 72, 76, 80, 88 s are refused too: the window holds 5 until the request at 40 s slides out at 100 s.
  6. At 101 s older requests have slid out of the window; only 4 remain, so this one passes.
  7. At 106 s older requests have slid out of the window; only 4 remain, so this one passes as well.
  8. 7 of 13 requests passed, and no 60-second span holds more than 5. The price is memory: one timestamp per accepted request.

Comparing them

BurstsMemory per clientAccuracy
Token bucketUp to capacity2 numbersGood
Leaky bucketQueued, smoothedQueueGood
Fixed window2× at the edge1 counterWeak at edges
Sliding logNone1 timestamp per requestExact
Sliding counterSmall2 countersApproximate

Where it runs

  • API gateway or load balancer: one place for every service, before the work starts.
  • In the service: for limits that need business data (per plan, per tenant).
  • Distributed counters in Redis: every server reads and writes the same count. Make the check-and-increment atomic (INCR plus EXPIRE, or a Lua script) so two servers cannot both admit the last request.
  • Fail open or closed: if the counter store is down, decide whether to let traffic through or refuse it.

Tell the client what happened and when to retry:

HTTP/1.1 429 Too Many Requests
Retry-After: 30
Content-Type: application/json

{"error": "rate limit exceeded, retry in 30 seconds"}

Interview questions

  • Which algorithm would you choose for a public API? Token bucket: it allows short bursts, holds the long-run rate, and needs two numbers per client.
  • Why is a fixed window not enough? Its reset lets up to twice the limit through around a window edge; a sliding window removes that.
  • How do you limit across ten API servers? Shared counters in Redis with atomic updates, or a gateway that sees all traffic.
  • What does the client get when limited? A 429 with Retry-After, so well-behaved clients back off instead of hammering.

Next: Microservices and API Gateway · Before this: Message Queues

Common questions

Why do we need rate limiting?

Without a limit, one buggy script, scraper or attacker can send enough requests to slow or crash a service for everyone. A limit caps each client's share, protects expensive resources such as databases and paid third-party APIs, and blunts brute-force login attempts.

What is the difference between token bucket and leaky bucket?

A token bucket lets a burst through up to its capacity, then holds clients to the refill rate. A leaky bucket queues requests and sends them on at a constant rate, so the output is smooth but a burst waits or is dropped when the queue is full.

What is the problem with a fixed window counter?

Counters reset at the window edge, so a client can send the full limit at the end of one window and the full limit again at the start of the next. That lets twice the limit through within a few seconds, which a sliding window prevents.

How do you rate limit across many servers?

Keep the counters in a shared store such as Redis, so every server sees the same count for a client. An atomic increment with an expiry, or a small Lua script for token bucket, avoids two servers both admitting the last allowed request.

What HTTP status code is used for rate limiting?

429 Too Many Requests. A good response also carries a Retry-After header saying how many seconds to wait, and many APIs send headers with the limit, the requests remaining and when the window resets, so clients can slow down before they are refused.

← Message Queues · Microservices and API Gateway →