How to throttle your API with Django Rest Framework

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.

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

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.

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.

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...

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

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

class UploadView(APIView):
    throttle_scope = 'uploads'
    ...

...and the following settings.

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 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 and GitHub to be up to date with all my projects and content.

14