How I Built A Sliding Window Rate Limiter in Redis

Our video API used to crash every evening at 8pm UTC.

It was not real traffic. A few scrapers found our trending endpoint and hammered it. Our database queries piled up. Real users saw loading spinners instead of videos.

We needed rate limiting. Most simple methods made the problem worse.

The problem with Fixed Window counters: Most people start with fixed windows. You count requests in a set time block. If your limit is 100 per minute, a user can send 100 requests at 11:59:59 and another 100 at 12:00:00. That is 200 requests in one second. Scrapers exploit these boundaries to bypass your limits.

The solution: Sliding Window. A sliding window counts requests in the last N seconds relative to right now. There is no boundary to exploit.

We use two different methods based on our needs:

  1. Sliding Window Log (For precision) We use Redis Sorted Sets (ZSET) for authenticated users. We store a timestamp for every request.
  • We use ZREMRANGEBYSCORE to drop old entries.
  • We use ZCARD to count current requests.
  • We use ZADD to record new ones.

To prevent race conditions, we run this logic in a single Lua script. This makes the operation atomic. One script handles everything in one trip to Redis. This prevents two requests from both thinking they are under the limit at the same time.

  1. Sliding Window Counter (For scale) For anonymous traffic, we have millions of keys. We cannot store every timestamp. Instead, we use a counter that weights the current and previous window. It uses much less memory and is highly efficient.

Key lessons from production:

  • Use Lua scripts. This ensures your check and your write happen as one single action.
  • Emit standard headers. Always send X-RateLimit-Remaining and Retry-After. This helps well-behaved clients back off automatically.
  • Fail open. If Redis goes down, allow the request. An overloaded API is better than a completely dead API.
  • Identify clients correctly. Use API keys for users and hashed IPs for guests.
  • Match limits to endpoints. A heavy search endpoint needs tighter limits than a simple list endpoint.

The result was immediate. The evening spikes disappeared. Our database performance returned to normal.

Source: https://dev.to/ahmet_gedik778845/how-i-built-a-sliding-window-rate-limiter-for-our-video-api-in-redis-4k1c