Setting Up API Call Limits In Laravel - Part 1
Tags: laravel, api, php
Published: 2021-03-09 16:53:40

In this two part series of articles, I'll go through two ways to set up a request limit for your API based on the calling user's access level. This is extremely useful for any subscription-based API where you want to limit user groups to a specific number of API calls per a certain amount of time.

This part is going to be focused on using Laravel's built in Rate limiters, the second one will dive into creating a custom middleware to enforce the set limits.

# Using a Rate Limiter
The simplest way to achieve this goal is to set up a rate limiting service which is built into Laravel and enabled by default for API middleware-restriced routes. The default limit is set to **60 requests** per minute. The configuration can be changed in */app/Providers/RouteServiceProvider* in the *configureRateLimiting()* method. This is the default:

```
protected function configureRateLimiting()
{
    RateLimiter::for('api', function (Request $request) {
        return Limit::perMinute(60);
    });
}
``` 

If you wanted to disable the default limit (which I wouldn't recommend as you would allow users to make unlimited requests to your API endpoints which may lead to overloads), you may do *return Limit::none();* in this function, or you could remove this throttle from the *api* middleware group in */app/Http/Kernel*.

## Defining a custom rate limiter
In the *configureRateLimiting()* method, you can define more rate limiters. Let's make a limiter called *matches* which is going to limit calls to API route for fetching results from various football matches.

For this use case, let's say we have 3 types of users, an unregistered visitor, a registered user with a free account and a registered user who has subscribed to our service. The first one isn't registered, thus doesn't have an API token, therefore they cannot access our API, the second one has an API access token, but their request limit is one request per hour, the subscribed user can make unlimited number of calls to the API. We will be using the following variables:
- Access Level - Category the user belongs to, 0 is a free account user, 1 is a subscribed user. This is represented by the *access* column of the *users* table in the database
- Bearer Token - API token type for request authentication which is represented by the *api_token* column of the *users* table in the database, every user has a unique token

Now let's put it all together.

```
//RouteServiceProvider.php
protected function configureRateLimiting()
{
    RateLimiter::for('api', function (Request $request) {
        return Limit::perMinute(60);
    });

    RateLimiter::for('matches', function (Request $request) {
      $user = User::where('api_token', $request->bearerToken())
              ->first();
      switch ($user->access) {
        case 0:
          return Limit::perHour(1)->response(function () {
            return response()->json(
              ['error' => 'Request limit exceeded'],
              429
            );
          });
          break;
        case 1:
          return Limit::none();
          break;
      }
    });
}
``` 

```
//routes/api.php
Route::middleware(['auth:api','json.force','throttle:matches'])
->get('test', 'ApiController@testMatches');
``` 

Now that we have successfully set our own limit, we can disable the original *api* rate limiter, as our subscribed users would still be limited by it, as you can see below.

![api_test.gif](https://cdn.hashnode.com/res/hashnode/image/upload/v1614881058458/7ZJI_ENy9.gif)

*The script used for testing the API can be found [here](https://gist.github.com/Loupeznik/10558006bfa3a14237c88d349aee1202)*

To achieve this, we can simply set the API rate limiter to no limit, alternatively, we could disable the *throttle:api* in the *api* middleware group in */app/Http/Kernel* altogether. 

# Next up
Next time, I will be showing you how I tackled the request rate limiting problem myself the first time I encountered the need to limit the access to my API. This option is a little bit more tricky than the one I've shown you today, as it involves creating a set of custom middleware and working with the database a little more.

To be continued...