Rate limiting utilities for Python with multiple strategies and storage backends
—
Rate limit items define the parameters for rate limiting including the limit amount, time window granularity, and optional namespace for categorization. Each item represents a specific rate limit configuration that can be applied to different identifiers.
The foundational class for all rate limit items, providing common functionality for key generation, expiry calculation, and granularity matching.
class RateLimitItem:
"""Base class for rate limit items with configurable granularity"""
def __init__(self, amount: int, multiples: int | None = 1, namespace: str = "LIMITER"):
"""
Initialize a rate limit item.
Args:
amount: The rate limit amount (number of requests allowed)
multiples: Multiple of the granularity period (default: 1)
namespace: Category for the specific rate limit (default: "LIMITER")
"""
def get_expiry(self) -> int:
"""
Get the duration the limit is enforced for in seconds.
Returns:
Duration in seconds based on granularity and multiples
"""
def key_for(self, *identifiers: str | int | float | bytes) -> str:
"""
Construct a key for the current limit and identifiers.
Args:
identifiers: List of values to append to the key for uniqueness
Returns:
String key identifying this resource with identifiers
"""
@classmethod
def check_granularity_string(cls, granularity_string: str) -> bool:
"""
Check if this class matches a granularity string.
Args:
granularity_string: String like "second", "minute", etc.
Returns:
True if the granularity matches this item type
"""Concrete implementations for different time granularities, each with predefined time periods.
class RateLimitItemPerSecond(RateLimitItem):
"""Rate limit item with 1-second granularity"""
GRANULARITY: Granularity # (1, "second")
class RateLimitItemPerMinute(RateLimitItem):
"""Rate limit item with 1-minute granularity"""
GRANULARITY: Granularity # (60, "minute")
class RateLimitItemPerHour(RateLimitItem):
"""Rate limit item with 1-hour granularity"""
GRANULARITY: Granularity # (3600, "hour")
class RateLimitItemPerDay(RateLimitItem):
"""Rate limit item with 1-day granularity"""
GRANULARITY: Granularity # (86400, "day")
class RateLimitItemPerMonth(RateLimitItem):
"""Rate limit item with 1-month granularity (30 days)"""
GRANULARITY: Granularity # (2592000, "month")
class RateLimitItemPerYear(RateLimitItem):
"""Rate limit item with 1-year granularity (365 days)"""
GRANULARITY: Granularity # (31104000, "year")from limits import (
RateLimitItemPerSecond,
RateLimitItemPerMinute,
RateLimitItemPerHour,
RateLimitItemPerDay
)
# Simple rate limits
requests_per_second = RateLimitItemPerSecond(10) # 10 requests per second
api_calls_per_minute = RateLimitItemPerMinute(100) # 100 calls per minute
uploads_per_hour = RateLimitItemPerHour(50) # 50 uploads per hour
downloads_per_day = RateLimitItemPerDay(1000) # 1000 downloads per day
# Rate limits with custom time multiples
burst_limit = RateLimitItemPerSecond(20, multiples=5) # 20 requests per 5 seconds
weekly_limit = RateLimitItemPerDay(7000, multiples=7) # 7000 requests per 7 days
# Rate limits with custom namespaces
user_api_limit = RateLimitItemPerMinute(60, namespace="USER_API")
admin_api_limit = RateLimitItemPerMinute(300, namespace="ADMIN_API")from limits import RateLimitItemPerMinute
# Create a rate limit item
rate_limit = RateLimitItemPerMinute(100, namespace="API")
# Generate keys for different identifiers
user_key = rate_limit.key_for("user", "12345")
# Returns: "API/user/12345/100/1/minute"
ip_key = rate_limit.key_for("192.168.1.1")
# Returns: "API/192.168.1.1/100/1/minute"
endpoint_key = rate_limit.key_for("endpoint", "/api/users", "POST")
# Returns: "API/endpoint//api/users/POST/100/1/minute"
# Check expiry duration
expiry_seconds = rate_limit.get_expiry() # Returns: 60from limits import RateLimitItemPerMinute, RateLimitItemPerHour
# Check if rate limit items match granularity strings
minute_item = RateLimitItemPerMinute(10)
hour_item = RateLimitItemPerHour(100)
print(minute_item.check_granularity_string("minute")) # True
print(minute_item.check_granularity_string("hour")) # False
print(hour_item.check_granularity_string("hour")) # Truefrom typing import NamedTuple
class Granularity(NamedTuple):
"""Defines a time granularity"""
seconds: int # Duration in seconds
name: str # Human-readable name
# Predefined time granularities
TIME_TYPES: dict[str, Granularity] = {
"second": Granularity(1, "second"),
"minute": Granularity(60, "minute"),
"hour": Granularity(3600, "hour"),
"day": Granularity(86400, "day"),
"month": Granularity(2592000, "month"), # 30 days
"year": Granularity(31104000, "year") # 365 days
}
# Registry mapping granularity names to rate limit classes
GRANULARITIES: dict[str, type[RateLimitItem]]Install with Tessl CLI
npx tessl i tessl/pypi-limits