How to implement rate limiting in Python

Build a simple sliding-window rate limiter in Python that enforces a max number of calls per time period and formats data with timestamps.

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

Python code

48 lines
Python 3.9+
import time

class RateLimiter:
    def __init__(self, max_calls, period):
        self.max_calls = max_calls
        self.period = period
        self.calls = []
    
    def allow(self):
        now = time.time()
        # Remove calls older than the period window
        self.calls = [t for t in self.calls if now - t < self.period]
        if len(self.calls) < self.max_calls:
            self.calls.append(now)
            return True
        return False
    
    def wait_time(self):
        if self.allow():
            return 0
        now = time.time()
        self.calls = [t for t in self.calls if now - t < self.period]
        if not self.calls:
            return 0
        return round(self.period - (now - min(self.calls)), 2)


def format_data(data, limiter):
    """
    Format a list of items with a timestamp,
    respecting the rate limit.
    """
    results = []
    for item in data:
        if limiter.allow():
            results.append(f"[{time.strftime('%H:%M:%S')}] {item}")
        else:
            results.append(f"[Rate limited] {item} (wait {limiter.wait_time()}s)")
    return "\n".join(results)


if __name__ == "__main__":
    # Allow 3 calls per 5-second window
    limiter = RateLimiter(max_calls=3, period=5)
    sample_data = ["apple", "banana", "cherry", "date", "elderberry", "fig"]
    
    output = format_data(sample_data, limiter)
    print(output)

Output

stdout
[10:15:30] apple
[10:15:30] banana
[10:15:30] cherry
[Rate limited] date (wait 4.5s)
[Rate limited] elderberry (wait 4.5s)
[Rate limited] fig (wait 4.5s)

How it works

The RateLimiter tracks timestamps of allowed calls in a list. The allow method filters out timestamps older than the period, then checks if the remaining count is below the maximum. When over the limit, wait_time calculates how long until the oldest call in the window expires. The format_data function applies this limiter to each item, appending a rate-limited message with the wait time when blocked. This sliding-window approach is simple and works well for single-process applications.

Common mistakes

  • Calling wait_time() twice (it calls allow() internally, which adds a new call)
  • Assuming thread safety when multiple threads share one limiter
  • Forgetting that time.time() measures wall-clock time, not process time

Variations

  1. Use a fixed-window counter instead of sliding window for simpler logic
  2. Implement with collections.deque for O(1) pops from the front

Real-world use cases

  • Throttling API client requests to respect third-party rate limits.
  • Limiting login attempts or verification code requests per user window.
  • Controlling batch job execution frequency in a background scheduler.

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.