spatie/guzzle-rate-limiter-middleware
Guzzle middleware to rate-limit HTTP requests by requests/second or requests/minute. When the limit is hit, it sleeps until a slot is available. Includes an in-memory store and supports custom persistence for sharing limits across processes.
Installation:
composer require spatie/guzzle-rate-limiter-middleware
Add to composer.json if using Guzzle 6/7:
"require": {
"guzzlehttp/guzzle": "^7.0"
}
Basic Usage:
use Spatie\GuzzleRateLimiterMiddleware\RateLimiterMiddleware;
$client = new \GuzzleHttp\Client([
'middleware' => [
new RateLimiterMiddleware(10, 60), // 10 requests per 60 seconds
],
]);
First Use Case: Rate-limit API calls to a third-party service (e.g., Twitter API):
$response = $client->get('https://api.twitter.com/1.1/statuses/user_timeline.json');
Per-Endpoint Rate Limiting: Use middleware stacks for granular control:
$client = new \GuzzleHttp\Client([
'middleware' => [
new RateLimiterMiddleware(5, 60, 'twitter_endpoint'), // Unique key per endpoint
],
]);
Dynamic Rate Limits: Fetch limits from a config file or database:
$rateLimit = config('services.api.rate_limits.twitter');
$middleware = new RateLimiterMiddleware($rateLimit['max'], $rateLimit['period']);
Retry Logic: Combine with Guzzle’s retry middleware:
$client = new \GuzzleHttp\Client([
'middleware' => [
new RateLimiterMiddleware(3, 10), // 3 requests per 10 seconds
new \GuzzleHttp\Middleware\RetryMiddleware([
'max_retries' => 3,
'delay' => 100,
]),
],
]);
Laravel Integration: Use in HTTP clients (Laravel 8+):
$client = new \GuzzleHttp\Client([
'base_uri' => 'https://api.example.com',
'middleware' => [
new RateLimiterMiddleware(100, 60),
],
]);
$response = Http::client($client)->get('/endpoint');
Async Requests:
For async requests (e.g., ReactPHP), use the InMemoryStore or implement a custom driver:
$store = new \Spatie\GuzzleRateLimiterMiddleware\InMemoryStore();
$middleware = new RateLimiterMiddleware(10, 60, null, $store);
Process Isolation:
InMemoryStore does not work across processes (e.g., queues, cron jobs). Use a shared store (e.g., Redis, database) for distributed systems.$store = new \Spatie\GuzzleRateLimiterMiddleware\RedisStore(
new \Redis(),
'rate_limiter'
);
Sleep Overhead:
sleep() when limits are hit. Avoid in long-running scripts or high-throughput systems.SETNX + EXPIRE).Key Collisions:
/users/{id}), override the key logic:
$middleware = new RateLimiterMiddleware(10, 60, function ($request) {
return 'users_' . $request->getUri()->getPath();
});
Testing:
$store = Mockery::mock(\Spatie\GuzzleRateLimiterMiddleware\Store::class);
$store->shouldReceive('increment')->andReturn(true); // Simulate limit hit
$middleware = new RateLimiterMiddleware(1, 1, null, $store);
Monitoring:
$middleware = new RateLimiterMiddleware(10, 60);
$middleware->setLogger(new \Monolog\Logger('rate_limiter'));
Burst Handling:
5, 1 for 5 requests per second).Custom Drivers:
\Spatie\GuzzleRateLimiterMiddleware\Store for database-backed stores:
class DatabaseStore implements Store {
public function increment(string $key): bool {
// Custom logic (e.g., MySQL + LUA script)
}
}
Laravel Caching:
$store = new \Spatie\GuzzleRateLimiterMiddleware\CacheStore(
app('cache.store')
);
Debugging:
$middleware->setDebug(true);
How can I help you explore Laravel packages today?