How to implement rate limiting per API key in Python

A simple sliding-window rate limiter that tracks request timestamps per API key and rejects requests exceeding the configured limit.

Medium Python 3.9+ Aug 9, 2026 Reliability & rate limiting 13 views 0 copies

Python code

26 lines
Python 3.9+
import time

API_RATE_LIMITS = {"api_key_1": 5, "api_key_2": 3}  # max requests per window
WINDOW_SECONDS = 10

class RateLimiter:
    def __init__(self, limits, window):
        self.limits = limits
        self.window = window
        self.requests = {key: [] for key in limits}

    def allow(self, api_key):
        now = time.time()
        timestamps = self.requests.setdefault(api_key, [])
        # Remove old timestamps outside the window
        timestamps[:] = [t for t in timestamps if now - t < self.window]
        if len(timestamps) >= self.limits.get(api_key, 0):
            return False
        timestamps.append(now)
        return True

if __name__ == "__main__":
    limiter = RateLimiter(API_RATE_LIMITS, WINDOW_SECONDS)
    for key in ["api_key_1", "api_key_2", "unknown_key"]:
        results = [limiter.allow(key) for _ in range(6)]
        print(f"{key}: {results}")

Output

stdout
api_key_1: [True, True, True, True, True, False]
api_key_2: [True, True, True, False, False, False]
unknown_key: [True, True, True, True, True, True]

How it works

The RateLimiter keeps a list of timestamps for each API key in self.requests. On each allow call, it prunes timestamps older than the window using a list comprehension slice assignment, then checks if the count is at or above the limit. If not, it appends the current time and returns True. For unknown keys, setdefault creates an empty list and limits.get returns a default of 0, so all requests are allowed if the key is not in the limits dict. This sliding window avoids burst spikes by enforcing a rolling time window.

Common mistakes

  • Forgetting to prune old timestamps, leading to premature blocking
  • Using `limits[key]` without `.get()` which raises KeyError for unknown keys
  • Not making timestamps thread-safe when used in concurrent environments

Variations

  1. Use `collections.deque` for efficient timestamp management
  2. Implement a fixed-window counter using a dictionary of counts and a window reset time

Real-world use cases

  • Enforce per-customer API call quotas in a microservice to prevent abuse or overuse.
  • Limit login attempt rates per user in a web application to slow brute-force attacks.
  • Control how often a background job calls an external vendor API to stay within paid tiers.

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.