System Design Interview Questions: Rate Limiting Patterns

Reviewed by Mark Dickie · Last updated

7 questions on this page you can answer and see marked on the spot. Go to the first one

Rate limiting is a technique that controls how many requests a client can send to a service within a fixed time window, protecting backend resources from overload and abuse. For system design interviews, you should be able to identify the right rate-limiting algorithm for a given scenario, explain the trade-offs between accuracy and memory, and discuss how to enforce limits in a distributed environment where multiple servers share state. Expect to justify where the limiter sits (client, API gateway, application server) and how it behaves under partial failures.

The most common algorithms and their core properties:

AlgorithmHow it worksStrengthsWeaknesses
Token bucketTokens refill at a fixed rate; each request consumes one tokenAllows bursts up to bucket capacity; memory-efficientHard to tune burst size precisely
Leaky bucketRequests enter a queue and drain at a constant rateSmooths traffic to a steady output rateQueue can grow unbounded under sustained load
Fixed windowCounter resets at each time boundary (e.g. every minute)Simple to implement and reason aboutBurst at window edges can double traffic
Sliding window logStores timestamps of each request, removes expired onesMost accurate limit enforcementHigh memory and compute per request
Sliding window counterSplits the window into sub-windows and weights the current positionGood accuracy with low memorySlight over- or under-counting at boundaries

What does a rate-limiting interview question typically ask you to design?

You are usually given a service (a URL shortener, an API gateway, a login endpoint) and asked to add rate limiting to it. The interviewer wants to see you work through the following decisions in order:

  1. Choose an algorithm that fits the traffic pattern — token bucket for bursty APIs, leaky bucket for smoothing, sliding window for strict per-user caps.
  2. Decide where to enforce the limit — at the load balancer, in an API gateway, or inside the application logic — and explain the trade-off of each placement.
  3. Pick a data store for distributed counting — Redis with INCR and EXPIRE is the default choice; explain why atomicity matters when multiple server nodes share the counter.
  4. Handle edge cases: what happens when Redis is temporarily unavailable, whether to fail open or fail closed, and how to return a 429 Too Many Requests response with a Retry-After header.
  5. Discuss observability — logging rejected requests, exposing metrics, and setting alerts so you can tune limits based on real traffic.

How do you implement distributed rate limiting across multiple servers?

In a single-server setup an in-memory counter is enough. Once you scale horizontally, every server instance needs to agree on the current count, so the limiter must read from and write to a shared store. Redis is the go-to because its single-threaded model makes operations like INCR atomic without explicit locking. For stricter guarantees you can use a Lua script that checks the count and increments it in one server-side round trip, avoiding race conditions between a read and a write. The cost is added latency per request — usually one to two milliseconds for a Redis round trip — which is acceptable for most API gateways but may matter for ultra-low-latency paths.

Key facts

  • Tarmac has 26 System Design interview questions on this topic, 25 of them on this page, at difficulty 1–4 of 5.
  • Tarmac tracked 3,763 job postings asking for System Design in September 2026.
  • Roles asking for System Design advertise a median base salary of US$180,347, across 1,250 job postings as of September 2026.
  • Tarmac last reviewed these System Design interview questions on 13 October 2026.

At a glance

Questions25 shown · 26 in the bank
Difficulty1–4 of 5
FormatsTrue / false, Short answer, Fill in the blank, Flashcard, Multiple choice, Multiple answer, Design exercise

What you'll review

  1. rate limiting

Practice questions

Try one before you open the answer. Pick an option and press Check; it's marked on the spot.

A token bucket rate limiter allows short bursts of traffic that exceed the steady-state refill rate, as long as the bucket has accumulated tokens.#

Options

Show answer

A token bucket rate limiter does allow short bursts above its refill rate. Unused tokens accumulate up to the bucket's capacity during idle periods, and a burst can consume them, so throughput briefly exceeds the steady refill rate until the bucket empties.

Why:

