Laravel Quota is a package for tracking and enforcing cumulative usage limits. For example, you could enforce limits like 50 PDF exports a month, 1,000 API queries a day, a pool of AI credits per billing cycle, and more.
It's aimed at a different problem than Laravel's RateLimiter: instead of throttling bursts of traffic in a sliding window, it counts consumption against a budget that resets on a calendar boundary, and it can persist that count in the database rather than only in the cache.
Here's what the package gives you:
- Calendar-aligned periods — define
perMinute(),perHour(),perDay(),perWeek(),perMonth(),perYear(), orperiod($start, $end)for a custom window - A
HasQuotastrait that puts quotas on any Eloquent model - Route middleware (
quota:exports,50,month) that only consumes on a successful response - Cache or database backends, switchable per call
- Atomic consumption - lock consumption around the work it meters
- Enforce budgets - throw an HTTP 429 when the budget is spent
- And more.
The Fluent API
A quota is a named counter scoped to an owner, with a limit and a period. Once you've described one, you can inspect it or spend against it:
use ZaberDev\Quota\Facades\Quota; $builder = Quota::for('api_queries', $user) ->limit(1000) ->perDay(); $builder->used();$builder->remaining();$builder->isExceeded();$builder->hasCapacity(10); $info = $builder->consume(5);
The consume() method returns an immutable QuotaInfo DTO. If you'd rather fail hard than branch, the enforce() method throws an HTTP 429 when the budget is gone:
Quota::for('api_queries', $user)->limit(1000)->perDay()->enforce();
For work where a double-spend actually matters, block() wraps the callback in a lock so two concurrent requests can't both slip through the capacity check:
Quota::for('pdf_generation', $user) ->limit(50) ->perMonth() ->block(function () use ($pdfService) { $pdfService->generate(); }, amount: 1, lockSeconds: 30);
Quotas on Eloquent Models
Add the HasQuotas trait and the same builder hangs off the model:
use ZaberDev\Quota\HasQuotas; class User extends Authenticatable{ use HasQuotas;}
$user->quota('pdf_exports')->limit(25)->perMonth()->consume(); $user->quota('pdf_exports')->limit(25)->perMonth()->remaining();
On the database backend the records are polymorphic, so you can query a model's quotas like any other relation:
$activeQuotas = $user->quotas() ->where('period_end', '>', now()) ->get();
Route Middleware
The quota middleware takes a name, a limit, a period, and an optional driver:
Route::post('/exports/generate', [ExportController::class, 'store']) ->middleware('quota:exports,50,month'); Route::post('/api/v1/query', [ApiController::class, 'query']) ->middleware('quota:api_query,1000,day,database');
The ordering is worth noting: capacity is checked before the route runs, but the quota is only consumed when the response comes back 2xx or 3xx. A request that 500s or fails validation doesn't cost the user anything.
Storage Backends
The default driver is cache, backed by whatever store you've configured — Redis and Memcached both work. The database driver writes to a quotas table instead, which is what you want when the count is tied to billing and can't evaporate with a cache flush. You can pick per call:
Quota::for('api_ping', $ip)->using('cache')->limit(5000)->perDay()->consume(); Quota::for('monthly_exports', $user)->using('database')->limit(50)->perMonth()->consume();
Quota::extend() registers a custom backend from a service provider if you need somewhere else to put the counters, and expired database rows can be cleaned up on a schedule:
use ZaberDev\Quota\Models\Quota; Schedule::command('model:prune', ['--model' => Quota::class])->daily();
Installation
The package requires PHP 8.2 and supports Laravel 11, 12, and 13:
composer require zaber-dev/laravel-quota php artisan vendor:publish --provider="ZaberDev\Quota\QuotaServiceProvider"php artisan migrate
The config/quotas.php configuration file sets the default driver (QUOTA_DRIVER), the cache store and key prefix, the database table name, and whether events are dispatched.
You can find installation instructions and full documentation on GitHub.