Skip to main content

Django Gradual Throttle

A Django middleware library that provides gradual request throttling with configurable delay strategies. Unlike traditional rate limiting that immediately blocks requests, this library applies progressive delays to slow down excessive requests gracefully.

Features

  • Gradual Throttling: Apply progressive delays instead of hard blocking
  • Configurable Delay Strategies: Linear, exponential, or custom delay algorithms
  • Flexible Key Functions: Throttle by IP address, user ID, or custom keys
  • Django Cache Integration: Works with any Django-compatible cache backend
  • Comprehensive Configuration: Extensive settings for fine-tuning behavior
  • Monitoring & Debugging: Built-in hooks and dry-run mode
  • Headers Support: Optional response headers for client awareness
  • Path & User Exemptions: Skip throttling for specific paths or user types

Installation

pip install django-gradual-throttle

Quick Start

  1. Add the middleware to your Django settings:
MIDDLEWARE = [
    # ... other middleware
    'django_gradual_throttle.middleware.GradualThrottleMiddleware',
    # ... rest of middleware
]
  1. Basic configuration in settings.py:
# Allow 60 requests per minute
DJANGO_GRADUAL_THROTTLE_RATE = 60
DJANGO_GRADUAL_THROTTLE_WINDOW = 60  # seconds

# Apply 0.2s delay per excess request, max 5s
DJANGO_GRADUAL_THROTTLE_BASE_DELAY = 0.2
DJANGO_GRADUAL_THROTTLE_MAX_DELAY = 5.0
  1. That's it! The middleware will now apply gradual throttling to your Django application.

Configuration

All settings are optional and have sensible defaults:

Basic Settings

# Enable/disable throttling (default: True)
DJANGO_GRADUAL_THROTTLE_ENABLED = True

# Requests allowed per time window (default: 60)
DJANGO_GRADUAL_THROTTLE_RATE = 60

# Time window in seconds (default: 60)
DJANGO_GRADUAL_THROTTLE_WINDOW = 60

# Base delay per excess request in seconds (default: 0.2)
DJANGO_GRADUAL_THROTTLE_BASE_DELAY = 0.2

# Maximum delay in seconds (default: 5.0)
DJANGO_GRADUAL_THROTTLE_MAX_DELAY = 5.0

Advanced Settings

# Cache backend to use (default: 'default')
DJANGO_GRADUAL_THROTTLE_CACHE_ALIAS = 'throttle_cache'

# Key function for identifying requests (default: IP/User based)
DJANGO_GRADUAL_THROTTLE_KEY_FUNC = 'myapp.utils.custom_key_func'

# Delay strategy class (default: linear)
DJANGO_GRADUAL_THROTTLE_DELAY_STRATEGY = 'myapp.strategies.CustomDelayStrategy'

# Paths to exempt from throttling
DJANGO_GRADUAL_THROTTLE_EXEMPT_PATHS = ['/admin/', '/health/']

# User attributes that exempt from throttling
DJANGO_GRADUAL_THROTTLE_EXEMPT_USERS = ['is_staff', 'is_superuser']

# Hook function for monitoring/logging
DJANGO_GRADUAL_THROTTLE_HOOK = 'myapp.monitoring.throttle_hook'

# Add throttling headers to responses (default: True)
DJANGO_GRADUAL_THROTTLE_HEADERS_ENABLED = True

# Hard limit - return 429 after this many requests (default: 0 = disabled)
DJANGO_GRADUAL_THROTTLE_HARD_LIMIT = 200

# Dry run mode - log delays instead of applying them (default: False)
DJANGO_GRADUAL_THROTTLE_DRY_RUN = False

Delay Strategies

Built-in Strategies

Linear Delay (Default)

DJANGO_GRADUAL_THROTTLE_DELAY_STRATEGY = 'django_gradual_throttle.strategies.linear.LinearDelayStrategy'

Delay increases linearly: delay = base_delay * excess_requests

Exponential Delay

DJANGO_GRADUAL_THROTTLE_DELAY_STRATEGY = 'django_gradual_throttle.strategies.exponential.ExponentialDelayStrategy'

Delay increases exponentially: delay = base_delay * (multiplier ^ (excess_requests - 1))

Custom Delay Strategy

Create your own delay strategy by extending BaseDelayStrategy:

# myapp/strategies.py
from django_gradual_throttle.strategies.base import BaseDelayStrategy

class CustomDelayStrategy(BaseDelayStrategy):
    def calculate_delay(self, excess_requests: int) -> float:
        if excess_requests <= 0:
            return 0.0
        
        # Custom logic here
        delay = self.base_delay * (excess_requests ** 1.5)
        return self._clamp_delay(delay)

Custom Key Functions

By default, requests are keyed by IP address for anonymous users and user ID for authenticated users. You can customize this:

# myapp/utils.py
def custom_key_func(request):
    """Custom key function example."""
    if hasattr(request, 'tenant'):
        return f"throttle:tenant:{request.tenant.id}"
    return f"throttle:ip:{get_client_ip(request)}"

# settings.py
DJANGO_GRADUAL_THROTTLE_KEY_FUNC = 'myapp.utils.custom_key_func'

Monitoring & Hooks

Set up monitoring by providing a hook function:

