---
title: "Rate limiting requests — HTTP Client Rate Limits for Laravel | Roundly"
description: "Throttle Laravel HTTP client calls with the Http::rateLimit() macro or the RateLimit Guzzle middleware, from per-second to per-day."
url: https://roundly-consulting.com/open-source/docs/http-client-rate-limits-for-laravel/rate-limiting-requests
language: en
---

[All packages](https://roundly-consulting.com/open-source.md)

[HTTP Client Rate Limits for Laravel](https://roundly-consulting.com/open-source/docs/http-client-rate-limits-for-laravel.md)

# Rate limiting requests

The package adds a rateLimit() macro to Laravel’s HTTP client — the quickest way to throttle outgoing calls:

```php
use Illuminate\Support\Facades\Http;

// Integer shorthand = N requests per minute.
$response = Http::rateLimit(30)->get('https://api.example.com/orders');
```

When the limit is reached, the middleware waits the exact time until the next request is allowed, checks again, then proceeds — you simply get your response a little later. (If another worker on a shared store took that slot while this one waited, it waits again rather than sending over the limit.) Pass a RateLimit (built by the RateLimits facade) or a Limit to pick a different window, and by: to scope the budget to an owner:

```php
use RoundlyConsulting\HttpClientRateLimits\Facades\RateLimits;

Http::rateLimit(RateLimits::perSecond(5))->get('https://api.example.com/things');
Http::rateLimit(RateLimits::perDay(10_000))->get('https://api.example.com/report');

// Each owner gets its own budget (e.g. per account or outbound IP).
Http::rateLimit(30, by: 'acct-1')->get('https://api.example.com/orders');
```

## What rateLimit() accepts

```php
use RoundlyConsulting\HttpClientRateLimits\Facades\RateLimits;
use RoundlyConsulting\HttpClientRateLimits\Limit;

Http::rateLimit(30);                                                     // int: N requests per minute
Http::rateLimit('github');                                               // string: named profile from config
Http::rateLimit(RateLimits::perSecond(5));                               // a RateLimit
Http::rateLimit(new Limit(maxAttempts: 100, timespan: 'hour'));          // a Limit value object
Http::rateLimit([RateLimits::perSecond(5), RateLimits::perMinute(100)]); // compound windows
Http::rateLimit(['github', 30]);                                         // compound: each entry reads as it would alone
Http::rateLimit(30, by: 'acct-1');                                       // any form, scoped to an owner
```

## The RateLimit middleware directly

RateLimit is a Guzzle middleware, so you can also attach it with Http::withMiddleware():

```php
$response = Http::withMiddleware(RateLimits::perSecond(5))
    ->get('https://api.example.com/things');
```

## Building limits

Every limit comes from the RateLimits facade (see The RateLimits facade):

```php
use RoundlyConsulting\HttpClientRateLimits\Facades\RateLimits;

RateLimits::make(Limit $limit);                  // from a Limit value object
RateLimits::perSecond(int $maxAttempts = 1);     // N requests per second
RateLimits::perMinute(int $maxAttempts = 1);     // N requests per minute
RateLimits::perHour(int $maxAttempts = 1);       // N requests per hour
RateLimits::perDay(int $maxAttempts = 1);        // N requests per day
RateLimits::profile(string $name);               // a named profile from config
RateLimits::compound(array $limits);             // several windows at once
```

Each returned RateLimit is fluent: ->by(), ->alongside(), ->maxWait(), ->jitter(), ->adaptive(), the inspection methods ->remaining(), ->availableIn() and ->tooManyAttempts(), and ->reset().

## The Limit value object

A Limit holds the owner key (default global), the number of attempts, the window and the trim flag. Build one directly when you need full control, then wrap it with RateLimits::make(). Fewer than one attempt per window throws InvalidLimitException:

```php
use RoundlyConsulting\HttpClientRateLimits\Facades\RateLimits;
use RoundlyConsulting\HttpClientRateLimits\Limit;

// key (owner), maxAttempts, timespan (Timespan enum or 'second'|'minute'|'hour'|'day'), trim
$limit = new Limit(key: 'acct-1', maxAttempts: 100, timespan: 'hour', trim: true);

$middleware = RateLimits::make($limit);

// Limit is fluent as well:
$limit = (new Limit)->perMinute(30)->by('acct-1')->maxWait(5_000)->jitter(50);
```

With trim enabled, the limiter drops the hits older than the window after each allowed request. Every built-in store already self-trims hits older than a day (plus an hour’s margin), so trim is optional — it keeps a key’s history down to its own window.

## One budget per key and window

Every limit the manager builds records hits in one shared store, under {key}:{window} — e.g. github:second. Limits that share both key and window therefore share one budget wherever they are built, separate Http::rateLimit() calls included. The default InMemoryStore is per process only: each queue worker, PHP-FPM child or server keeps its own count, so switch to a shared store (see Stores) when several processes must draw from one budget:

```php
use RoundlyConsulting\HttpClientRateLimits\Facades\RateLimits;

// Same key + same window = one budget, however and wherever the limit is built.
foreach ($repositories as $repository) {
    Http::rateLimit(RateLimits::perSecond(5)->by('github'))
        ->get("https://api.github.com/repos/{$repository}");
}

// Or build it once and reuse the instance.
$github = RateLimits::perSecond(5)->by('github');

Http::withMiddleware($github)->get('https://api.github.com/user');
```

## Rate limiting any callable

The limiter isn’t tied to the HTTP client. handle() paces any callable and returns its result:

```php
use RoundlyConsulting\HttpClientRateLimits\Facades\RateLimits;

$limit = RateLimits::perSecond(10)->by('sdk');

// Pace any callable (an SDK call, a raw Guzzle request, …) through the same limiter.
$result = $limit->handle(fn () => $client->createInvoice($payload));
```

## Show your open-source love

This package is free and MIT-licensed. If it saves you time, a one-off donation or a Patreon membership keeps it maintained, tested and documented.

[More ways to support, including crypto](https://roundly-consulting.com/support-us.md)

By donating, you agree to our [donation terms](https://roundly-consulting.com/donation-terms.md).

[Support our open source work (opens in a new tab)](https://donate.stripe.com/dRmeVe8FX5PF1Qd9pXcEw00) [Join us on Patreon (opens in a new tab)](https://www.patreon.com/cw/roundly)

## Want this built into your product?

We integrate our packages into custom Laravel and AI builds. Tell us what you're working on and we'll reply within 48 hours.

[Get a quote in 48 hours](https://roundly-consulting.com/contact.md) [Browse all packages](https://roundly-consulting.com/open-source/docs/http-client-rate-limits-for-laravel.md)