The token bucket's defining feature is burst tolerance. Tokens refill at a fixed rate, and unused tokens accumulate up to the bucket's capacity during idle periods. A burst can then consume those accumulated tokens, so the client's throughput briefly exceeds the steady refill rate until the bucket is depleted. The statement is true because it describes exactly this behavior.

What is the main purpose of rate limiting in a web application or API?#

Show answer

Rate limiting caps how many requests a single client or source can make within a given time window. Its purpose is to protect the server or API from being overwhelmed by excessive traffic, which prevents abuse, denial-of-service conditions, and resource exhaustion while keeping the service available for legitimate users.

Why:

Rate limiting enforces a maximum number of requests a client may make in a fixed time window, so that no single source can monopolize or exhaust server resources. This defends against abuse, accidental flooding, and denial-of-service attacks, and it keeps capacity available for legitimate traffic. The core idea is protecting the service from excessive request volume from any one source.

In a token bucket rate limiter, what condition must be met for an incoming request to be allowed through?#

Show answer

At least one token must be available in the bucket at the time of the request. If the bucket is empty, the request is denied or queued until a token is refilled.

Why:

A token bucket rate limiter admits a request only when the bucket holds at least one token at the moment the request arrives. The admitted request consumes one token, and the bucket is refilled over time at a fixed rate. When the bucket is empty, no token can be consumed, so the request cannot be admitted immediately and must be rejected or delayed until a new token is added.

The _____ _____ algorithm maintains a bucket that holds up to a maximum number of tokens. Tokens are added at a fixed refill rate, and each incoming request consumes one token; if the bucket is empty, the request is rejected or delayed.#

Show answer

The token bucket algorithm maintains a bucket that holds up to a maximum number of tokens. Tokens are added at a fixed refill rate, and each incoming request consumes one token; if the bucket is empty, the request is rejected or delayed.

Why:

The algorithm described is the token bucket. The bucket capacity caps the burst size a client can send at once, while the fixed refill rate sets the long-term average throughput. Because tokens accumulate while traffic is idle, short bursts are allowed up to the capacity, and sustained traffic is held to the refill rate.

Explain the token bucket rate limiting algorithm: how it works, what the bucket capacity and refill rate each control, and why it allows bursts.#

Show answer

The token bucket algorithm holds up to N tokens, refilled at R tokens per second. Each request consumes one token. Empty bucket → reject/delay. Capacity N = max burst; refill rate R = steady throughput. It permits bursts but averages to R over time.

Why:

The token bucket decouples burst capacity (bucket size) from long-term rate (refill rate). Tokens accumulate during idle periods up to the capacity, enabling bursts; the steady refill rate limits average throughput.

Which rate-limiting algorithm uses a fixed-capacity pool of tokens that refill at a constant rate, allowing short bursts of requests up to the pool's capacity while bounding the average rate over time?#

Show answer

The token bucket algorithm. It maintains a fixed-capacity pool of tokens that refill at a constant rate; each request consumes one token, so short bursts up to the bucket's capacity are allowed while the average rate stays bounded.

Why:

The token bucket algorithm holds up to N tokens (the bucket capacity) and adds tokens at a steady refill rate R. Each incoming request removes one token; if the bucket is empty the request is rejected or delayed. This design permits bursts up to the bucket capacity while maintaining an average rate of R requests per unit time. The leaky bucket algorithm, modeled as a queue, smooths all traffic into a steady stream and does not allow bursts. Fixed-window and sliding-window counters track request counts within time windows but do not model a refillable capacity pool.

In a token bucket rate limiter, each incoming request must consume one _____ from the bucket to be allowed through. If none is available, the request is rejected. The bucket refills at a constant _____ up to a maximum capacity, which determines the long-term average request rate.#

Show answer

In a token bucket rate limiter, each incoming request must consume one token from the bucket to be allowed through. If none is available, the request is rejected. The bucket refills at a constant rate up to a maximum capacity, which determines the long-term average request rate.

Why:

In a token bucket, the bucket holds a maximum number of tokens (its capacity). Each request must consume one token to pass; if no token is available, the request is rejected. Tokens are added at a constant refill rate (also called the fill rate) up to the capacity, and that rate sets the long-term average throughput the limiter allows. So the first blank is 'token' and the second is 'rate'.

