grazulex / laravel-api-throttle-smart
Intelligent, plan-aware rate limiting for Laravel APIs - Built for SaaS, multi-tenant, and enterprise applications
Package info
github.com/Grazulex/laravel-api-throttle-smart
pkg:composer/grazulex/laravel-api-throttle-smart
Fund package maintenance!
Requires
- php: ^8.3
- illuminate/cache: ^12.0|^13.0
- illuminate/contracts: ^12.0|^13.0
- illuminate/http: ^12.0|^13.0
- illuminate/redis: ^12.0|^13.0
- illuminate/routing: ^12.0|^13.0
- illuminate/support: ^12.0|^13.0
Requires (Dev)
- larastan/larastan: ^3.4
- laravel/pint: ^1.22
- orchestra/testbench: ^10.0|^11.0
- pestphp/pest: ^3.8|^4.0
- pestphp/pest-plugin-laravel: ^3.2|^4.0
- phpstan/phpstan: ^2.1
- rector/rector: ^2.0
Suggests
None
Provides
None
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-09-18 16:12:52 UTC
README
Intelligent, plan-aware rate limiting for Laravel APIs - Multi-algorithm, multi-tenant, quota management
Tip
What Laravel API Throttle Smart does for you โ Give your Free, Pro and Enterprise customers different API limits and quotas without writing custom rate limiting for each plan. One config, standard headers, and your API stays fair and predictable under load.
This package is free and maintained on my own time. If it saves you hours, a small contribution helps me keep it going: ๐ GitHub Sponsors ยท โ Buy Me a Coffee ยท PayPal
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 12.x or 13.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
planattribute - 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
Support This Package
Laravel API Throttle Smart is free, open source and maintained on my own time. If it saves you hours, here is how you can give back:
- โญ Star the repository โ it helps other developers find it
- ๐ฆ Share it with your team and network
- ๐ Sponsor on GitHub, buy me a coffee or donate via PayPal โ every contribution funds maintenance, new features and Laravel upgrades
License
The MIT License (MIT). Please see License File for more information.
See Also
- laravel-api-idempotency - API idempotency support
- laravel-apiroute - API versioning lifecycle management