# Throttling

Throttles allows to control the rate of requests that clients can make to an API. Django Ninja allows to set custom throttlers globally (across all operations in NinjaAPI instance), on router level and each operation individually.

!!! note
    The application-level throttling that Django Ninja provides should not be considered a security measure or protection against brute forcing or denial-of-service attacks. Deliberately malicious actors will always be able to spoof IP origins. The built-in throttling implementations are implemented using Django's cache framework, and use non-atomic operations to determine the request rate, which may sometimes result in some fuzziness.


Django Ninja’s throttling feature is pretty much based on what Django Rest Framework (DRF) uses, which you can check out [here](https://www.django-rest-framework.org/api-guide/throttling/). So, if you’ve already got custom throttling set up for DRF, there’s a good chance it’ll work with Django Ninja right out of the box. The key difference is that you need to pass initialized Throttle objects instead of classes (which should give a better performance).

You can specify a rate using the format requests/time-unit, where time-unit represents a number of units followed by an optional unit of time. If the unit is omitted, it defaults to seconds. For example, the following are equivalent and all represent "100 requests per 5 minutes":

    * 100/5m
    * 100/300s
    * 100/300

The following units are supported:

    * `s` or `sec`
    * `m` or `min`
    * `h` or `hour`
    * `d` or `day`

## Usage

### Global

The following example will limit unauthenticated users to only 10 requests per second, while authenticated can make 100/s

```Python
from ninja.throttling import AnonRateThrottle, AuthRateThrottle

api = NinjaAPI(
    throttle=[
        AnonRateThrottle('10/s'),
        AuthRateThrottle('100/s'),
    ],
)
```

!!! tip
    `throttle` argument accepts single object and list of throttle objects

### Router level

Pass `throttle` argument either to `add_router` function

```Python
api = NinjaAPI()
...

api.add_router('/sensitive', 'myapp.api.router', throttle=AnonRateThrottle('100/m'))
```

or directly to init of the Router class:

```Python
router = Router(..., throttle=[AnonRateThrottle('1000/h')])
```


### Operation level

If `throttle` argument is passed to operation - it will overrule all global and router throttles:

```Python
from ninja.throttling import UserRateThrottle

@api.get('/some', throttle=[UserRateThrottle('10000/d')])
def some(request):
    ...
```

## Builtin throttlers

### AnonRateThrottle

Will only throttle unauthenticated users. The IP address of the incoming request is used to generate a unique key to throttle against.


### UserRateThrottle

Will throttle users (**if you use django build-in user authentication**) to a given rate of requests across the API. The user id is used to generate a unique key to throttle against. Unauthenticated requests will fall back to using the IP address of the incoming request to generate a unique key to throttle against.

### AuthRateThrottle

Will throttle by Django ninja [authentication](authentication.md) to a given rate of requests across the API.  Unauthenticated requests will fall back to using the IP address of the incoming request to generate a unique key to throttle against.

Note: the cache key in case of `request.auth` will be generated by `sha256(str(request.auth))` - so if you returning some custom objects inside authentication make sure to implement `__str__` method that will return a unique value for the user.


## Custom throttles
To create a custom throttle, override `BaseThrottle` (or any of builtin throttles) and implement `.allow_request(self, request)`. The method should return `True` if the request should be allowed, and `False` otherwise.

Example

```Python
from ninja.throttling import AnonRateThrottle

class NoReadsThrottle(AnonRateThrottle):
    """Do not throttle GET requests"""
    
    def allow_request(self, request):
        if request.method == "GET":
            return True
        return super().allow_request(request)
```
