grazulex/laravel-api-throttle-smart

Intelligent, plan-aware rate limiting for Laravel APIs - Built for SaaS, multi-tenant, and enterprise applications

Fund package maintenance!
Grazulex

Installs: 0

Dependents: 0

Suggesters: 0

Security: 0

Stars: 0

Watchers: 0

Forks: 0

pkg:composer/grazulex/laravel-api-throttle-smart

v0.1.0 2026-02-04 23:24 UTC

This package is auto-updated.

Last update: 2026-02-04 23:26:25 UTC


README

Intelligent, plan-aware rate limiting for Laravel APIs - Multi-algorithm, multi-tenant, quota management

Latest Version on Packagist Tests Total Downloads License

Features

  • Plan-Based Limits - Define different rate limits per subscription plan (Free, Pro, Enterprise)
  • Multiple Algorithms - Fixed Window, Sliding Window, or Token Bucket
  • Multi-Window Limits - Per-second, minute, hour, day, and month limits
  • Quota Management - Daily/monthly API quota with alerts and top-ups
  • Multi-Tenant Scoping - Scope by user, team, tenant, or IP
  • Multiple Storage Drivers - Cache, Redis, or Database
  • RFC 7231 Compliant - Standard rate limit headers
  • Burst Handling - Token bucket algorithm for controlled bursts
  • Artisan Commands - Status, analytics, cleanup, and management tools
  • Testing Helpers - Fluent testing API with ThrottleSmartFake

Requirements

  • PHP 8.3+
  • Laravel 11.x or 12.x

Installation

composer require grazulex/laravel-api-throttle-smart

Publish the configuration:

php artisan vendor:publish --tag="throttle-smart-config"

For database driver, publish migrations:

php artisan vendor:publish --tag="throttle-smart-migrations"
php artisan migrate

Quick Start

Apply the middleware to routes:

// routes/api.php
Route::middleware(['auth:sanctum', 'throttle.smart'])->group(function () {
    Route::get('/users', [UserController::class, 'index']);
    Route::post('/orders', [OrderController::class, 'store']);
});

The middleware automatically:

  • Resolves the user's plan from the plan attribute
  • Applies plan-specific rate limits
  • Adds standard rate limit headers
  • Returns 429 when limits exceeded

Response Headers

HTTP/1.1 200 OK
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 58
X-RateLimit-Reset: 1706835600
X-RateLimit-Policy: 60;w=60
X-RateLimit-Plan: pro
X-Quota-Limit: 1000000
X-Quota-Remaining: 999542
X-Quota-Reset: 1709251200

When rate limited:

HTTP/1.1 429 Too Many Requests
Retry-After: 45
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1706835600

Plan Configuration

// config/throttle-smart.php
'plans' => [
    'free' => [
        'label' => 'Free Plan',
        'requests_per_minute' => 60,
        'requests_per_hour' => 500,
        'requests_per_day' => 5000,
        'requests_per_month' => 100000,
        'burst_size' => 10,
        'burst_refill_rate' => 1,
    ],

    'pro' => [
        'label' => 'Pro Plan',
        'requests_per_minute' => 300,
        'requests_per_hour' => 5000,
        'requests_per_day' => 50000,
        'requests_per_month' => 1000000,
        'burst_size' => 50,
        'burst_refill_rate' => 5,
    ],

    'enterprise' => [
        'label' => 'Enterprise Plan',
        'requests_per_minute' => 1000,
        'requests_per_hour' => 20000,
        'requests_per_day' => 200000,
        'requests_per_month' => null, // Unlimited
        'burst_size' => 200,
        'burst_refill_rate' => 20,
    ],
],

PHP Attributes

use Grazulex\ThrottleSmart\Attributes\RateLimit;
use Grazulex\ThrottleSmart\Attributes\QuotaCost;

class ApiController extends Controller
{
    #[RateLimit(perMinute: 10, perHour: 100)]
    public function sensitiveEndpoint(Request $request)
    {
        // Custom limits for this endpoint
    }

