1.Anonymous, user, and scoped throttling
A throttle counts requests against a key and refuses once the count exceeds a rate. DRF ships three classes that differ only in how the key is derived. `AnonRateThrottle` throttles unauthenticated callers by IP address. `UserRateThrottle` throttles by user id when signed in, falling back to the IP when not. `ScopedRateThrottle` reads a `throttle_scope` attribute off the view and applies the rate configured for that scope, which is how one endpoint gets a tighter limit than the rest of the API. Rates are strings like `"100/hour"` or `"5/min"`, and when a caller exceeds one, DRF returns 429 with a `Retry-After` header saying how long to wait.
Key features
- `AnonRateThrottle` keys on the client IP and applies only to unauthenticated requests; authenticated ones pass through it untouched
- `UserRateThrottle` keys on `user.pk` when authenticated and falls back to the IP when not, so listing it alone still limits anonymous traffic — just under the same bucket as everyone else behind that address
- `ScopedRateThrottle` applies only to views that define `throttle_scope`; a view without one is not throttled by it at all
- Rate strings are `<number>/<period>` where only the first character of the period counts — `s`, `m`, `h`, `d`, so `"5/min"` and `"5/m"` are identical
- A throttled request returns 429 with `Retry-After` in seconds, from the class's `wait()` method
- DRF's own docs note the implementations are open to race conditions under high concurrency, so a few extra requests may get through
The three shipped throttle classes
- A request arrives
- AnonRateThrottle — key = client IP
- Unauthenticated only — signed-in requests skip it entirely
- One key per address — a NAT puts thousands of people in one bucket
- The only option before login — which is where the abuse actually is
- UserRateThrottle — key = user.pk, or IP when anonymous
- Precise for signed-in callers — one account cannot hide behind a shared address
- The general per-account budget — generous — it is a backstop, not the real defence
- Falls back to IP — so it also covers anonymous traffic, coarsely
- ScopedRateThrottle — key = scope + user.pk or IP
- throttle_scope = "login" — the limit becomes a property of the endpoint
- Tight where cost is high — password reset, email send, export, search
- No scope, no throttle — a view without the attribute is untouched by this class
Command
class PasswordResetView(APIView):
throttle_classes = [ScopedRateThrottle]
throttle_scope = "password_reset" # rate from DEFAULT_THROTTLE_RATES["password_reset"]A throttle is a counter keyed on "who". `AnonRateThrottle` keys on IP and covers only unauthenticated traffic; `UserRateThrottle` keys on the user id (IP when anonymous) — list both, since they cover different halves. `ScopedRateThrottle` moves the limit onto the endpoint via `throttle_scope`, which is how login and password reset get 5/min while the API keeps a generous default. Setting `throttle_classes` on a view replaces the defaults rather than adding to them, and behind a proxy you must set `NUM_PROXIES` or the IP key is meaningless.