HLDRate Limiting35 min read

Rate Limiting in System Design

Learn how scalable systems protect APIs from abuse, traffic spikes, bots and expensive workloads using fixed windows, sliding windows, token buckets, leaky buckets, Redis and distributed rate limiting.

Fundamentals

What is Rate Limiting?

Rate limiting controls how much traffic a client or workload is allowed to send during a defined period or concurrent execution limit.

Client

1000 requests

Rate Limiter

Allowed: 100 req/min

Allowed requests

Application

Protected

Rate limiting is a protection mechanism

It protects the service, databases, downstream APIs and other limited resources from receiving more work than they can safely process.

Why?

Why Do We Need Rate Limiting?

Traffic can become dangerous even when every request is technically valid.

Prevent Abuse

Stop clients from sending unlimited API requests.

Protect Infrastructure

Prevent traffic from overwhelming databases, queues and downstream services.

Control Cost

Protect expensive resources such as AI inference, third-party APIs and payment providers.

Fairness

Prevent one tenant or user from consuming all available capacity.

DDoS Mitigation Layer

Rate limiting can be one part of a larger abuse and traffic-protection strategy.

Business Quotas

Different plans can receive different API limits.

Architecture

Where Should the Rate Limiter Live?

Rate limiting can be enforced at several layers, and large systems often combine them.

Internet Traffic

CDN / Edge / WAF

Coarse IP / bot limits

API Gateway

User / API key / route quotas

Application

Business-specific limits

DB / Downstream Services

Protected resources

Layer your limits

Edge-level limits can reject obvious abuse early. Gateway limits can enforce user or API quotas. The application can still enforce domain rules such as “one OTP every 30 seconds.”

Identity

What Can We Rate Limit By?

The limiter key determines who shares the quota.

IP Address

Useful for unauthenticated traffic and basic abuse control.

User ID

Good for authenticated user-level quotas.

API Key

Common for developer APIs.

Tenant / Organization

Protect multi-tenant systems and enforce plan limits.

Endpoint

Expensive endpoints can have stricter limits.

Operation

OTP, password reset, AI generation or export can each have separate quotas.

Composite Rate-Limit Key
rate-limit:
tenant:company_123:
user:user_42:
route:/api/payments

Algorithm 1

Fixed Window Counter

Split time into fixed intervals and maintain a request counter for each client and interval.

Example
Limit:

100 requests / minute

Window 1:
10:00:00 - 10:00:59

Window 2:
10:01:00 - 10:01:59

Boundary Problem

10:00:59
-> 100 requests accepted

10:01:00
-> another 100 requests accepted

Result:

200 requests may arrive
within approximately 1 second.

Why use it?

It is simple, uses little memory and can be implemented efficiently with an atomic counter plus TTL.

Algorithm 2

Sliding Window Log

Store individual request timestamps and remove entries older than the rolling window.

Current time:
10:01:20

Window:
last 60 seconds

Stored timestamps:

10:00:25
10:00:40
10:00:55
10:01:10
10:01:18

Remove anything older than:
10:00:20

Count remaining timestamps.

Benefit

Very accurate rolling-window enforcement.

Cost

Potentially store one timestamp for every request.

Algorithm 3

Sliding Window Counter

Approximate a rolling window using weighted counts from the current and previous fixed windows.

Concept
Previous window count = 80

Current window count = 20

Current window is 25% complete.

Approximation:

80 * 0.75 + 20

= 80 requests

Good compromise

Sliding counters avoid the large memory overhead of storing every request timestamp while reducing the sharp fixed-window boundary problem.

Algorithm 4

Token Bucket

Tokens enter a bucket at a configured rate. A request is accepted only when enough tokens are available.

Refill

+10 tokens/sec

Token Bucket

Capacity = 100

Request consumes 1 token

API

Request allowed

Why Token Bucket is Useful

If the bucket has accumulated tokens, the client can send a short burst. But over time, traffic is still restricted by the token refill rate.

Token Bucket Pseudocode
function allowRequest(bucket) {

    now = currentTime()

    elapsed =
        now - bucket.lastRefill

    newTokens =
        elapsed * bucket.refillRate

    bucket.tokens =
        min(
            bucket.capacity,
            bucket.tokens + newTokens
        )

    bucket.lastRefill = now

    if (bucket.tokens < 1) {
        return false
    }

    bucket.tokens -= 1

    return true
}