# myapp/monitoring.py
import logging

logger = logging.getLogger(__name__)

def throttle_hook(request, action, **kwargs):
    """Hook function for monitoring throttling events."""
    if action == 'throttled':
        logger.warning(
            f"Request throttled: {kwargs['current_count']} requests, "
            f"{kwargs['delay']:.2f}s delay"
        )
    elif action == 'hard_limit_exceeded':
        logger.error(f"Hard limit exceeded: {kwargs['current_count']} requests")

# settings.py
DJANGO_GRADUAL_THROTTLE_HOOK = 'myapp.monitoring.throttle_hook'

Response Headers

When DJANGO_GRADUAL_THROTTLE_HEADERS_ENABLED is True, the following headers are added:

  • X-Throttle-Remaining: Requests remaining in current window
  • X-Throttle-Limit: Request limit per window
  • X-Throttle-Window: Time window in seconds
  • X-Throttle-Delay: Applied delay in seconds (if any)
  • X-Throttle-Excess: Number of excess requests (if any)

Cache Backends

The library works with any Django cache backend:

Redis Example

# settings.py
CACHES = {
    'default': {
        'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
    },
    'throttle': {
        'BACKEND': 'django_redis.cache.RedisCache',
        'LOCATION': 'redis://127.0.0.1:6379/1',
        'OPTIONS': {
            'CLIENT_CLASS': 'django_redis.client.DefaultClient',
        }
    }
}

DJANGO_GRADUAL_THROTTLE_CACHE_ALIAS = 'throttle'

Testing

The library includes comprehensive tests. To run them:

# Install development dependencies
pip install -e .[dev]

# Run tests
python -m pytest

# Run with coverage
python -m pytest --cov=django_gradual_throttle --cov-report=html

Dry Run Mode

For testing and development, use dry run mode to log delays without applying them:

DJANGO_GRADUAL_THROTTLE_DRY_RUN = True

Examples

Basic Rate Limiting

# 100 requests per 5 minutes, 0.1s delay per excess request
DJANGO_GRADUAL_THROTTLE_RATE = 100
DJANGO_GRADUAL_THROTTLE_WINDOW = 300
DJANGO_GRADUAL_THROTTLE_BASE_DELAY = 0.1
DJANGO_GRADUAL_THROTTLE_MAX_DELAY = 10.0

API Throttling with Hard Limit

# 1000 requests per hour, exponential delays, hard limit at 2000
DJANGO_GRADUAL_THROTTLE_RATE = 1000
DJANGO_GRADUAL_THROTTLE_WINDOW = 3600
DJANGO_GRADUAL_THROTTLE_DELAY_STRATEGY = 'django_gradual_throttle.strategies.exponential.ExponentialDelayStrategy'
DJANGO_GRADUAL_THROTTLE_HARD_LIMIT = 2000

Development Setup

# Exempt admin and health checks, enable dry run
DJANGO_GRADUAL_THROTTLE_EXEMPT_PATHS = ['/admin/', '/health/', '/metrics/']
DJANGO_GRADUAL_THROTTLE_EXEMPT_USERS = ['is_staff']
DJANGO_GRADUAL_THROTTLE_DRY_RUN = True  # For development

Performance Considerations

  • Cache Performance: Use Redis or Memcached for production deployments
  • Key Distribution: Ensure your key function distributes load evenly
  • Window Size: Larger windows use more memory but provide smoother throttling
  • Delay Granularity: Very small delays (< 0.01s) may not be effective

Contributing

Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.

License

This project is licensed under the MIT License - see the LICENSE file for details.

Changelog

v1.0.0

  • Initial release
  • Linear and exponential delay strategies
  • Comprehensive configuration options
  • Django cache integration
  • Monitoring hooks and dry run mode
  • Full test coverage

Release files for django-gradual-throttle 1.0.3

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for django-gradual-throttle 1.0.3
File Size Uploaded
django_gradual_throttle-1.0.3.tar.gz 19.2 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for django-gradual-throttle 1.0.3
File Interpreter ABI Platform
django_gradual_throttle-1.0.3-py3-none-any.whl Python 3 none any Details

Total release size: 36.4 kB

Release files / django_gradual_throttle-1.0.3.tar.gz

Download URL django_gradual_throttle-1.0.3.tar.gz
Size 19.2 kB
Tags Source
SHA-256 checksum
How to use checksums
972ae25984b992bf1aef14f34fe9676e67b6593e878bc29867366b2e57aa2b48
BLAKE2b-256 checksum
How to use checksums
e0de0a683985bffd2f6a49137733b8047e636245fdcdf927b1352ea8141fb7e8
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.13.3

Release files / django_gradual_throttle-1.0.3-py3-none-any.whl

Download URL django_gradual_throttle-1.0.3-py3-none-any.whl
Size 17.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
136d58bad9ac52a401aa696ae73beb55318e23ecc143461a7eb13aeb5ec91752
BLAKE2b-256 checksum
How to use checksums
87e0e8adc8947056ac6f6dae6ed4cccb6755328a6e7ceba636185a1ca5bd02c4
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.13.3

Release history Release notifications | RSS feed

This release

1.0.3 This release

2 release files

1.0.1

2 release files

1.0.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page