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}"))
This code implements a practical rate limiter using Python’s decorator pattern. Here’s why it’s useful and how it works:
Problem Solved: Many web APIs enforce rate limits (e.g., 5 requests per 10 seconds). This decorator helps stay within these limits automatically.
@rate_limiter(max_calls=X, period=Y)
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.