A tenant has a global limit of 100 requests per second. Your API runs on 10 edge servers, and each server keeps its own in-memory counter that allows at most 100 requests per second for that tenant. Explain why this setup can let the tenant exceed the global limit, and name one way to fix it.#

Show answer

Each server counts only the requests it sees, so a tenant whose traffic is spread across all 10 servers can send 100 requests per second to each one, for a total of about 1,000 requests per second. To fix this, share one counter across the servers, for example a central store such as Redis that every server increments atomically. Another option is to split the limit into per-server quotas (100 / 10 = 10 per server each second), though that wastes capacity when traffic is uneven.

Why:

Each edge server enforces its own counter, so the limit applies per server rather than per tenant. Traffic that is spread across the servers is never compared against the combined total, so the tenant can receive up to 10 times the intended rate. The fix is to make the accounting global, either with one shared counter that all servers update or by dividing the global limit into fixed per-server shares that add up to the total.

What is the Token Bucket algorithm and how does it work?#

Show answer

Token Bucket is a rate-limiting algorithm where tokens are added to a bucket at a fixed refill rate. Each request consumes one token; if the bucket is empty, the request is rejected. The bucket has a maximum capacity, so tokens can accumulate up to that cap during idle periods, allowing short bursts of traffic up to the bucket size. This makes it well-suited for APIs that need to allow bursts while enforcing an average rate over time. Alternative approaches include fixed-window counters and sliding-window logs.

Why:

Token Bucket is the most widely taught rate-limiting pattern in system design interviews. Its key property is that it separates average throughput (governed by the refill rate) from burst tolerance (governed by the bucket capacity). This distinguishes it from a simple fixed-window counter, which rejects any request beyond a fixed count per window regardless of prior idle time. At difficulty 1, the goal is to recall the core mechanism: tokens refill continuously, requests consume tokens, and an empty bucket means the request is denied.

Which rate-limiting algorithm allows short bursts of requests up to a configured burst capacity (bucket size) while still enforcing a steady long-term average rate?#

Show answer

Token Bucket

Why:

The Token Bucket algorithm maintains a bucket that is filled with tokens at a constant rate. Each request consumes one token; if the bucket is empty the request is denied. Because the bucket can accumulate tokens up to its capacity, a sudden burst of requests can be served as long as tokens are available. Over the long term the average rate equals the token refill rate, so both burst tolerance and steady-rate enforcement are achieved. The Fixed Window Counter resets its count at each window boundary and has no burst-bucket concept. The Leaky Bucket smooths traffic to a constant output rate and does not allow bursts. The Sliding Window Log tracks individual request timestamps, which controls bursts but at higher memory cost.

In the Fixed Window Counter rate-limiting algorithm, time is divided into equal-length intervals and a counter tracks requests within each interval. A known drawback is that a burst can occur at the boundary of two consecutive windows. Explain this drawback: how many times the configured limit can be sent in a brief period, and which algorithm family mitigates the issue by not fully resetting the count at each window boundary?#

Show answer

A burst can occur at the boundary of two consecutive windows, allowing up to 2 times (double) the configured limit in a brief period. The Sliding Window approaches (Sliding Window Counter and Sliding Window Log) mitigate this by considering the previous window's count (weighted) or by logging individual timestamps, so the limit is never fully reset at a hard boundary.

Why:

The Fixed Window Counter resets its counter at each window boundary. If the limit is N requests per window, a client can send N requests just before a boundary and another N just after, yielding up to 2N requests in a very short interval, roughly double the configured rate. The Sliding Window Counter and Sliding Window Log algorithms address this by considering the previous window's count (weighted) or by logging individual timestamps, so the limit is never fully reset at a hard boundary.

True or false: With a fixed-window counter rate limiter that allows N requests per window, a burst of requests straddling the boundary between two consecutive windows can cause up to 2N requests to be served within a short interval.#

Options

Show answer

