The RateLimits facade
The RateLimits facade is the package’s one entry point for building limits. It resolves the container-bound RateLimitManager singleton, so the configured store and deferrer — or a RateLimits::fake() — apply to every limit, including the ones Http::rateLimit() builds:
use Illuminate\Support\Facades\Http;
use RoundlyConsulting\HttpClientRateLimits\Facades\RateLimits;
// Build a limit and attach it to a request.
Http::rateLimit(RateLimits::perSecond(5)->by('acct-1'))->get('https://api.example.com/orders');
// Profiles and compound windows come from the same place.
$github = RateLimits::profile('github');
$api = RateLimits::compound([RateLimits::perSecond(5), RateLimits::perMinute(100)]);
// Inspect or clear a budget without sending anything.
$limit = RateLimits::perMinute(30)->by('acct-1');
$limit->remaining();
$limit->reset();The full surface
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
RateLimits::usingStore(Store $store); // a copy of the manager on this store
RateLimits::usingDeferrer(Deferrer $deferrer); // … or with this deferrer
RateLimits::releasingJob(object $job); // … releasing a queued job instead of sleeping
RateLimits::retryAfter(Response|RequestException $response); // ?int seconds from Retry-After
RateLimits::store(); // the shared default store
RateLimits::deferrer(); // the default deferrer| Method | Returns | Purpose |
|---|---|---|
make(Limit $limit) | RateLimit | Build from a Limit value object, on the manager’s store and deferrer. |
perSecond / perMinute / perHour / perDay(int $maxAttempts = 1) | RateLimit | N requests per window. |
profile(string $name) | RateLimit | A named profile from the limiters config; unknown names throw UnknownLimiterProfileException. |
compound(array $limits) | RateLimit | Several windows at once; the first is primary, the strictest wins. |
usingStore(Store $store) | RateLimitManager | A copy of the manager on this store; the singleton is untouched. |
usingDeferrer(Deferrer $deferrer) | RateLimitManager | A copy of the manager with this deferrer. |
releasingJob(object $job) | RateLimitManager | A copy that releases the queued job instead of sleeping (the ReleaseDeferrer). |
retryAfter(Response|RequestException $response) | ?int | Seconds from a Retry-After header (delta or HTTP-date); null when absent or unparseable. |
store() | Store | The shared store new limits record hits in. |
deferrer() | Deferrer | The deferrer new limits wait with. |
fake() | RateLimitsFake | Swap in the recording test fake. |
What a limit can do
Every factory returns a RateLimit — a Guzzle middleware you pass to Http::rateLimit() or Http::withMiddleware(), or wrap around any callable with handle(). It is fluent, and anything it doesn’t define itself is forwarded to the underlying Limit, then the Limiter:
| Group | Methods on the returned RateLimit |
|---|---|
| Configure (fluent) | by(), alongside(), maxWait(), jitter(), adaptive(), setStore(), setDeferrer() |
| Inspect | remaining(), availableIn(), tooManyAttempts(), delayUntilNextRequestInMs(), getKey(), getMaxAttempts(), getTimespan(), isOverMaxAttempts(), isUnderMaxAttempts(), getStore(), getDeferrer(), getLimiter() |
| Clear | reset() |
| Run | handle(callable) |
by() scopes every window the limit enforces; alongside() and compound() take copies of the limits you hand them, so re-keying the result never changes those.
Per-call overrides
Override the store or deferrer for one call site without touching the global defaults:
use RoundlyConsulting\HttpClientRateLimits\Deferrer\SleepDeferrer;
use RoundlyConsulting\HttpClientRateLimits\Facades\RateLimits;
use RoundlyConsulting\HttpClientRateLimits\Store\RedisStore;
// usingStore() / usingDeferrer() return a configured copy; the shared manager is untouched.
$limit = RateLimits::usingStore(new RedisStore('default'))
->usingDeferrer(new SleepDeferrer)
->perMinute(30);Prefer an explicit dependency over a static call? See DI and actions — the injected manager has the same API.
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 cryptoBy donating, you agree to our donation terms.
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.