Laravel cookbook · Intermediate

Respect third-party API rate limits in queued jobs

A burst of jobs hits a partner API's rate limit and most of them fail.

Create a free account to save this page, take notes and track your progress. Sign in

Approach

Define a named rate limiter and attach the RateLimited job middleware; jobs over the limit are released back onto the queue instead of failing. Combine with retryUntil so jobs keep trying for a bounded time.

Solution

<?php
// app/Providers/AppServiceProvider.php (boot)
use Illuminate\Cache\RateLimiting\Limit;
use Illuminate\Support\Facades\RateLimiter;

RateLimiter::for('crm-api', fn (object $job) => Limit::perMinute(60));
<?php
// app/Jobs/PushContactToCrm.php
namespace App\Jobs;

use App\Models\Contact;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Queue\Queueable;
use Illuminate\Queue\Middleware\RateLimited;
use Illuminate\Support\Facades\Http;

class PushContactToCrm implements ShouldQueue
{
    use Queueable;

    public function __construct(public Contact $contact) {}

    public function middleware(): array
    {
        return [new RateLimited('crm-api')];
    }

    public function retryUntil(): \DateTimeInterface
    {
        return now()->addHours(2);
    }

    public function handle(): void
    {
        Http::withToken(config('services.crm.token'))
            ->timeout(10)
            ->put("https://crm.example.com/contacts/{$this->contact->external_id}", [
                'email' => $this->contact->email,
                'name' => $this->contact->name,
            ])
            ->throw();
    }
}

Pitfalls

  • Using $tries with rate limiting; every release counts as an attempt, so jobs fail quickly. Use retryUntil.
  • A cache store that isn't shared between workers, so each worker has its own limit.
  • Rate limiting per job class when the partner limits per account; key the limiter by account.

Variations

  • Return Limit::perMinute(60)->by($job->contact->account_id) for per-tenant limits.
  • Use RateLimitedWithRedis for better performance on Redis.

Further reading: Job rate limiting

Reference: https://laravel.com/docs/13.x/queues#rate-limiting

More Laravel recipes

Was this page helpful?