A fixed-window counter rate limiter can serve up to twice its per-window limit in a short interval. Clients can send N requests just before a window ends and N more right after the counter resets at the next window's start. Sliding-window algorithms are a common fix for this boundary burst.

Why:

The fixed-window counter resets its count to zero at the start of each window. A client can send N requests just before a window ends, all of which are allowed because the count stays within the limit. Immediately after the reset at the start of the next window, the client can send N more, all allowed. That yields up to 2N served requests across a short interval that straddles the boundary. This is a well-known drawback of the fixed-window approach, which sliding-window algorithms mitigate.

In rate limiting, the _____ algorithm smooths incoming traffic by enqueuing requests and releasing them at a constant output rate, in contrast to the token bucket algorithm, which allows bursts up to the bucket's capacity.#

Show answer

In rate limiting, the leaky bucket algorithm smooths incoming traffic by enqueuing requests and releasing them at a constant output rate, in contrast to the token bucket algorithm, which allows bursts up to the bucket's capacity.

Why:

The leaky bucket algorithm, in its queuing or metering variant, enqueues incoming requests and processes them at a fixed output rate, which smooths bursts regardless of how requests arrive. The token bucket, by contrast, accumulates tokens and lets a burst through up to the bucket's capacity before throttling. The sentence describes constant-rate smoothing, so the blank is the leaky bucket algorithm.

In the sliding-window log rate-limiting algorithm, what does the limiter store per client, and how does it decide whether to allow or reject a new request?#

Show answer

The limiter stores a log of request timestamps, one per recent request, for each client, typically in a sorted list or deque. When a new request arrives, it first removes all timestamps older than the current window start. If the remaining count is below the configured limit, it appends the new request's timestamp and allows the request; otherwise it rejects it.

Why:

The sliding-window log keeps a per-client record of individual request timestamps within a rolling time window. On each incoming request, it evicts timestamps that fall outside the window, then checks whether the remaining count is under the limit. If so, it records the new timestamp and allows the request; if not, it rejects it. This approach avoids the fixed-window boundary problem but requires storing and pruning a timestamp per request, which costs more memory than counter-based schemes.

Which rate limiting algorithm is specifically designed to permit short traffic bursts up to a configured maximum, while still enforcing a configurable long-term average rate?#

Options

Show answer

Token bucket is the rate limiting algorithm built to allow short bursts up to a configured bucket capacity while enforcing a configurable long-term average rate set by its refill rate. Leaky bucket queues traffic to a constant output rate, and fixed window and sliding window log have no dedicated burst-capacity parameter.

Why:

The token bucket holds up to a fixed number of tokens and refills them at a fixed rate. Each request consumes a token, so a burst of requests passes as long as tokens remain, up to the bucket capacity. The refill rate fixes the long-term average throughput. The leaky bucket implemented as a queue drains at a constant output rate, so it smooths traffic and does not permit bursts by design. The fixed window counter resets at window boundaries and can double-count at edges, but it has no burst-size parameter. The sliding window log stores individual request timestamps for precise counting, but it has no explicit burst-capacity concept.

In the token bucket rate limiting algorithm, the _____ sets the maximum burst size by capping how many tokens can accumulate, while the _____ sets the steady-state average request rate at which new tokens are added.#

Show answer

In the token bucket rate limiting algorithm, the bucket capacity sets the maximum burst size by capping how many tokens can accumulate, while the refill rate sets the steady-state average request rate at which new tokens are added.

Why:

In the token bucket algorithm, each request consumes one or more tokens from a bucket. The bucket's maximum capacity limits how many unused tokens can accumulate, which directly bounds the burst size a client can send at once. Tokens are added at a fixed refill (generation) rate, which determines the long-term average throughput. If the bucket is full, new tokens are discarded; if it is empty, requests are rate-limited until a token becomes available.

An API must allow clients a sustained rate of 10 req/s but also tolerate short bursts (e.g. a client firing 50 requests after being idle). Which rate-limiting algorithm naturally supports this?#

Options

Show answer

