CMD Guide
HomeSystem DesignScalable Systems (Advanced Topics)

What Is the Difference between Rate Limiting and Throttling and Quotas

Under the hood all three are the same move: before a request is served, a counter tied to the caller is checked against a limit, and the request either decrements it or is refused. What separates them is only two knobs — how long the window is, and what happens on overflow. Rate limiting uses a short window (seconds) and rejects the overflow; throttling reacts to load by delaying or queuing it instead of dropping it; a quota uses a long window (a day, a billing month) and blocks until the period resets. Everything else on this page is a consequence of those two knobs. So the real content is the counter: the algorithm that decides, on every single request, whether a token is available.

The mechanism: a token bucket

The dominant rate-limiter algorithm (used by AWS API Gateway, Stripe, Envoy; NGINX's limit_req uses the closely related leaky bucket) is the token bucket. Picture a bucket with three parameters:

The bucket refills continuously toward C. Each request that arrives tries to remove a token: if at least one is present the request is served and a token is spent; if the bucket is empty the request is refused (429). Two numbers per client — current token count and the timestamp of the last refill — capture the entire state. The key insight: a client that has been quiet accumulates a full bucket and can fire C requests instantly (a burst), then is throttled down to the steady rate r once the bucket drains. That is exactly the "short bursts above the steady rate" behaviour the naive counter cannot express.

The whole algorithm, lazily refilled on each request (no background timer needed):

# Per client: state = {tokens: float, last: timestamp}
# CAPACITY = 10 tokens, REFILL_RATE = 5 tokens/sec

def allow(client, now):
    b = state[client]
    elapsed = now - b.last
    b.tokens = min(CAPACITY, b.tokens + elapsed * REFILL_RATE)  # accrue
    b.last = now
    if b.tokens >= 1:
        b.tokens -= 1
        return True     # 200 OK  (or, if throttling: delay then serve)
    return False        # 429 Too Many Requests

Note the refill is computed from elapsed time, not ticked by a clock — that is what keeps it O(1) memory and lock-light per client.

Worked trace: C = 10, r = 5 tokens/sec

The bucket starts full (10 tokens). Watch a burst drain it, watch requests get refused, and watch tokens re-accrue at 5/sec. Each request costs 1 token.

TimeEventRefill accrued (Δt × 5)Tokens beforeDecisionTokens after
t = 0.0sbucket starts full10.0
t = 0.0s8 requests arrive at once+0.010.0all 8 served (this is the burst)2.0
t = 0.0s3 more requests, same instant+0.02.02 served, 1 rejected (429)0.0
t = 0.5s1 request+2.52.5served1.5
t = 1.0s4 requests+2.54.0all 4 served0.0
t = 1.1s1 request+0.50.5rejected (0.5 < 1)0.5

In the first 1.1 seconds the client got 15 requests served. A dumb "5 per second" rule would have allowed roughly 5–6. The extra 9 came out of the pre-filled bucket — bounded burst, then hard convergence to the sustained 5/sec. That single behaviour is why token bucket won.

diagram
diagram

How the three differ, concretely

The distinction is not the algorithm — a token bucket can power all three — it is window length and overflow policy.

AspectRate limitingThrottlingQuota
Windowseconds – minutesreal-time, reacts to loadday / week / billing month
Overflow actionreject (429)delay / queue, serve laterblock until reset
Purposefairness, abuse & DoS defencekeep the service alive under a spikebilling tiers, capacity planning
Counter storagein-memory, resets constantlyin-memory + a queuedurable (survives reboots)
Enforced atAPI gateway / proxyapp or infra (load balancer)account / subscription
ExampleGitHub: 60 req/hr unauthenticatedMS Graph “throttling”: 429/503 + Retry-After (client-side delay)Free tier: 1,000 calls/month

Terminology honesty: vendors do not use these labels consistently — Microsoft calls Graph's 429-with-Retry-After “throttling” even though by this page's taxonomy that is rejection-style rate limiting (the delay is pushed to the client via Retry-After rather than queued server-side). In an interview, define your terms by the two knobs (window, overflow action) and the ambiguity disappears.

They compose: a real gateway runs a per-second token bucket (rate limit), spills the overflow into a short queue on a spike (throttle), and separately tallies a monthly counter per API key (quota). GitHub's 60-req/hour unauthenticated limit is a real, correct example of the rate-limiting knob.

Pitfalls

When to use which — and which algorithm

Two decisions: which overflow policy, and which counting algorithm.

Reject vs. delay vs. block

Counting algorithm

Rule of thumb: reach for a token bucket rate limiter as the default; add a durable quota for monetization; add throttling only for the specific downstream you must protect from spikes; and never ship a fixed-window counter where the boundary burst would actually hurt.

Takeaways

🎯 Drill Ladder — survive the follow-ups

L0 · a limiter is just a counter with a window and an overflow policy — reject, delay, or block

L1 · ① Concurrency — "how do you enforce ONE global limit across a fleet of 200 stateless app servers?"
Trap: "each instance runs its own token bucket in memory — simple, no network hop."
Bar: a per-node bucket means 200 nodes each honoring 100 req/s admits 20,000 req/s fleet-wide — the limit is silently multiplied by N. Fix: move the counter to a shared store (Redis) and make check-and-decrement one atomic operation (a Lua script or `INCR` + conditional `EXPIRE`), never a separate GET-then-SET — otherwise two concurrent requests both read "9 tokens left" and both proceed, blowing past capacity. Rate Limiting Algorithms — Token Bucket, Sliding Window & Distributed

L2 · ② Failure — "your central Redis counter store goes down mid-traffic. What happens to the API?"
Trap: "fail closed and reject every request until Redis is back — safest, never over-admit."
Bar: failing closed turns a rate limiter into a single point of failure that takes the whole API down on a Redis blip; instead wrap the Redis call in a circuit breaker and fail open onto a conservative local token bucket per node, accepting a brief window of looser (not zero) enforcement until the store recovers. This is exactly the sticky-local-vs-global tension: global is correct but fragile, local is available but imprecise, so production systems degrade from one to the other rather than picking once. System Design Trade-offs II — Retry Storms, Rate-Limiter Coordination

L3 · ③ Scale — "10M req/sec across 5,000 nodes; a single Redis INCR per request won't keep up. Now what?"
Trap: "vertically scale Redis / add more replicas and keep doing INCR per request."
Bar: shard the counter keyspace across many Redis instances (hash by client key, same trick as consistent hashing for cache clusters), or trade exactness for throughput: each node keeps a local counter and syncs a delta to the central store every 50–100 ms, accepting a bounded over-admission equal to (N nodes × sync interval × local rate) instead of a network round-trip per request. Drill: memory for a rate limiter

L4 · ④ Time/Lifecycle — "two regions enforcing the same per-minute fixed window have clocks 300ms apart. What breaks?"
Trap: "NTP-sync the clocks tighter and the fixed window is fine."
Bar: clock skew is irrelevant to the real bug — a fixed window resets its counter at a wall-clock boundary regardless of skew, so a client firing the limit at :59.9 and again at :00.1 gets 2× the intended rate in 200ms even with perfect clocks; skew only makes cross-region enforcement additionally disagree about which window a request even falls in. Fix both problems the same way: anchor window boundaries at the central store's clock (not each node's), and use a sliding-window log or token bucket, which have no reset seam to exploit. Multi-Region Rate Limiting