Algorithm 5

Leaky Bucket

Requests enter a bucket or queue while work leaves at a controlled rate.

Burst Traffic

1000 req/sec

Bucket / Queue

Finite capacity

Constant leak rate

Backend

100 req/sec

Token bucket vs leaky bucket

Token bucket usually allows controlled bursts. Leaky bucket is useful when the output should be smoother and more constant.

Expensive Operations

Concurrency Limiting

Requests per second alone may not protect a resource when each request has a very different execution time.

Maximum concurrent AI requests:

20

Current in-flight:

20

New request:

REJECT / QUEUE

RPS Limit

Controls how many requests begin during a time interval.

Concurrency Limit

Controls how many operations can remain active at once.

Selection

Which Rate Limiting Algorithm Should You Use?

The correct choice depends on accuracy, burst tolerance, state size and complexity.

1

Fixed Window Counter

Simple

Count requests inside fixed time buckets such as 10:00:00–10:00:59.

Good For

Simple APIs where exact fairness around window boundaries is not critical.

Benefit

Very simple, cheap and easy to implement.

Trade-off

Allows burst traffic around window boundaries.

2

Sliding Window Log

Accurate

Store timestamps of recent requests and count only timestamps inside the current rolling interval.

Good For

Low-to-medium traffic systems where precise rolling-window limits matter.

Benefit

Very accurate rolling limit.

Trade-off

Memory cost grows with request volume.

3

Sliding Window Counter

Balanced

Approximate a sliding window by combining the current and previous fixed-window counts.

Good For

APIs needing smoother boundaries without storing every request timestamp.

Benefit

Good accuracy with much lower memory than a request log.

Trade-off

Still an approximation.

4

Token Bucket

Bursts

Tokens refill at a constant rate. Every request consumes a token.

Good For

APIs that want to allow controlled bursts while enforcing a long-term average rate.

Benefit

Allows bursts without giving unlimited traffic.

Trade-off

Needs atomic token refill and consumption logic.

5

Leaky Bucket

Smooth

Incoming work enters a bucket and leaves at a controlled constant rate.

Good For

Protecting systems that need smooth downstream request flow.

Benefit

Turns bursts into predictable output traffic.

Trade-off

Queued requests may experience latency or be dropped when the bucket fills.

6

Concurrency Limiter

In Flight

Limits how many expensive operations can execute at the same time.

Good For

Expensive DB queries, AI inference, uploads, report generation and slow downstream calls.

Benefit

Protects limited resources even when requests have variable durations.

Trade-off

Does not directly control total requests per second.

Distributed Systems

Local vs Distributed Rate Limiting

A limiter inside one application server is easy, but multiple application instances create a coordination problem.

Broken Local Design

User

Limit = 100/min

API 1

100 allowed

API 2

100 allowed

API 3

100 allowed

Effective limit may become 300/min.

Shared State

API 1

API 2

API 3

Shared counter

Redis

user:42 => 76 / 100

Implementation

Redis-Based Rate Limiting

Redis is commonly useful because application instances can share fast counters and expirations.

Fixed Window
key =
  "rate:user:42:202609291546"

count =
  INCR key

if count == 1:
    EXPIRE key 60

if count > 100:
    rejectRequest()

INCR and EXPIRE must be handled safely

If your application increments a brand-new key and crashes before setting its TTL, the counter may remain indefinitely. Use an atomic operation, transaction or script suited to your design.

Concurrency

Why Atomicity Matters

Two requests can reach the limiter at exactly the same time.

Incorrect Read-Modify-Write
Request A:

GET counter -> 99

Request B:

GET counter -> 99

A thinks:
99 < 100 -> allow

B thinks:
99 < 100 -> allow

Both update counter.

Limit is violated.

Use atomic Redis operations, scripts or another shared state mechanism that performs check + update as one logical operation.

Atomic Lua-Style Concept
local current =
    redis.call(
        "GET",
        KEYS[1]
    )

if current and
   tonumber(current) >=
   tonumber(ARGV[1])