Use a token bucket. It refills tokens at the sustained rate and lets unused tokens accumulate up to the bucket size, so an idle client banks capacity and can spend it in a burst — exactly the behaviour required. A leaky bucket deliberately flattens bursts to a fixed drain rate, and a fixed-window counter is bursty across boundaries in the wrong way.

Why:

Token bucket refills tokens at the sustained rate and lets unused tokens accumulate up to the bucket size, so an idle client banks capacity and can spend it in a burst — exactly the behaviour required. A leaky bucket smooths output to a fixed drain rate, deliberately flattening bursts, which is the opposite of what we want. A fixed window counter is bursty in the wrong way: it permits double the limit across a window boundary while still rejecting legitimate bursts inside a window. A concurrency semaphore of 1 limits parallelism, not request rate, and would serialise the client rather than rate-limit it.

Design a distributed rate-limiting service that sits in front of a fleet of API servers. It must enforce per-client quotas (for example, 1000 requests per minute per API key) across many application server instances, at a peak of 50,000 RPS. In your answer, address: (1) which rate-limiting algorithm you would choose and why; (2) how you store and coordinate shared counters across instances; (3) how you handle an extreme hot key, where one API key generates a disproportionate share of the load; and (4) what the system does when the rate-limiting backend becomes unavailable.#

Show answer

Algorithm: I would use a sliding window counter. Each minute is split into fixed windows, and the count is estimated as a weighted mix of the current and previous window counts. This gives close to rolling-window accuracy with two integers of state per key, which matters at 50K RPS across millions of keys. A sliding window log would store a timestamp per request and cost far more memory. A plain fixed window can admit up to twice the limit across a window boundary, violating the quota. If the contract also allows bursts, a token bucket can sit alongside for short-burst control.

Shared state: Local per-instance counters undercount, because one API key's requests are spread across instances by the load balancer. I would keep counters in Redis. The check-and-increment must be atomic, for example a Lua script run via EVALSHA that reads the window counts, checks the limit, and increments only if under quota, all in one round trip. Separate GET and INCR calls would allow lost updates when instances race on the same key. Each key gets a TTL so stale windows expire.

Hot keys: One API key at 50K RPS would saturate a single Redis shard. I would split that key's quota into K sub-counters on different Redis nodes, each with a budget of roughly limit/K, enforced atomically by the same Lua script, and route each request to a sub-counter at random. This spreads load across shards. A short-lived local cache on each application instance can absorb some repeated checks, at the cost of a small overshoot bounded by the cache window.

Degradation: If Redis is unreachable, I would fail open so legitimate traffic is not blocked by a non-critical dependency. I would pair this with a circuit breaker to stop hammering Redis during the outage, alerting that pages on-call, and a local in-memory fallback that enforces a per-instance share of the global limit (global limit divided by the expected instance count), which prevents an unbounded free-for-all until Redis recovers.

Why:

This is an open design question with four sub-parts, so it stays SHORT_ANSWER and is graded against the keywords and the sample answer. A strong answer names a concrete algorithm with a justified trade-off (sliding window counter balances accuracy against memory versus a sliding log and avoids the fixed-window boundary burst), uses Redis with an atomic Lua script to coordinate counters across instances, splits a hot key's quota across multiple shards, and states an explicit degradation policy with a fallback. The sample answer splits the quota per shard so each shard's Lua script stays single-key, which avoids Redis Cluster cross-slot limits.

An API team needs to limit each client to no more than 1000 requests within any rolling 60-second window, and needs an exact count of requests in that window rather than an approximation. Which rate-limiting algorithm stores individual request timestamps and gets this exact count by evicting entries older than the window?#

Options

Show answer

Sliding window log is the rate-limiting algorithm that stores each request's timestamp and evicts entries older than the window, so the remaining count is the exact number of requests in a rolling window. Sliding window counter approximates this with weighted buckets, while token bucket and fixed window counter do not give an exact rolling-window count.

Why:

