βHandling Rate Limits
Handling rate limits with API integrations can be hard. Saloon has a first-party plugin that provides you with the tools you need to prevent rate limits and handle what happens if a rate limit is exceeded.
With this plugin, you are able to define various limits on a per-connector or request basis. You can also control if Saloon should throw an exception or sleep if a limit is reached. Saloon will keep track of how many requests are made and when a rate limit is hit, Saloon will prevent further requests on the connector/request until the rate limit has been lifted.
Saloon will even listen out for "429 Too Many Requests" responses and will automatically throw exceptions before your future requests are sent and the limit will be lifted based on the Retry-After
header.
This plugin also comes with a Laravel Job middleware which you can use inside of your jobs to automatically release them back onto the queue if a rate limit has been reached.
Installation
You can install this plugin via Composer.
Getting Started
To use the plugin, add the HasRateLimit
trait to your connector or request. If you have a connector, you should put it on the connector, but it can be put on an individual request if a specific endpoint has a different rate limit or if you are using solo requests. If you are using the trait on both your connector and request, the rate limits are combined together.
Next, you will be required to implement two methods: resolveLimits
and resolveRateLimitStore
. These methods allow you to define the limits that Saloon will keep track of and the store where the limit "hits" will be kept.
Stores
Here are the various stores that the rate-limiting plugin supports. You may also create your own stores if this library does not come with the one you need. Stores are used to keep track of how many requests have been sent through a given connector/request.
Available Stores
In-Memory (Array)
File
Redis
Predis
PSR Cache Store
Laravel Cache Store
Memory Store
The simplest store. This store is persisted on the current instance of the connector/request and all information is lost when the connector is destructed.
File Store
This store will use the local filesystem to store the limits. The only requirement for this store is for you to define the absolute path to a directory where you would like the limits to be stored.
Redis Store
This store will use PHP's Redis
extension to store the limits on a Redis database. You should pass the Redis configuration into this store.
Predis Store
Similar to the RedisStore
, the PredisStore
allows you to connect to Redis through the predis/predis
PHP library.
PSR Cache Store
This store supports any PSR-16 cache store provided by the psr/simple-cache
library.
Laravel Cache Store
This store can only be used in a Laravel environment but allows you to use any of Laravel's cache disks.
Limits
While this plugin can detect if a 429 status occurs from a response, it's better to prevent your application from hitting rate limits than let them happen. This plugin provides an expressive Limit
class which can be used to define different limits. You can define as many limits as you like, with various intervals.
Configuring Limits
Here is a simple example of a limit for an API which only allows 60 requests per minute, but has a daily limit of 1,000 API calls. There are many different limit intervals, as well as different ways you can instruct Saloon to handle the limit. There is no restriction on the number of limits you can have.
Limit Intervals
There are various limit intervals which you can use on your limiter, ranging for seconds to up to the end of the month.
Custom Names
Sometimes you may need to add a name to your limiter. A good example of this is if you have separate API keys per user and therefore require a different API rate limit per user. You can add the name()
method to your limit to specify a custom name for your limiter. Each limiter's name must be unique.
Custom Prefixes
Saloon will append the class name of the connector or a request as the prefix to the limiter name. For example SpotifyConnector:30_every_60. You may customise the prefix by extending the getLimiterPrefix
method on your connector or request.
Custom Thresholds
You may want to specify the percentage threshold that Saloon should accept as the number of "hits" on a given limit. This is useful if you want to stay just under the real API limit while still defining the limit in the connector/request.
The threshold must be a number between 0 and 1 (e.g 0.8 = 80%)
Sleep
If would rather Saloon didn't throw an exception, you can use the sleep
method when defining a limit. When using the sleep method, an exception won't be thrown. Instead, Saloon will wait the remaining number of seconds before a request is attempted again.
"429: Too Many Attempts" Detection
While it's recommended that you should define your limits above, Saloon will try to catch 429 "Too Many Attempts" errors from an API and will automatically mark a limit as "exceeded" if it sees this status. By default, Saloon will attempt to parse the Retry-After
header to work out when a limit has been exceeded. If Saloon cannot calculate this, the limit will be released after 60 seconds.
You can customise this behaviour by overwriting the handleTooManyAttempts
method.
Alternatively, you may choose to disable this functionality. You can do this by setting the detectTooManyAttempts
property to false
in your connector/request's constructor.
Handling Rate Limits Being Exceeded
When a rate limit has been reached, Saloon will throw a RateLimitReachedException
. This exception contains a getLimit
method which may be used to see the limit that has thrown the exception and see the number of seconds to wait before a request can be sent again. This can be done with a simple try-catch approach or if you are using the provided Laravel job middleware, then you can instruct your jobs to wait until the limit has been lifted.
Try/Catch
As mentioned above, Saloon will throw an exception if a rate limit is reached or if the API returns a 429: Too Many Requests response. You could use a try/catch block to catch the exception and do something with the limit. For example, you may return an error to your users to let them know how long they need to wait - or you might retry the request later. If you are using Saloon in a context of a queued process, then you may want to retry the queued job in the future, from the remaining seconds.
Sleep
If would rather Saloon didn't throw an exception, you can use the sleep
method when defining a limit. When using the sleep method, an exception won't be thrown. Instead, Saloon will wait the remaining number of seconds before a request is attempted again.
Laravel Job Middleware
If you are using Laravel, then this library comes with a job middleware that you can use. This job middleware will catch the RateLimitReachedException
and automatically release your job back onto the queue with the remaining seconds added. Add this to the middleware
method on your Laravel Job.
You may also wish to increase your job's tries when using this middleware in case the job needs to be retried multiple times.
Creating your own store
You may create your own rate limit store by implementing the RateLimitStore
interface.
Disabling Rate Limiting
Sometimes you might want to disable rate limiting by default or even disable it on a per-connector basis. You can either use a property to disable the rate-limiting functionality by default, or you can use the useRateLimitPlugin()
method to disable it on a per-instance basis.
Last updated