then
    return 0
end

local value =
    redis.call(
        "INCR",
        KEYS[1]
    )

if value == 1 then
    redis.call(
        "EXPIRE",
        KEYS[1],
        ARGV[2]
    )
end

return 1

API Behavior

HTTP 429 & Rate Limit Responses

Clients should receive enough information to understand that the request was throttled and, where appropriate, when to retry.

Example Response
HTTP/1.1 429 Too Many Requests

Retry-After: 30

Content-Type: application/json

{
  "success": false,
  "message": "Rate limit exceeded",
  "retryAfterSeconds": 30
}

Do not make clients retry immediately

If thousands of clients receive 429 and immediately retry in a tight loop, the limiter itself can become part of a retry storm. Clients should use backoff and respect retry guidance.

Traffic Spikes

Handling Burst Traffic

Rate limits need to distinguish between acceptable short bursts and sustained overload.

Normal Traffic

100 req/sec

Flash event

10,000 req/sec

Sudden burst

Token Bucket

Allow bounded burst

Backend

Protected

Burst Capacity

Allows temporary traffic above the steady refill rate.

Sustained Rate

Controls long-term throughput.

Queueing

Some workloads can wait instead of being rejected.

Load Shedding

Optional requests may be rejected under extreme pressure.

Scale

Rate Limiter Hot-Key Problem

A single very popular identity can create enormous traffic against one Redis key.

R1

R2

R3

Millions

Same API key

rate:api-key:popular-key

Hot Redis key

Local + Global Limits

Enforce small local limits first and use global storage for authoritative quota.

Sharded Counter Design

For some approximate limits, distribute counter work across multiple keys.

Edge Enforcement

Reject obvious traffic before it reaches central infrastructure.

Plan-Specific Architecture

Very high-volume tenants may need dedicated quota state.

Global Systems

Multi-Region Rate Limiting

A global limit becomes difficult when traffic is handled independently in several geographical regions.

Global User

Limit = 1000/min worldwide

India

400

US

400

Europe

400

Independent regional limits could allow 1200 instead of 1000.

Design Choices

Central Global Counter

More accurate globally, but adds latency and cross-region dependency.

Regional Quota Allocation

Split 1000 global tokens among regions. Fast locally but unused quota can become stranded.

Eventually Synchronized Counters

Lower latency but may temporarily exceed exact global limit.

Hybrid

Enforce strict local ceilings while asynchronously coordinating global quota.

Ask whether the limit must be exact

An abuse-prevention limit can often tolerate small approximation. Billing or expensive quota enforcement may require stronger correctness.

Case Study

BookMyShow-Style Flash Sale Rate Limiting

Imagine tickets for a blockbuster movie opening at exactly 10:00 AM.

Interview-style architecture

This is a conceptual ticket-booking design for learning. It is not a claim about BookMyShow's private implementation.

500K+ Users

Tickets open

CDN / Edge

Static assets + coarse abuse controls

Rate Limiter

IP + user + booking limits

Allowed traffic

Load Balancer

Booking 1

Booking 2

Booking N

Redis

Seat holds + quota state

Booking DB

Durable booking

Example Limits

Per IP

Reduce obvious bot floods.

Per User

Prevent one account from creating excessive booking attempts.

Per Show

Protect a highly popular show from overwhelming downstream booking state.

OTP

Prevent unlimited OTP generation.

Payment Attempts

Protect payment providers and reduce repeated abuse.

Search

Allow higher throughput because search is less correctness-sensitive than booking.

Layered Flash-Sale Protection

1

Edge filters obvious abusive traffic.

2

Gateway enforces IP, user and API-level quotas.

3

Booking service applies stricter business limits.

4

Token bucket allows a controlled burst.

5

Load balancer spreads accepted traffic.

6

Redis handles temporary distributed quota state.

7

Seat locking prevents concurrent booking conflicts.

8

Database protects final booking correctness.

Rate limiting does not solve seat concurrency

Rate limiting reduces excessive traffic. Redis seat holds, database locking, transactions and idempotency solve booking correctness.

Production Engineering

Rate Limiter Failure Scenarios

A limiter is part of the critical request path, so its own failures need explicit handling.