Sliding window log keeps each request's timestamp and evicts entries older than the 60-second boundary, so the number of remaining entries is the exact count of requests in the current rolling window, with no estimation or interpolation. The sliding window counter uses fixed sub-window buckets with weighted interpolation between adjacent buckets, which yields an approximate count. The token bucket enforces a steady refill rate with burst capacity rather than counting requests within a window, so it does not produce a windowed count. The fixed window counter resets at fixed boundaries, so it does not track a rolling window and can allow up to twice the limit across adjacent windows.

You are implementing rate limiting at the API gateway layer to protect backend services. Which statements are accurate about common rate-limiting algorithms and their trade-offs?#

Options

Pick every one that applies.

Show answer

Four statements are accurate: a token bucket allows short-term bursts up to the bucket capacity while enforcing an average refill rate, a sliding window log tracks per-request timestamps and prevents the burst-at-boundary problem, a fixed window counter can allow up to 2× the limit at a window boundary, and distributed rate limiting requires a shared counter (e.g. in Redis) to enforce limits accurately. Token buckets do not guarantee zero burstiness — that is the leaky bucket.

Why:

Token buckets are the canonical burst-tolerant algorithm: tokens accumulate at the refill rate up to bucket capacity, so a burst of capacity tokens is permitted before the rate kicks in. The sliding window log solves the boundary problem by tracking exact timestamps, so a burst at the end of one period plus the start of the next can't exceed the limit. The fixed window boundary exploit is a real and well-documented flaw: if the limit is 100/minute and 100 requests arrive in the last second of window 1 and 100 in the first second of window 2, you've accepted 200 in two seconds. Distributed coordination is essential in multi-node gateways — without a shared counter each node enforces the limit independently, effectively multiplying it by the number of nodes. The claim that token bucket guarantees zero burstiness is wrong: token bucket explicitly allows bursts; the algorithm that spaces requests exactly 1/rate apart is the leaky bucket (or its fixed-rate output variant), not the token bucket.

The _____ rate-limiting algorithm refills capacity at a steady rate while permitting short bursts up to its capacity, whereas the _____ algorithm smooths output to a strictly constant rate by draining queued requests at a fixed pace.#

Show answer

The token bucket rate-limiting algorithm refills capacity at a steady rate while permitting short bursts up to its capacity, whereas the leaky bucket algorithm smooths output to a strictly constant rate by draining queued requests at a fixed pace.

Why:

Token bucket adds tokens at a fixed rate up to a maximum; each request consumes a token, so accumulated tokens allow bursts while the average stays bounded — this is the most common API limiter. Leaky bucket processes requests out of a queue at a constant rate regardless of arrival pattern, which shapes traffic into a smooth stream but offers no burst allowance. Sliding-window counters are a third common option that trade a little accuracy for cheap memory.

Design a distributed rate limiter for a public API gateway. Every incoming request must be checked against a per-API-key quota before it reaches a backend.#

Show answer

Requirements. The limit is per API key over a rolling window (1,000/min), checked on every request. I'd confirm: is a brief burst above the steady rate acceptable (token bucket says yes, up to the bucket size), and — most importantly — if the counter store is unavailable, do we fail open (let requests through, protect availability) or fail closed (reject, protect the backend)? For a public gateway I default to fail-open with alerting, since rate limiting is a guardrail, not the product.

Algorithm. I'd use a sliding-window counter (or token bucket). Fixed-window counters are simplest but allow ~2× the limit across a boundary: a client sends 1,000 in the last second of minute N and another 1,000 in the first second of N+1. The sliding-window counter weights the previous window's count by the overlap fraction, smoothing that out at a fraction of the cost of a full per-request log. Token bucket is the equivalent framing when you want to allow controlled bursts: refill at limit/60 per second, cap the bucket at the burst size.

Distributed counter. Counters live in a shared in-memory store (Redis) so all gateway nodes see the same count. The check must be atomic — a read-modify-write from two nodes races and under-counts — so I use INCR + EXPIRE (or a small Lua script that does increment, TTL, and the limit comparison in one round trip). The API key is the Redis key, so all operations for one key land on one shard.

