Skip to main content

Overview

Throttling controls the rate of requests that clients can make to an API. Throttles indicate a temporary state and are used to implement rate limiting. Throttling is similar to permissions, but determines if a request should be authorized based on the rate of requests.
Application-level throttling is not a security measure. Malicious actors can spoof IP addresses. Use throttling for implementing business policies and basic protections against service over-use, not for security or DDoS protection.

Configuration

Global Settings

Set throttling policy globally using DEFAULT_THROTTLE_CLASSES and DEFAULT_THROTTLE_RATES:
REST_FRAMEWORK = {
    'DEFAULT_THROTTLE_CLASSES': [
        'rest_framework.throttling.AnonRateThrottle',
        'rest_framework.throttling.UserRateThrottle'
    ],
    'DEFAULT_THROTTLE_RATES': {
        'anon': '100/day',
        'user': '1000/day'
    }
}
Rate Format:
  • Format: number/period
  • Periods: s, m, h, d (or second, minute, hour, day, sec, min, hr)
  • Examples: 100/day, 60/min, 1000/hour

Per-View Settings

Set throttling on individual views:
from rest_framework.throttling import UserRateThrottle
from rest_framework.views import APIView

class ExampleView(APIView):
    throttle_classes = [UserRateThrottle]

    def get(self, request):
        return Response({'status': 'request was permitted'})
Or with function-based views:
from rest_framework.decorators import throttle_classes

@api_view(['GET'])
@throttle_classes([UserRateThrottle])
def example_view(request):
    return Response({'status': 'request was permitted'})

Per-Action Settings

Set throttling for specific actions:
from rest_framework.decorators import action

class MyViewSet(viewsets.ModelViewSet):
    @action(detail=True, methods=["post"], throttle_classes=[UserRateThrottle])
    def example_action(self, request, pk=None):
        return Response({'status': 'request was permitted'})

Base Classes

BaseThrottle

All throttle classes extend BaseThrottle.
class BaseThrottle:
    def allow_request(self, request, view):
        """
        Return True if request should be allowed, False otherwise.
        """
        raise NotImplementedError('.allow_request() must be overridden')

    def get_ident(self, request):
        """
        Identify the machine making the request.
        Uses X-Forwarded-For if present, otherwise REMOTE_ADDR.
        """
        # Implementation handles NUM_PROXIES setting
        ...

    def wait(self):
        """
        Return recommended seconds to wait before next request.
        Return None if not implemented.
        """
        return None

SimpleRateThrottle

Base class for rate throttles using cache. Subclasses must override get_cache_key().
class SimpleRateThrottle(BaseThrottle):
    cache = default_cache
    timer = time.time
    cache_format = 'throttle_%(scope)s_%(ident)s'
    scope = None
    THROTTLE_RATES = api_settings.DEFAULT_THROTTLE_RATES

    def get_cache_key(self, request, view):
        """
        Return unique cache key for throttling.
        Return None if request should not be throttled.
        """
        raise NotImplementedError('.get_cache_key() must be overridden')
Attributes:
cache
cache
default:"default_cache"
Django cache backend to use for storing request history
timer
callable
default:"time.time"
Callable that returns current time in seconds
cache_format
string
default:"'throttle_%(scope)s_%(ident)s'"
Format string for cache keys
scope
string
default:"None"
Scope name used to look up throttle rate in DEFAULT_THROTTLE_RATES
rate
string
default:"None"
Override rate instead of using scope (e.g., ‘100/day’)

Built-in Throttle Classes

AnonRateThrottle

Throttles unauthenticated users by IP address. Authenticated users are not throttled.
from rest_framework.throttling import AnonRateThrottle

class MyView(APIView):
    throttle_classes = [AnonRateThrottle]
Configuration:
REST_FRAMEWORK = {
    'DEFAULT_THROTTLE_RATES': {
        'anon': '100/day',
    }
}
Attributes:
scope
string
default:"'anon'"
Default scope for rate lookup
Suitable for restricting requests from unknown sources.

UserRateThrottle

Throttles authenticated users by user ID. Unauthenticated users are throttled by IP address.
from rest_framework.throttling import UserRateThrottle

class MyView(APIView):
    throttle_classes = [UserRateThrottle]
Configuration:
REST_FRAMEWORK = {
    'DEFAULT_THROTTLE_RATES': {
        'user': '1000/day',
    }
}
Attributes:
scope
string
default:"'user'"
Default scope for rate lookup
Multiple User Throttles:
class BurstRateThrottle(UserRateThrottle):
    scope = 'burst'

class SustainedRateThrottle(UserRateThrottle):
    scope = 'sustained'
REST_FRAMEWORK = {
    'DEFAULT_THROTTLE_CLASSES': [
        'example.throttles.BurstRateThrottle',
        'example.throttles.SustainedRateThrottle'
    ],
    'DEFAULT_THROTTLE_RATES': {
        'burst': '60/min',
        'sustained': '1000/day'
    }
}
Suitable for simple global rate restrictions per user.

ScopedRateThrottle

Restricts access to specific parts of the API. Only applied to views with a .throttle_scope property.
from rest_framework.throttling import ScopedRateThrottle

class ContactListView(APIView):
    throttle_classes = [ScopedRateThrottle]
    throttle_scope = 'contacts'

class UploadView(APIView):
    throttle_classes = [ScopedRateThrottle]
    throttle_scope = 'uploads'
