How to throttle your API with Django Rest Framework
Tags: python, django, webdev, programming
Published: 2021-08-28 03:02:38

Control the rate of requests that clients can make to your API.

### But what is throttling...?

As DRF says, throttling is 
> Throttling is similar to permissions, in that it determines if a request should be authorized. Throttles indicate a temporary state, and are used to control the rate of requests that clients can make to an API

In other words, we can say that throttling is a mechanism for limiting how many requests my API can accept in a period of time, we can specify this limit per user, IP address, etc. This is similar to how some APIs limit the number of requests you can make in a day or an hour.


### How throttling is handle in DRF

As with permissions and authentication, throttling in DRF is always defined as a list of classes.

Before running the main body of the view each throttle in the list is checked. If any throttle check fails an `exceptions.Throttled` exception will be raised, and the main body of the view will not run.

And the same as permissions we can set these throttles globally and per views.


#### Global Throttles

We can set a global default throttling policy using the `DEFAULT_THROTTLE_CLASSES` and `DEFAULT_THROTTLE_RATES` settings.


```python
REST_FRAMEWORK = {
    'DEFAULT_THROTTLE_CLASSES': [
        'rest_framework.throttling.AnonRateThrottle',
        'rest_framework.throttling.UserRateThrottle'
    ],
    'DEFAULT_THROTTLE_RATES': {
        'anon': '20/day',
        'user': '50/day'
    }
}
``` 

*The rate descriptions used in `DEFAULT_THROTTLE_RATES` may include `second`, `minute`, `hour`, or `day` as the throttle period.*

With these settings, 
 - Unauthenticated users will be able to only make 20 requests per day to our API, the IP address of the incoming request is used to generate a unique key to throttle against.

 - Authenticated users will be able to make 50 requests per day to our API, for these the id of the user is going to be used to generate the unique key.

When they reached the maximum of requests our API will respond with the error code  [*429 - Too Many Requests*](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/429) 

Since this is the global configuration this will apply to all views, but we can still override these settings per view.

#### Throttles Per View

It's always better to have control over what views we want to limit, for this DRF offers us the possibility of setting these throttles classes per view.

We do this by passing a list of throttles classes to the `throttle_classes` attribute on the `APIView` class-based views.


```python
from rest_framework.response import Response
from rest_framework.throttling import UserRateThrottle
from rest_framework.views import APIView

class ExampleView(APIView):
    throttle_classes = [UserRateThrottle]

    def get(self, request, format=None):
        content = {
            'status': 'request was permitted'
        }
        return Response(content)
``` 

In this way, `ExampleView` will use the `UserRateThrottle` class to limit the number of requests that this view can receive, the rates, in this case, are still defined on the `DEFAULT_THROTTLE_CLASSES` settings key.

But what if we want to specify a different rate for a specific view, we can do this by extending the `UserRateThrottle` class and specifying a new rate.

```python
from rest_framework.response import Response
from rest_framework.throttling import UserRateThrottle
from rest_framework.views import APIView

class CustomUserRateThrottle(UserRateThrottle):
    rate= '5/day'

class VeryLimitedView(APIView):
    throttle_classes = [CustomUserRateThrottle]

    def get(self, request, format=None):
        content = {
            'status': 'request was permitted'
        }
        return Response(content)
``` 
Now **only** for this view the authenticated users have 5 requests per day, even though the global settings say that the users have 50 requests per day.

#### ScopedRateThrottles

The `ScopedRateThrottle` class can be used to restrict access to specific parts of the API. This throttle will only be applied if the view that is being accessed includes a `.throttle_scope` attribute.

The allowed request rate is determined by the `DEFAULT_THROTTLE_RATES` setting using a key from the request *"scope"*.

For example, given the following views...


```python
class ContactListView(APIView):
    throttle_scope = 'contacts'
    ...

class ContactDetailView(APIView):
    throttle_scope = 'contacts'
    ...

class UploadView(APIView):
    throttle_scope = 'uploads'
    ...
``` 
...and the following settings.


```python
REST_FRAMEWORK = {
    'DEFAULT_THROTTLE_CLASSES': [
        'rest_framework.throttling.ScopedRateThrottle',
    ],
    'DEFAULT_THROTTLE_RATES': {
        'contacts': '100/day',
        'uploads': '50/day'
    }
}
``` 
User requests to either `ContactListView` or `ContactDetailView` would be restricted to a total of 100 requests per day. User requests to `UploadView` would be restricted to 50 requests per day.


### So, what's next...

As we saw, throttles is a powerful and really helpful feature that we can implement on our API if we need to impose different constraints on different parts of the API, due to some services being particularly resource-intensive. Also, it's worth mentioning that DRF provides a `BaseThrottle` class which you can override to create custom throttles with custom implementations.

If you plan to use this method as a security feature just consider that the DRF throttling isn't intended as a security feature, it has some [weaknesses](https://github.com/encode/django-rest-framework/issues/8127)  and you shouldn't rely only upon the throttling. 

Yet, it's a cool feature and you should definitely try it on your API.

You can follow me on [Twitter](https://www.twitter.com/juanbenitezdev) and [GitHub](https://www.github.com/juanbenitezdev) to be up to date with all my projects and content.