Accuracy vs latency. A central store is exact but adds a network hop to every request. To hold <5 ms p99, I keep the store cell-local (one Redis per region/cell, no cross-region hop) and accept that limits are enforced per-region. If even one hop is too much, the alternative is local per-node token buckets that sync counts every few hundred ms — fast and highly available, but approximate (a key can briefly exceed the global limit by roughly the number of nodes). I'd start central-per-cell and only move to local+sync if the hop proves too costly.

Failure modes. Store down: fail open, serve requests un-limited, and alert. Hot key: a single key's counter is one Redis key on one shard, so a 200K-req/sec key hammers that shard — I'd detect it and shard that key's counter into N sub-counters (key:0..N) summed on read, or shed it at the edge. Clock skew: window algorithms key off time, so I rely on the store's clock (single source) rather than each node's wall clock.

Scaling. At ~500K ops/sec the counter store is sharded by key across a Redis cluster; because each key's ops stay on its shard, adding shards scales throughput linearly. Replicas give availability, and per-cell stores keep latency flat as we add regions. The bottleneck is always the hottest single key, handled by sub-sharding as above.

Why:

A distributed rate limiter is the canonical 'shared mutable counter in the hot path' problem. The two decisions interviewers probe are the algorithm (sliding-window counter or token bucket, because fixed-window allows a 2× burst across the window boundary) and where the counter lives (a shared atomic store is exact but adds a hop; local counters with sync are fast but approximate — the <5 ms budget forces you to pick a point on that spectrum). The atomic increment-and-check (INCR+EXPIRE or Lua) is what prevents concurrent nodes from racing and under-counting. The dominant failure modes are the fail-open/fail-closed choice when the store dies and the hot-key problem, where one key's counter overloads its single shard.

Design a distributed rate-limiting system for an API gateway serving 100,000+ requests/sec across 20 geographically distributed edge nodes. Each client must be rate-limited independently (per API key), with limits configurable as both requests-per-second and requests-per-minute. The system must handle edge-node failures and Redis unavailability gracefully and must not reject valid traffic solely due to infrastructure issues. Cover your algorithm choice, how rate-limit state is shared and partitioned across nodes, how concurrent requests are handled atomically, and your failure-mode strategy.#

Show answer

I would use a token bucket algorithm per API key, stored in a Redis cluster with consistent hashing on the API key to partition state across shards. Token bucket is the right choice here because it naturally supports both per-second and per-minute limits as two separate buckets, and it tolerates short bursts without rejecting legitimate traffic—important for clients with spiky workloads. For each request, both buckets are checked: if both have at least one token, both are decremented and the request is allowed; otherwise it is rejected with HTTP 429 and a Retry-After header. For the per-minute limit, a second bucket with a 60-second refill window is maintained alongside the per-second bucket, both keyed by the same API key and therefore hashed to the same Redis shard. The check-and-decrement operation must be atomic. A naive read-then-write (GET remaining tokens, check > 0, then DECR) creates a race condition where two concurrent requests both see one remaining token and both decrement, over-allowing by one. I would implement this as a single Redis Lua script that atomically checks both the per-second and per-minute buckets, decrements both if both have tokens, and returns the decision plus the remaining counts. Lua scripts execute atomically in Redis, eliminating the race window without requiring distributed locks. For the failure strategy, I would fail open (allow the request) when Redis is unreachable. The prompt states that the system must not reject valid traffic due to infrastructure issues, so failing open aligns with this requirement. To bound the risk of unlimited over-allowing during an outage, each edge node would maintain a local in-memory fallback counter—a generous fixed-window limiter (e.g., 2× the configured limit per second)—that activates when Redis is unreachable, providing a degraded but still rate-limited mode. A circuit breaker would trip after N consecutive Redis failures to avoid hammering a downed cluster, and an alert would fire so operators can restore the rate-limit store quickly.

Why:

This exercise tests the candidate's ability to integrate algorithm choice, distributed state management, concurrency handling, and failure mode strategy into a coherent design. The rubric rewards concrete technology choices, correct identification of the check-then-increment race condition with a specific atomicity mechanism, and a failure strategy justified by the stated requirements.

