Python Snippets

Simple Yet Effective Web API Rate Limiter Decorator

This snippet demonstrates how to implement a rate limiter for Python functions, particularly useful when making API calls to services with strict rate limits. The decorator uses a sliding window algorithm to ensure fair and accurate rate limiting.

import time
from functools import wraps
from collections import deque

def rate_limiter(max_calls, period):
    """
    Decorator to limit how often a function can be called.
    
    Args:
        max_calls: Maximum allowed calls within the time period
        period: Time period in seconds
    """
    def decorator(func):
        calls = deque()
        
        @wraps(func)
        def wrapper(*args, **kwargs):
            now = time.time()
            
            # Remove calls older than the current period
            while calls and now - calls[0] > period:
                calls.popleft()
                
            if len(calls) >= max_calls:
                oldest = calls[0]
                elapsed = now - oldest
                if elapsed < period:
                    time.sleep(period - elapsed)
                    now = time.time()
                    # After sleeping, remove any expired calls again
                    while calls and now - calls[0] > period:
                        calls.popleft()
            
            calls.append(now)
            return func(*args, **kwargs)
        return wrapper
    return decorator

# Example usage
@rate_limiter(max_calls=5, period=10)
def call_api(endpoint):
    """Simulate an API call"""
    print(f"Calling {endpoint} at {time.time()}")
    return "success"

# Test the rate limiter
for i in range(8):
    print(call_api(f"/data/{i}"))

Explanation

This code implements a practical rate limiter using Python’s decorator pattern. Here’s why it’s useful and how it works:

  1. Problem Solved: Many web APIs enforce rate limits (e.g., 5 requests per 10 seconds). This decorator helps stay within these limits automatically.

  2. Key Features:
    • Uses a sliding window algorithm (more accurate than simple time buckets)
    • Thread-safe for single-threaded applications
    • Automatically sleeps to comply with limits
    • Maintains exact timing requirements
  3. Implementation Details:
    • Stores timestamps of recent calls in a deque
    • Before each call, removes outdated timestamps (> period seconds old)
    • If at limit, calculates needed sleep time to comply
    • Adds current call timestamp after execution
  4. Usage:
    • Apply the decorator to any function with @rate_limiter(max_calls=X, period=Y)
    • The example shows it limiting to 5 calls every 10 seconds
  5. When to Use:
    • When working with rate-limited APIs
    • To prevent your own services from being overwhelmed
    • In web scraping to avoid being blocked

To test it, simply run the example usage portion. You’ll see the first 5 calls execute immediately, then subsequent calls will be delayed to maintain the rate limit.