Configuration:
REST_FRAMEWORK = {
    'DEFAULT_THROTTLE_CLASSES': [
        'rest_framework.throttling.ScopedRateThrottle',
    ],
    'DEFAULT_THROTTLE_RATES': {
        'contacts': '1000/day',
        'uploads': '20/day'
    }
}
Attributes:
scope_attr
string
default:"'throttle_scope'"
View attribute name to read scope from

Client Identification

Clients are identified using:
  1. X-Forwarded-For HTTP header (if present)
  2. REMOTE_ADDR WSGI variable (fallback)

NUM_PROXIES Setting

Configure how many proxies are in front of your API:
REST_FRAMEWORK = {
    'NUM_PROXIES': 1,  # Number of proxies
}
NUM_PROXIES
int
default:"None"
  • None - Use all of X-Forwarded-For
  • 0 - Use REMOTE_ADDR only
  • N - Use Nth-to-last address from X-Forwarded-For
Clients behind a NAT gateway will be treated as a single client when NUM_PROXIES is configured.

Cache Configuration

Throttle classes use Django’s cache backend. Default Setup:
CACHES = {
    'default': {
        'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
    }
}
Custom Cache:
from django.core.cache import caches

class CustomAnonRateThrottle(AnonRateThrottle):
    cache = caches['alternate']

Custom Throttles

Implement custom throttles by overriding BaseThrottle:
import random
from rest_framework.throttling import BaseThrottle

class RandomRateThrottle(BaseThrottle):
    def allow_request(self, request, view):
        # Randomly throttle 1 in 10 requests
        return random.randint(1, 10) != 1
With wait() Method:
class CustomThrottle(BaseThrottle):
    def allow_request(self, request, view):
        # Custom logic
        if should_throttle:
            return False
        return True

    def wait(self):
        # Return seconds to wait
        return 60
If wait() is implemented and request is throttled, a Retry-After header is included in the response.

Rate Throttle Internals

parse_rate()

Parses rate string into (num_requests, duration) tuple:
def parse_rate(self, rate):
    """
    Given '60/min', returns (60, 60)
    Given '1000/day', returns (1000, 86400)
    """
    if rate is None:
        return (None, None)
    num, period = rate.split('/')
    num_requests = int(num)
    duration = {'s': 1, 'm': 60, 'h': 3600, 'd': 86400}[period[0]]
    return (num_requests, duration)

throttle_success() / throttle_failure()

Called when request passes or fails throttle check:
def throttle_success(self):
    """Insert current request timestamp into cache"""
    self.history.insert(0, self.now)
    self.cache.set(self.key, self.history, self.duration)
    return True

def throttle_failure(self):
    """Called when request is throttled"""
    return False

Settings

DEFAULT_THROTTLE_CLASSES
list
default:"[]"
List of throttle classes to use by default
REST_FRAMEWORK = {
    'DEFAULT_THROTTLE_CLASSES': [
        'rest_framework.throttling.AnonRateThrottle',
    ]
}
DEFAULT_THROTTLE_RATES
dict
default:"{}"
Dictionary mapping scope names to rate strings
REST_FRAMEWORK = {
    'DEFAULT_THROTTLE_RATES': {
        'anon': '100/day',
        'user': '1000/day',
        'burst': '60/min',
        'sustained': '1000/day',
    }
}
NUM_PROXIES
int
default:"None"
Number of application proxies in front of the API

Response Headers

Retry-After

Included when request is throttled and throttle implements wait() method:
HTTP/1.1 429 Too Many Requests
Retry-After: 60

Notes

Concurrency

Built-in throttle implementations are subject to race conditions under high concurrency. They may allow a few extra requests through due to non-atomic cache operations.
For strict rate limiting under concurrent load, implement a custom throttle class using atomic operations.

Performance

Throttle checks happen before the view executes. For high-traffic APIs, consider:
  • Using a fast cache backend (Redis, Memcached)
  • Configuring cache timeouts appropriately
  • Monitoring cache hit rates

Multiple Throttles

You can use multiple throttles simultaneously:
class MyView(APIView):
    throttle_classes = [
        BurstRateThrottle,
        SustainedRateThrottle,
        AnonRateThrottle,
    ]
All throttle checks must pass for the request to be allowed.

Common Use Cases

Burst + Sustained Throttling

REST_FRAMEWORK = {
    'DEFAULT_THROTTLE_CLASSES': [
        'app.throttles.BurstRateThrottle',
        'app.throttles.SustainedRateThrottle'
    ],
    'DEFAULT_THROTTLE_RATES': {
        'burst': '60/min',      # Max 60 per minute
        'sustained': '1000/day' # Max 1000 per day
    }
}

Different Rates for Anonymous vs Authenticated

REST_FRAMEWORK = {
    'DEFAULT_THROTTLE_CLASSES': [
        'rest_framework.throttling.AnonRateThrottle',
        'rest_framework.throttling.UserRateThrottle'
    ],
    'DEFAULT_THROTTLE_RATES': {
        'anon': '100/day',  # Restrictive for anonymous
        'user': '10000/day' # Generous for authenticated
    }
}

Scoped Rates for Different API Sections

class UploadView(APIView):
    throttle_classes = [ScopedRateThrottle]
    throttle_scope = 'uploads'

class SearchView(APIView):
    throttle_classes = [ScopedRateThrottle]
    throttle_scope = 'search'
REST_FRAMEWORK = {
    'DEFAULT_THROTTLE_RATES': {
        'uploads': '10/hour',   # Resource-intensive
        'search': '1000/hour'  # Less intensive
    }
}

Build docs developers (and LLMs) love