Design a distributed rate-limiting service that sits in front of a multi-tenant REST API platform. The platform runs behind a fleet of 50 API gateway pods spread across two data centres. Each tenant (identified by API key) may be on a different rate-limit tier (e.g., free, pro, enterprise). Requirements: limits must be enforced globally across all gateway pods, not per-node; the system must handle bursty traffic gracefully; the API must remain available even if the rate-limiting infrastructure experiences a partial outage. Discuss the algorithm you choose, how state is shared, how limits are configured per tenant, what happens when the backing store is unavailable, and how you make the system observable.#

Show answer

Use a token bucket per API key: capacity equals the configured burst limit and the refill rate equals the sustained rate (for example, capacity 100 with a refill of 10 tokens/sec). Share state through a sharded Redis Cluster, with a hash tag on the API key so each tenant's bucket lives on one shard. Each gateway pod's embedded limiter sends one Lua script per request; the script atomically refills based on elapsed time, checks the count, and decrements, so concurrent pods cannot race on check-and-decrement. Tier rules live in a rules table (free 100/min with burst 50, pro 1000/min with burst 200, enterprise 10000/min with burst 2000). Admins push rule changes over a Redis pub/sub channel, and each pod caches the active rule set locally so lookups never touch Redis. For availability, a circuit breaker with a short timeout (about 50 ms) and a health poller detect Redis trouble; when it trips, the limiter fails open to a conservative per-pod local token bucket (each pod gets roughly its share of the global limit), so the API keeps serving while abuse stays bounded. Across the two data centres, either run a Redis cluster per region with each tenant's quota split between regions, or keep one authoritative cluster and avoid synchronous cross-DC calls on the request path. For observability, return X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset headers; emit Prometheus counters for requests and rejections by tier, a Redis latency histogram, and a gauge showing fallback mode; alert on sustained rejection spikes and on any pod entering fallback.

Why:

This is a difficulty-4 senior design question: it is an open architecture problem with no single key, so it stays SHORT_ANSWER and is graded by keywords plus the sample answer. It requires integrating algorithm choice (token bucket with burst and sustained rate), shared global state (Redis with atomic Lua check-and-decrement across pods), multi-tenant tier configuration pushed to pods with local caching, failure semantics (the sample answer fails open to conservative local limits rather than rejecting traffic, because the requirement is that the API stay available during a partial outage), and observability (rate-limit headers, tier-labelled metrics, fallback alerts). The sample answer covers each of these facets, and the two-data-centre constraint is addressed explicitly.

Which of the following statements about common rate-limiting algorithms and their distributed implementations are true? Select all that apply.#

Options

Pick every one that applies.

Show answer

Token buckets allow bursts up to their capacity while still enforcing a long-term average rate. Fixed-window counters can admit up to twice the configured rate across a window boundary, and a Lua script makes a Redis token-bucket check-and-decrement atomic. The sliding-window counter is only an approximation; the sliding-window log is the exact variant.

Why:

The three correct statements are that a token bucket permits bursts up to its capacity while regulating the long-term refill rate, that wrapping the Redis token-bucket read and DECRBY in a Lua script makes the check-and-decrement atomic because Redis runs scripts in its single-threaded event loop, and that a fixed-window counter can admit up to 2x the nominal rate when a full quota is used at the end of one window and another at the start of the next. The statement that the sliding-window counter is an exact limiter that never exceeds the configured rate is false: the sliding-window counter is an approximation that weights the current and previous fixed-window counts, and only the sliding-window log (which stores every request timestamp) is exact.

Related interview questions

Job market

See system-design salaries and hiring demand from live job postings.

The other 1 question

This page shows 25 and marks what you pick. That's as far as a page can go. A free account opens the other 1 and keeps every answer. What you miss comes back until it's right: after a day, then at longer gaps.

Start with this topic

Free · the whole bank · 100 marked answers per 30 days · written feedback on the paid plan

Interview booked? Practise the job ad instead

What moved, monthly

One email a month when the bulletin comes out: what moved in the markets we track, and the new question topics we published. Confirm your address to join. Unsubscribe any time.