Skip to content

Rate limiting

Source URL: https://docs.bullmq.io/guide/rate-limiting

BullMQ provides queue rate limiting. It is possible to configure workers so that they obey a given rate limiting option:

import { Worker, QueueScheduler } from 'bullmq';
const worker = new Worker('painter', async job => paintCar(job), {
limiter: {
max: 10,
duration: 1000,
},
});
const scheduler = new QueueScheduler('painter');

{% hint style=“warning” %} Jobs that get rate limited will actually stay in the waiting state. {% endhint %}

{% hint style=“danger” %} From BullMQ 2.0 and onwards, the QueueScheduler is not needed anymore. {% endhint %}

{% hint style=“info” %} The rate limiter is global, so if you have for example 10 workers for one queue with the above settings, still only 10 jobs will be processed by second. {% endhint %}

{% hint style=“danger” %} From BullMQ 3.0 and onwards, group keys support is removed to improve global rate limit, so the information below is only valid for older versions. {% endhint %}

It is also possible to define a rate limiter based on group keys, for example you may want to have a rate limiter per customer instead of a global rate limiter for all customers:

import { Queue, Worker, QueueScheduler } from 'bullmq';
const queue = new Queue('painter', {
limiter: {
groupKey: 'customerId',
},
});
const worker = new Worker('painter', async job => paintCar(job), {
limiter: {
max: 10,
duration: 1000,
groupKey: 'customerId',
},
});
const scheduler = new QueueScheduler('painter');
// jobs will be rate limited by the value of customerId key:
await queue.add('rate limited paint', { customerId: 'my-customer-id' });

Sometimes is useful to rate-limit a queue manually instead of based on some static options. For example, you may have an API that returns 429 Too Many Requests, and you want to rate-limit the queue based on that response.

For this purpose, you can use the worker method rateLimit like this:

import { Worker } from 'bullmq';
const worker = new Worker(
'myQueue',
async () => {
const [isRateLimited, duration] = await doExternalCall();
if (isRateLimited) {
await worker.rateLimit(duration);
// Do not forget to throw this special exception,
// since we must differentiate this case from a failure
// in order to move the job to wait again.
throw Worker.RateLimitError();
}
},
{
connection,
limiter: {
max: 1,
duration: 500,
},
},
);

{% hint style=“warning” %} Don’t forget to pass limiter options into your worker’s options as limiter.max is used to determine if we need to execute the rate limit validation. {% endhint %}

Sometimes is useful to know if our queue is rate limited.

For this purpose, you can use the getRateLimitTtl method like this:

import { Queue } from 'bullmq';
const queue = new Queue('myQueue', { connection });
const maxJobs = 100;
const ttl = await queue.getRateLimitTtl(maxJobs);
if (ttl > 0) {
console.log('Queue is rate limited');
}

Sometimes is useful to stop a rate limit delay.

For this purpose, you can use the removeRateLimitKey method like this:

import { Queue } from 'bullmq';
const queue = new Queue('myQueue', { connection });
await queue.removeRateLimitKey();

By removing rate limit key, workers will be able to pick jobs again and your rate limit counter is reset to zero.