Rate Limiting Utilities
limits is a Python library for rate limiting via multiple strategies with commonly used storage backends such as Redis, Memcached, MongoDB, and Valkey. It provides identical APIs for use in synchronous and asynchronous codebases, enabling robust control over request rates. The library maintains an active development status with regular releases, with version 5.8.0 being the latest stable release at the time of verification.
Warnings
- breaking Version 5.0.0 introduced several backward-incompatible changes, including dropping support for the `Fixed Window with Elastic Expiry` strategy and the `etcd` storage backend. Additionally, the default implementation for `async+memcached` was changed from `emcache` to `memcachio`.
- gotcha Using `MemoryStorage` (the default in basic examples) in a multi-process or distributed environment will not provide a global rate limit. Each process will have its own independent rate counter, which can lead to limits being exceeded across the entire system.
- gotcha When migrating from `limits` versions prior to 5.6.0, note that project metadata moved to `pyproject.toml` and the project now uses `hatch` for package builds. While this primarily affects contributors, it's worth noting for build system interactions.
- deprecated Older versions of `limits` (e.g., prior to v2.7.0 and v2.4.0) had specific requirements for `redis` and `coredis` versions. While current versions relax these, always verify compatibility.
Install
-
pip install limits -
pip install 'limits[redis]' # For Redis storage -
pip install 'limits[memcached]' # For Memcached storage -
pip install 'limits[mongodb]' # For MongoDB storage -
pip install 'limits[async-redis]' # For Async Redis storage -
pip install 'limits[all]' # Installs all optional dependencies
Imports
- strategies
from limits import strategies
- storage
from limits import storage
- RateLimitItem
from limits import RateLimitItem
- RateLimitExceeded
from limits.errors import RateLimitExceeded
- parse
from limits import parse
- parse_many
from limits import parse_many
Quickstart
from limits import parse, strategies, storage, RateLimitExceeded
import time
import os
# 1. Initialize a storage backend
# For simplicity, using in-memory storage. For production, use Redis/Memcached.
# A Redis example:
# REDIS_URL = os.environ.get('REDIS_URL', 'redis://localhost:6379')
# store = storage.RedisStorage(REDIS_URL)
store = storage.MemoryStorage()
# 2. Define a rate limit (e.g., 5 requests per minute)
# parse() converts a string like '5/minute' into a RateLimitItem
rate_limit_string = '5/minute'
rate_limit = parse(rate_limit_string)
# 3. Initialize a rate limiter strategy
# FixedWindowRateLimiter is a common strategy
limiter = strategies.FixedWindowRateLimiter(store)
def do_limited_action(user_id):
try:
# 4. Test the limit for a specific identifier (e.g., user_id)
if limiter.test(rate_limit, user_id):
# 5. Consume the limit (i.e., record a hit)
limiter.hit(rate_limit, user_id)
print(f"[{user_id}] Action performed at {time.strftime('%H:%M:%S')}. Remaining: {limiter.get_remaining(rate_limit, user_id)}")
else:
raise RateLimitExceeded(rate_limit)
except RateLimitExceeded as e:
# Query available capacity and reset time
remaining = limiter.get_remaining(e.limit, user_id)
reset_at = limiter.get_reset_time(e.limit, user_id)
print(f"[{user_id}] Rate limit exceeded for {e.limit}. Try again in {reset_at - time.time():.1f} seconds. Remaining: {remaining}")
# Simulate some requests
user = "test_user_1"
print(f"--- Simulating requests for {user} ({rate_limit_string}) ---")
for i in range(7):
do_limited_action(user)
time.sleep(1) # Simulate some delay
print("\n--- Waiting for reset time ---")
time.sleep(60)
print("\n--- Simulating requests after reset ---")
for i in range(3):
do_limited_action(user)
time.sleep(1)