Comparing Two Ways to Use “scope” in DRF Throttling: ScopedRateThrottle vs Inheriting UserRateThrottle
When applying scope‑based throttling in DRF, two common patterns frequently appear.
- Pattern A: Inherit from
UserRateThrottle(orSimpleRateThrottle), create a custom class with a fixedscope, and attach it to the view viathrottle_classes. - Pattern B: Register
ScopedRateThrottleinDEFAULT_THROTTLE_CLASSESand simply setthrottle_scope = "login"on the view.
Do they produce the same effect?
If you only need a single rate limit based on user ID (or unauthenticated IP) + scope, the outcomes are almost identical. Differences arise in how the throttling is applied (error prevention), extensibility (multiple policies / custom criteria), and code structure (settings‑centric vs code‑centric).
Commonality: Both build a key from scope + (user ID or IP)
ScopedRateThrottleconstructs a key usingthrottle_scope(if present) plus the user ID or IP, then looks up the corresponding rate inDEFAULT_THROTTLE_RATES.UserRateThrottlealso limits by user ID for authenticated users and by IP for unauthenticated ones. To support multiple scopes, DRF recommends subclassing the throttle and assigning differentscopevalues.
Difference 1: Where the scope is declared – view attribute vs class attribute
Pattern A: Scope fixed in the class
from rest_framework.throttling import UserRateThrottle
from rest_framework.views import APIView
class CustomDomainSaveThrottle(UserRateThrottle):
scope = "custom_domain_save"
class SaveCustomDomainView(APIView):
throttle_classes = [CustomDomainSaveThrottle]
- The
scopeis hard‑coded in the class. - The view only needs to reference the class.
- Reduces the chance of typos in the scope string across multiple views.
Pattern B: Scope set on the view, interpreted by a shared throttle
REST_FRAMEWORK = {
"DEFAULT_THROTTLE_CLASSES": [
"rest_framework.throttling.ScopedRateThrottle",
],
"DEFAULT_THROTTLE_RATES": {
"login": "5/min",
}
}
from rest_framework.views import APIView
class LoginView(APIView):
throttle_scope = "login"
- The
scopelives directly on the view. - The shared
ScopedRateThrottlechecks the view forthrottle_scopeand applies the rate from settings. - Keeps code concise and centralizes policy management in settings.
Difference 2: Who guarantees the application – global registration vs per‑view attachment
Misconception about Pattern B
Adding ScopedRateThrottle to DEFAULT_THROTTLE_CLASSES does not affect views that lack throttle_scope. So a global registration does not blanket‑block all APIs.
Strength of Pattern A
You can attach a throttle to specific views without touching global settings. This is handy when you cannot modify settings extensively or when you want to enforce throttling only on particular apps or endpoints.
Difference 3: Extensibility – beyond a single limit
The two patterns look similar only when each view has a single scope and a single rate. Beyond that, differences become pronounced.
1) Applying multiple throttles (burst + sustained) to one view
DRF’s documentation shows using several UserRateThrottle subclasses for burst and sustained limits.
class Burst(UserRateThrottle):
scope = "burst"
class Sustained(UserRateThrottle):
scope = "sustained"
class SearchView(APIView):
throttle_classes = [Burst, Sustained]
With ScopedRateThrottle alone, expressing two scopes for one view is awkward; you would need custom logic or additional classes.
2) Changing the basis of throttling
ScopedRateThrottle operates on user ID / IP by default. Real‑world scenarios often require:
- IP + username for login (to mitigate account‑targeted attacks)
- tenant_id for multi‑tenant services
- API key for key‑based limits
These cases necessitate overriding get_cache_key() or similar logic, which naturally leads to Pattern A (subclassing / custom throttle). DRF also encourages custom throttles for such needs.

Which pattern should you choose?
For most "view‑specific rate limits" → Pattern B (ScopedRateThrottle)
- Minimal code changes
- Central policy management via settings
- No effect on views without
throttle_scope
If any of the following apply → Pattern A (inherit / custom class)
- You need multiple limits (burst + sustained) on a single view
- The throttling basis is not user ID / IP (e.g., username+IP, tenant, API key)
- You cannot modify global settings and want to enforce throttling only on selected views
- You prefer documenting the policy through the class name
Related article: