Django cookbook · Beginner

Rate limit API endpoints in DRF

Login and expensive endpoints get hammered by scripts.

Create a free account to save this page, take notes and track your progress. Sign in

Approach

Configure default user and anonymous rates, and use scoped throttles for sensitive endpoints. Use a shared cache (Redis) so limits apply across all application servers.

Solution

# settings.py
REST_FRAMEWORK = {
    "DEFAULT_THROTTLE_CLASSES": [
        "rest_framework.throttling.AnonRateThrottle",
        "rest_framework.throttling.UserRateThrottle",
        "rest_framework.throttling.ScopedRateThrottle",
    ],
    "DEFAULT_THROTTLE_RATES": {"anon": "60/min", "user": "600/min", "login": "5/min", "exports": "10/hour"},
}
CACHES = {"default": {"BACKEND": "django.core.cache.backends.redis.RedisCache", "LOCATION": "redis://redis:6379/1"}}


# views.py
from rest_framework.views import APIView


class LoginView(APIView):
    throttle_scope = "login"
    authentication_classes = []
    permission_classes = []

    def post(self, request):
        ...


class ExportView(APIView):
    throttle_scope = "exports"

    def get(self, request):
        ...

Pitfalls

  • Using the default local-memory cache with several processes; each has its own counters.
  • Behind a proxy, every client appears as the proxy IP; configure NUM_PROXIES correctly.
  • Throttling as the only protection for login; also lock out or slow down per account.

Variations

  • Subclass SimpleRateThrottle to throttle per API key or per tenant.
  • Apply coarse limits at the reverse proxy or gateway too.

Further reading: DRF throttling

Reference: https://www.django-rest-framework.org/api-guide/throttling/

More Django recipes

Was this page helpful?