How I protected my Django REST API with custom throttling — login was wide open to brute force
I was building a Django REST API with JWT auth and realized
my login endpoint had zero rate limiting. Anyone could
attempt logins as fast as their connection allowed.
DRF's default throttling puts the same rate on every
endpoint. That makes no sense — your public feed endpoint
and your login endpoint are completely different threat
surfaces.
Here's the fix I used — custom throttle classes per endpoint:
from rest_framework.throttling import AnonRateThrottle
class LoginThrottle(AnonRateThrottle):
rate = '5/minute'
scope = 'login'
class OTPThrottle(AnonRateThrottle):
rate = '3/minute'
scope = 'otp'
class PasswordResetThrottle(AnonRateThrottle):
rate = '5/hour'
scope = 'password_reset'
Then apply per view:
class LoginView(APIView):
throttle_classes = [LoginThrottle]
def throttled(self, request, wait):
raise Throttled(detail={
"message": "Too many attempts. Please wait.",
"wait_seconds": int(wait)
})
The wait_seconds in the error response is important —
your frontend can show a real countdown instead of a
generic error message.
Three things I learned:
5/minute on login is more than enough for real users
Always test throttling with pytest — loop requests
until you hit 429 to confirm it's actually working
If you run multiple servers, use Redis as your cache
backend or throttling won't work across instances
I wrote the full breakdown with pytest examples and
Redis setup here:
https://learnpython-xi.vercel.app/post/0c469fda-d843-4d87-9ca1-0a606387d84e
Happy to answer questions — been running this in
production for a few weeks now.