Rails Sidekiq job that polls an AI video API with perform_in

A Sidekiq worker reads Sume's /v1/jobs/{id}/status once, then reschedules itself with perform_in using next_poll_after_seconds until the job is terminal.

5 min readSume
All posts

In Rails, poll an AI video job by letting a Sidekiq worker read the status once and schedule its own next read with perform_in, instead of sleeping inside perform. Sume's GET /v1/jobs/{id}/status returns terminal, result_ready, sume_status and, while the job is still running, next_poll_after_seconds; the worker obeys that number and falls back to 15 seconds when it is absent (Sume jobs guide, read 2026-10-06).

Sidekiq's perform_in(interval_in_seconds, *args) schedules a job for later, and its scheduler "is not meant to be second-precise": it checks the scheduled set about every 5 seconds by default (Sidekiq wiki, read 2026-10-06). That is fine for a video that renders for minutes.

What does the worker look like?

A /v1/videos job can be read at /v1/jobs/{id}/status as well, so one worker serves every Sume generation endpoint. Video here is your own ActiveRecord model with a sume_job_id column; the attempts counter caps the loop at 120 reads.

require "net/http"
require "json"

class PollVideoJob
  include Sidekiq::Job
  sidekiq_options retry: 5

  def perform(job_id, attempts = 0)
    uri = URI("https://api.sume.com/v1/jobs/#{job_id}/status")
    req = Net::HTTP::Get.new(uri)
    req["Authorization"] = "Bearer #{ENV.fetch('SUME_API_KEY')}"
    res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |h| h.request(req) }
    raise "status #{res.code}" unless res.is_a?(Net::HTTPSuccess)

    s = JSON.parse(res.body)
    if s["terminal"]
      Video.find_by!(sume_job_id: job_id).update!(status: s["sume_status"])
    elsif attempts < 120
      wait = (s["next_poll_after_seconds"] || 15).to_i
      self.class.perform_in(wait, job_id, attempts + 1)
    end
  end
end

How many reads is that for a typical clip?

If a 10 second Seedance 2.5 clip takes 3 minutes and Sume asks for a read every 15 seconds, the worker makes about 12 status reads. A fixed 5 second sleep would make 36. The server-provided delay also grows when the job is queued behind your workspace's concurrency limit, where a fixed short interval just burns reads; for a queued job Sume caps the suggested delay at 30 seconds.

What about retries of perform itself?

sidekiq_options retry: 5 covers transient HTTP failures (the raise on a non-2xx response). It does not re-submit anything: the worker only reads, so a retry can never bill a second video. Keep the paid POST /v1/videos in a different job and send an Idempotency-Key there. Do not treat a Sidekiq retry exhaustion as a failed render; the job keeps running at Sume, so schedule a fresh read or check GET /v1/jobs/{id}/events before you tell a customer anything.

Sources

Related posts

More in Developers

All Developers posts

Written by Sume