L5 · ⑤ Adversary/Edge — "an attacker fans a burst out across 10,000 rotating IPs and API keys to dodge your per-key limiter. Now?"
Trap: "tighten the per-key limit further and add more IP-based rules."
Bar: a purely per-key/per-IP limiter cannot see a distributed attack because each identity individually looks under-limit — the fix is a second, coarser limiter keyed on a shared resource (a hot endpoint, a tenant, a shard) that catches aggregate abuse the per-identity view misses, plus a progressive-cost response (CAPTCHA/challenge) instead of a flat 429 once anomaly signals fire. This is the same asymmetry as hot-key skew: the attacker chooses the key distribution, you don't, so the defense has to key on something the attacker doesn't control. Rate-Limit Hot Keys

The floor keeps dropping: at L6+ they stack failure onto scale — "the shard holding the hot tenant's counter is also the one failing over during the botnet burst; walk me through what each request sees, in order" — there is no clean answer, only a graceful-degradation story with numbers attached.

Self-locate: died at L1 → mid-level; L4+ → staff signal.

Facing any new concept? Hit it with the six: concurrent? failing? at 100×? over time? adversarial? worth the cost? — that's the interviewer's whole playbook.


Re-authored and deepened for this guide. Sources: the token-bucket and leaky-bucket algorithms as documented in the AWS API Gateway usage-plan / throttle-and-burst model and NGINX/Envoy rate-limiting docs; the fixed-window boundary-burst problem and sliding-window counter as described in Alex Xu, System Design Interview (Ch. 4, "Design a Rate Limiter"); GitHub REST API rate-limit documentation (60 req/hr unauthenticated); Microsoft Graph throttling guidance (429/503 with Retry-After); and DesignGurus "What is rate limiting?". Worked trace values are illustrative and hand-verified.

🤖 Don't fully get this? Learn it with Claude

Stuck on What Is the Difference between Rate Limiting and Throttling and Quotas? Open Claude, copy a block below, and it'll teach you this exact concept — visually and interactively.

🎨 Explain it visually

Build the mental picture, not memorization.

I just read a lesson on **What Is the Difference between Rate Limiting and Throttling and Quotas** (System Design) and want to truly understand it. Explain What Is the Difference between Rate Limiting and Throttling and Quotas from first principles using ONE vivid real-world analogy and a visual mental model — draw it as ASCII art or a clear step-by-step diagram — with a concrete example using real numbers. Then ask me one question to check I got the mental picture, and wait for my reply. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
🤔 Walk me through it (interactive)

Socratic — adapts to where you're stuck.

Teach me **What Is the Difference between Rate Limiting and Throttling and Quotas** interactively. Ask me ONE guiding question at a time, wait for my answer, and adapt to my confusion — build the idea with me step by step instead of explaining it all at once. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
🧪 Quiz me & fix my gaps

Active recall exposes what you missed.

Quiz me on **What Is the Difference between Rate Limiting and Throttling and Quotas** with 5 questions, easy to tricky, ONE at a time. Tell me if each answer is right; at the end, explain clearly what I got wrong and why. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
🧠 Make it stick

Intuition + hook + flashcards for long-term memory.

Help me remember **What Is the Difference between Rate Limiting and Throttling and Quotas** for the long term: give the one-sentence intuition, a memorable hook/mnemonic, a tiny worked example, and 3 active-recall flashcards (Q -> A). If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.

📝 My notes