Redis Is Down

Decide whether the endpoint fails open or fails closed. The correct behavior depends on the risk of allowing traffic versus rejecting valid users.

Race Condition

Use atomic state changes so concurrent requests cannot exceed the quota.

Clock Skew

Distributed algorithms relying on timestamps need a consistent time strategy.

Hot Key

A highly active user, API key or tenant may overload one quota key.

Limiter Adds Latency

Use low-latency storage, local optimization or hierarchical limits where appropriate.

Configuration Error

A limit of 10/min accidentally deployed instead of 10,000/min can cause an outage.

Regional Partition

Global quota enforcement may become inconsistent when regions cannot communicate.

Retry Storm

Rejected clients must use backoff instead of immediately retrying.

Fail Open vs Fail Closed

Fail Open

If limiter storage fails, allow the request.

Useful when availability is more important than strict quota enforcement.

Fail Closed

If limiter storage fails, reject the request.

Useful when the protected operation is very expensive or abuse-sensitive.

Observability

Rate Limiter Metrics

Monitoring tells you whether limits are protecting the system or simply blocking legitimate users.

Allowed Requests
Rejected Requests
429 Rate
Limiter Latency
Redis Latency
Redis Errors
Hot Keys
Top Limited Users
Top Limited IPs
Limit Utilization
Fail-Open Count
Fail-Closed Count
Burst Rate
Quota Exhaustion
Regional Drift
Config Changes

Interview Preparation

Rate Limiting Interview Questions

These are the concepts you should be able to explain with trade-offs.

1

Why do we need rate limiting?

2

Where should a rate limiter be placed?

3

Rate limiting vs throttling — how do you think about them?

4

What can you rate limit by?

5

Explain Fixed Window Counter.

6

What is the fixed-window boundary problem?

7

Explain Sliding Window Log.

8

Why is Sliding Window Log memory intensive?

9

Explain Sliding Window Counter.

10

Explain Token Bucket.

11

Why does Token Bucket allow bursts?

12

Explain Leaky Bucket.

13

Token Bucket vs Leaky Bucket?

14

What is concurrency limiting?

15

RPS limiting vs concurrency limiting?

16

How would you rate limit across multiple application servers?

17

Why is Redis commonly used?

18

How do race conditions break a rate limiter?

19

How can Lua scripts help with Redis rate limiting?

20

What should happen after HTTP 429?

21

What is Retry-After?

22

How do you prevent client retry storms?

23

How do you support different limits for free and premium users?

24

How would you rate limit by API key?

25

How would you rate limit OTP generation?

26

How do you handle rate-limiter hot keys?

27

How would you implement a global multi-region rate limit?

28

Exact vs approximate rate limiting?

29

When would you fail open?

30

When would you fail closed?

31

How would you handle Redis failure?

32

How would you protect a flash sale?

33

How would you rate limit a BookMyShow-style booking API?

34

How would you protect an expensive AI inference endpoint?

35

What metrics would you monitor?

Quick Revision

Frequently Asked Questions

Short answers to common rate-limiting questions.

What is rate limiting in system design?

Rate limiting controls how much traffic a client, user, API key, tenant, route or other identity can send during a defined interval or concurrency limit.

Which rate limiting algorithm is most commonly used?

There is no universal best algorithm. Token bucket is useful when controlled bursts are allowed, sliding windows provide smoother rolling limits, and fixed windows are attractive when simplicity matters.

Why is Redis used for distributed rate limiting?

Redis provides fast shared state, TTL support and atomic operations, making it useful when multiple application instances need to enforce one shared limit.

What HTTP status code should a rate limiter return?

HTTP 429 Too Many Requests is commonly returned when the client exceeds an applicable request limit.

Should rate limits be based only on IP address?

Usually not. IP limits are useful for unauthenticated traffic, but authenticated systems often also limit by user, API key, tenant, route or operation because many users can share an IP address.

Mastering System Design — HLD

Learn how rate limiting fits into complete system designs.

Combine rate limiting with caching, databases, load balancing, queues, distributed locking, concurrency and failure handling in complete HLD interview designs.

Rate LimiterRedisCachingQueuesDatabasesBookMyShowScalingConcurrency

Continue Learning