Laravel Quota: Enforce Usage Budgets for Calendar Periods in Laravel
Laravel Composer Pacakge #Laravel #Packages #Rate Limiting #Usage Quota #API

Laravel Quota: Enforce Usage Budgets for Calendar Periods in Laravel

4 min read Mohamed Said Mohamed Said

What Is Laravel Quota?

Laravel Quota (zaber-dev/laravel-quota) is a package for tracking and enforcing cumulative usage limits that reset on calendar boundaries. Think 50 PDF exports per month, 1,000 API queries per day, or a pool of AI credits per billing cycle.

It solves a different problem than Laravel's built-in RateLimiter. Instead of throttling bursts in a sliding window, it counts consumption against a named budget that resets at a predictable calendar boundary—and it can persist that count in the database rather than only in the cache.

Key Features

  • Calendar-aligned periods: perMinute(), perHour(), perDay(), perWeek(), perMonth(), perYear(), or a custom period($start, $end)
  • A HasQuotas Eloquent trait to attach quotas directly to any model
  • Route middleware (quota:exports,50,month) that only consumes the budget on a successful 2xx/3xx response
  • Switchable cache or database backends, configurable per call
  • Atomic consumption via block() to prevent double-spend under concurrency
  • Hard enforcement via enforce(), which throws an HTTP 429 when the budget is exhausted

The Fluent API

A quota is a named counter scoped to an owner, with a limit and a period:

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);

To fail hard instead of branching on the result, call enforce():

Quota::for('api_queries', $user)->limit(1000)->perDay()->enforce();

For work where a double-spend matters, block() wraps the callback in a lock:

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 to any model and the same builder is available directly on the instance:

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();

With the database backend, quota records are polymorphic, so you can query them like any other relation:

$activeQuotas = $user->quotas()
    ->where('period_end', '>', now())
    ->get();

Route Middleware

Apply quota enforcement directly to routes with the quota middleware:

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');

Capacity is checked before the route runs, but the quota is only consumed when the response is 2xx or 3xx. A request that errors or fails validation costs the user nothing.

Storage Backends

The default driver is cache (Redis, Memcached, or any configured store). Switch to database when the count is tied to billing and must survive a cache flush:

Quota::for('api_ping', $ip)->using('cache')->limit(5000)->perDay()->consume();
Quota::for('monthly_exports', $user)->using('database')->limit(50)->perMonth()->consume();

Expired database rows can be pruned on a schedule:

Schedule::command('model:prune', ['--model' => Quota::class])->daily();

Installation

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

Real Takeaways

  • Laravel Quota is a calendar-reset budget system, not a sliding-window rate limiter.
  • The enforce() method returns an HTTP 429 automatically—no manual branching needed.
  • Use block() for atomic consumption when concurrent requests could both pass the capacity check.
  • The database backend is the right choice for billing-critical counters that can't be lost on a cache flush.
  • Route middleware only charges the budget on successful responses, protecting users from being penalized for server errors.
  • Custom backends can be registered via Quota::extend() in a service provider.

Source: Laravel Quota: Usage Budgets for Calendar Periods — Laravel News

Found this useful?

Frequently Asked Questions

3 questions
Q01 How is Laravel Quota different from Laravel's built-in RateLimiter?
Laravel's RateLimiter throttles bursts of traffic using a sliding time window and is typically backed by cache only. Laravel Quota counts cumulative consumption against a named budget that resets on a fixed calendar boundary (e.g., the start of a month), and it can persist counts in the database so they survive cache flushes—making it suitable for billing-tied limits.
Q02 Does the route middleware charge the quota if the request fails?
No. The middleware checks capacity before the route runs but only consumes the quota when the response returns a 2xx or 3xx status code. Requests that result in a 4xx or 5xx response do not count against the user's budget.
Q03 When should I use the database backend instead of the cache backend?
Use the database backend when the quota count is tied to billing or any business-critical limit that must not be lost if the cache is flushed or expires. The cache backend is fine for soft limits where occasional resets are acceptable.

Continue reading

More Articles

View all