Skip to content

Rate Limiting

A rate limiter caps how many requests a client can make in a given time window. Redis makes this easy with two atomic commands: INCR to count requests and EXPIRE to bound the window.

For each incoming request, increment a counter keyed by user and time window. On the very first hit — when the counter equals 1 — set an expiry on that key. If the counter exceeds the limit, reject the request.

flowchart TD
  A["Incoming request"] --> B["count = INCR rate:${userId}:window"]
  B --> C{"count == 1?"}
  C -->|"Yes (first hit)"| D["EXPIRE rate:${userId}:window 60"]
  C -->|"No"| E{"count > 100?"}
  D --> E
  E -->|"Yes"| F["Reject (HTTP 429)"]
  E -->|"No"| G["Allow request"]
Fixed-window rate limiter: INCR the counter, set EXPIRE on the first hit, reject once the limit is exceeded

The key encodes the window so each new window gets a fresh counter automatically.

127.0.0.1:6379> INCR rate:user:42:window1
(integer) 1
127.0.0.1:6379> EXPIRE rate:user:42:window1 60
(integer) 1
127.0.0.1:6379> INCR rate:user:42:window1
(integer) 2
127.0.0.1:6379> TTL rate:user:42:window1
(integer) 58

The first INCR returns 1, so we set the expiry immediately. Subsequent increments count against the same window, and TTL confirms the key will expire after the window closes.

INCR rate:user:42:window1
EXPIRE rate:user:42:window1 60
INCR rate:user:42:window1
TTL rate:user:42:window1

INCR + EXPIRE gives a fixed window: all requests in the same 60-second bucket share one counter. A burst at the very end of one window and the very start of the next can effectively double the allowed rate at the boundary.

For a true sliding window you would use a sorted set: store each request timestamp with ZADD, trim old entries with ZREMRANGEBYSCORE, then count live entries with ZCARD. This is more precise but slightly more expensive per request.

Why do we call EXPIRE only when count == 1?
What HTTP status code should a rate-limited response return?
INCR is atomic. What does this mean for concurrent requests?
Which data structure enables a true sliding-window rate limiter?