Rate Limiting Background Jobs: Protecting APIs and Scaling Laravel
Learn how to apply rate limiting to Laravel background jobs to protect third-party APIs and manage resource contention using Redis-based throttlers.
Previously in this course, we discussed Queue Worker Prioritization to organize our job execution flow. While prioritization ensures critical tasks run first, it doesn't solve the problem of overwhelming external services. In this lesson, we shift our focus to Rate Limiting background jobs to prevent hitting API exhaustion limits and to maintain system stability under load.
Rate Limiting Jobs from First Principles
In a distributed environment, you often push thousands of jobs to the queue that interact with external services (e.g., Stripe, SendGrid, or internal microservices). Without throttling, your workers will blast these services with requests, leading to 429 Too Many Requests errors.
Laravel provides a native, Redis-backed mechanism to throttle jobs using the Illuminate\Queue\Middleware\RateLimited middleware. This middleware uses the RateLimiter facade under the hood to ensure that only a specific number of jobs execute within a defined time window.
Implementing Job Rate Limiting
To apply rate limiting, you add the RateLimited middleware to your job's middleware() method. This requires a Redis connection, as it relies on atomic increments to track execution counts.
The Worked Example: Throttling a Sync Job
Imagine we are building a SaaS platform that syncs user data to a third-party CRM. The CRM allows only 10 requests per minute.
PHPnamespace App\Jobs; use Illuminate\Bus\Queueable; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Foundation\Bus\Dispatchable; use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\Middleware\RateLimited; use Illuminate\Support\Facades\Redis; class SyncUserToCrm implements ShouldQueue { use Dispatchable, InteractsWithQueue, Queueable; public function __construct(public int $userId) {} public function middleware(): array { return [ (new RateLimited('crm-sync-limiter')), ]; } public function handle(): void { #6A9955">// Integration logic here } }
Next, define the rate limit in your AppServiceProvider (or a dedicated RateLimiterServiceProvider):
PHPuse Illuminate\Cache\RateLimiting\Limit; use Illuminate\Support\Facades\RateLimiter; public function boot(): void { RateLimiter::for('crm-sync-limiter', function ($job) { return Limit::perMinute(10)->by('crm-api'); }); }
Handling Rate Limit Exceptions
When a job is throttled, it isn't discarded. By default, the middleware releases the job back onto the queue with a delay, effectively rescheduling it. However, you can control the release behavior or handle failures explicitly by overriding shouldBeReleased or catching the throttled state.
If you need to perform a specific action when a job is hitting the limit—like alerting your team via Slack—you can hook into the failed method or use the whenThrottled callback logic within the middleware.
Hands-on Exercise
- Create a dummy job that simulates an external API call using
sleep(1). - Apply a rate limit of 2 jobs per minute to this job.
- Dispatch 5 instances of this job simultaneously.
- Observe the logs: you should see the first two process immediately, while the others are released back to the queue to wait for the next window.
Common Pitfalls
- Forgetting Redis: The
RateLimitedmiddleware requires therediscache driver. If you usefileorarraydrivers, the rate limiting will not be distributed across multiple workers, rendering it ineffective in production environments. - Global vs. Specific Limiters: Using a single key for all users can lead to "noisy neighbor" issues. Always use a dynamic key (e.g.,
->by($job->tenantId)) if your rate limits are per-customer rather than per-API-endpoint. - Infinite Loops: If your rate limit is set too low and your queue is backed up, you may end up with a "queue clog" where jobs are constantly being released and re-queued, consuming CPU cycles without making progress. Ensure your
retryAfterlogic is sane.
Summary
By implementing Rate Limiting for your Jobs, you create a robust buffer between your infrastructure and external dependencies. This practice ensures your Throttling strategies are declarative and maintainable, preventing cascading failures in your SaaS architecture.
Up next: Event-Driven Architecture where we will decouple our modules by emitting domain events instead of triggering side effects directly within our jobs.
Work with me

Laravel REST API Development
Clean, secure, well-documented Laravel REST APIs — the backend engine for your app, mobile client, or SaaS. Built by an API specialist.

FilamentPHP Admin Panel & Dashboard Development
A powerful admin panel for your Laravel app — built with FilamentPHP so you can manage everything without touching the database.