BullMQ delayed job that polls an AI video job and reschedules itself

A BullMQ worker reads Sume's job status once, then adds the next poll with a delay from next_poll_after_seconds, so no worker slot is held while a clip renders.

5 min readSume
All posts

In BullMQ, poll an AI video render with a delayed job that re-adds itself, not with a setTimeout inside the processor. The processor reads Sume's GET /v1/jobs/{id}/status once, and if terminal is false it calls queue.add('poll', data, { delay }) with the number of seconds from next_poll_after_seconds (Sume jobs guide, read 2026-10-06). Worker concurrency then limits how many reads run at once, not how many videos you can have in flight.

The example polls a Wan 3.0 clip. At 720p, Sume's Wan 3.0 rate is $0.125 per second, so a 10 second clip is $1.25.

What does the worker look like?

Run it as an ES module with Node 20 or later and SUME_API_KEY set. The attempts counter caps the loop at 120 reads.

import { Queue, Worker } from 'bullmq';

const connection = { host: '127.0.0.1', port: 6379 };
const queue = new Queue('sume-poll', { connection });
const headers = { Authorization: `Bearer ${process.env.SUME_API_KEY}` };

export const track = (jobId) =>
  queue.add('poll', { jobId, n: 0 }, { delay: 15_000, removeOnComplete: true });

new Worker('sume-poll', async ({ data }) => {
  const res = await fetch(
    `https://api.sume.com/v1/jobs/${data.jobId}/status`, { headers });
  if (!res.ok) throw new Error(`status ${res.status}`); // BullMQ retries
  const s = await res.json();
  if (s.terminal) {
    console.log(data.jobId, s.sume_status);
    return;
  }
  if (data.n < 120) {
    const wait = (s.next_poll_after_seconds ?? 15) * 1000;
    await queue.add('poll', { ...data, n: data.n + 1 },
      { delay: wait, removeOnComplete: true });
  }
}, { connection, concurrency: 5 });

Which status fields should the processor trust?

Fields on Sume's job status response, from the Sume jobs guide (read 2026-10-06)
FieldUse in the processor
terminalStop re-adding the poll job when true
sume_statuscompleted, failed or canceled; store it
result_readyTrue when the output can be fetched
next_poll_after_secondsDelay for the next poll; absent on a terminal job

Does a retry of the poller bill a second clip?

No, as long as the poller only reads. Keep the paid POST /v1/videos in a separate step with an Idempotency-Key you own, and let the poller use GET /v1/jobs/{id}/status. A client-side give-up never cancels the render; Sume's jobs guide says the job keeps running, so store the job id and read it again later instead of submitting a second time.

Sources

Related posts

More in Developers

All Developers posts

Written by Sume