Rate Limit per User ID in Python with a Dict Mock

Implements a simple sliding window rate limiter using a defaultdict of timestamps per user ID, blocking requests that exceed a max count within a time window.

Easy Python 3.9+ Aug 9, 2026 Reliability & rate limiting 14 views 0 copies

Python code

38 lines
Python 3.9+
import time
from collections import defaultdict


class RateLimiter:
    def __init__(self, max_requests, window_seconds):
        self.max_requests = max_requests
        self.window_seconds = window_seconds
        self.user_timestamps = defaultdict(list)

    def allow_request(self, user_id):
        now = time.time()
        timestamps = self.user_timestamps[user_id]

        while timestamps and timestamps[0] <= now - self.window_seconds:
            timestamps.pop(0)

        if len(timestamps) >= self.max_requests:
            return False

        timestamps.append(now)
        return True


if __name__ == "__main__":
    limiter = RateLimiter(max_requests=3, window_seconds=10)

    test_requests = [
        ("alice", True),
        ("alice", True),
        ("alice", True),
        ("alice", False),  # 4th request should be blocked
        ("bob", True),     # different user not affected
    ]

    for user_id, expected in test_requests:
        result = limiter.allow_request(user_id)
        print(f"user={user_id}, allowed={result}, expected={expected}")

Output

stdout
user=alice, allowed=True, expected=True
user=alice, allowed=True, expected=True
user=alice, allowed=True, expected=True
user=alice, allowed=False, expected=False
user=bob, allowed=True, expected=True

How it works

The defaultdict(list) stores a list of request timestamps for each user. time.time() returns the current epoch, and the while loop removes expired timestamps older than the window. The length of the remaining list determines if the request is allowed. This is a sliding window approach, not a fixed bucket, so it adjusts naturally as time passes. The code is intentionally simple for a mock, lacking persistence or thread safety.

Common mistakes

  • Forgetting to remove old timestamps, causing false blocks.
  • Using a global list instead of per-user mapping, blocking all users together.
  • Assuming `time.time()` is monotonic; use `time.monotonic()` for production.
  • Not handling key errors when using a plain dict instead of defaultdict.

Variations

  1. Use `collections.deque` with `popleft()` for O(1) removal instead of `pop(0)`.
  2. Use a fixed window counter with reset instead of sliding window for simpler logic.

Real-world use cases

  • Limiting API calls from a single user to prevent abuse or overuse.
  • Throttling login attempts per account to slow down brute-force attacks.
  • Capping the number of file uploads per customer within a time period.

Sponsored

Run this sample

Open the browser IDE to tweak the example and see results without installing anything.

Open editor

More from Reliability & rate limiting

Related tutorials and quizzes for this topic.