    #[QuotaCost(5)]
    public function expensiveOperation(Request $request)
    {
        // Costs 5 quota units instead of 1
    }
}

Programmatic Usage

use Grazulex\ThrottleSmart\Facades\ThrottleSmart;

// Get rate limits for a user
$limits = ThrottleSmart::getLimits($user);
$limits->minute['remaining']; // 58
$limits->isLimited; // false

// Get quota information
$quota = ThrottleSmart::getQuota($user);
$quota->monthly['remaining']; // 999542
$quota->percentageUsed; // 0.05

// Check without consuming
if (ThrottleSmart::wouldLimit($request)) {
    return response()->json(['message' => 'Please slow down'], 429);
}

// Manually consume quota
ThrottleSmart::consume(5); // Consume 5 units

// Reset limits for a user
ThrottleSmart::reset("user:{$user->id}");

// Grant bonus quota
ThrottleSmart::addQuota($user, 10000, 'Customer support bonus');

Rate Limiting Algorithms

Fixed Window (Default)

Simple counter-based limiting with discrete time windows.

Sliding Window

Weighted average across windows for smoother rate limiting.

'sliding_window' => [
    'enabled' => true,
    'precision' => 1,
],

Token Bucket

Allows controlled bursts while maintaining average rate.

'token_bucket' => [
    'enabled' => true,
    'initial_tokens' => null, // Start with full bucket
],

Artisan Commands

# View rate limit status
php artisan throttle:status

# Check specific user
php artisan throttle:user --user=123

# View analytics
php artisan throttle:analytics --period=day

# Reset user limits
php artisan throttle:reset --user=123

# Reset user quota
php artisan throttle:reset-quota --user=123

# Grant bonus quota
php artisan throttle:grant-quota --user=123 --amount=10000 --reason="Support"

# Cleanup old data
php artisan throttle:cleanup --days=90

Events

use Grazulex\ThrottleSmart\Events\RateLimitExceeded;
use Grazulex\ThrottleSmart\Events\RateLimitApproaching;
use Grazulex\ThrottleSmart\Events\QuotaExceeded;
use Grazulex\ThrottleSmart\Events\QuotaThresholdReached;

// In EventServiceProvider
protected $listen = [
    RateLimitExceeded::class => [
        SendRateLimitNotification::class,
    ],
    QuotaThresholdReached::class => [
        SendQuotaWarningEmail::class,
    ],
];

Testing

use Grazulex\ThrottleSmart\Facades\ThrottleSmart;

public function test_rate_limiting(): void
{
    ThrottleSmart::fake();

    // Make requests...

    ThrottleSmart::assertLimitExceeded('user:123');
    ThrottleSmart::assertNotLimited('user:456');
    ThrottleSmart::assertQuotaConsumed(150);
}

Integration testing:

public function test_api_is_rate_limited(): void
{
    $user = User::factory()->create(['plan' => 'free']);

    // Make 61 requests (free plan allows 60/min)
    for ($i = 0; $i < 61; $i++) {
        $response = $this->actingAs($user)
            ->getJson('/api/users');
    }

    $response->assertStatus(429)
        ->assertHeader('X-RateLimit-Remaining', '0')
        ->assertJsonPath('error.code', 'RATE_LIMIT_EXCEEDED');
}

Quality Tools

# Run tests
composer test

# Code style (Laravel Pint)
composer pint

# Static analysis (PHPStan Level 5)
composer analyse

Error Responses

Code Status Description
RATE_LIMIT_EXCEEDED 429 Rate limit exceeded for time window
QUOTA_EXCEEDED 429 Monthly/daily quota exceeded

Changelog

Please see CHANGELOG for more information on what has changed recently.

Contributing

Please see CONTRIBUTING for details.

Security

If you discover any security-related issues, please email security@grazulex.dev instead of using the issue tracker.

Credits

License

The MIT License (MIT). Please see License File for more information.

See Also