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.

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
endHow 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
- Rails Active Job that polls an AI video API: retry_job and wait
An Active Job on Solid Queue reads Sume's job status once and calls retry_job with wait from next_poll_after_seconds until the video job is terminal.
- Read Sume TTS raw PCM in Python: f32le samples at 24 kHz
Ask the TTS Router for container raw, 24000 Hz and pcm_f32le, then load the samples with the standard library for DSP or a custom player.
- Record the Idempotency-Key before you POST a Sume video job
A worker that dies after submitting a video job loses the job id. Write the key to a ledger first, then POST; a retry returns the same job. Tested SQLite code.
- Redact sume_live_ API keys from Python logs with a handler filter
A logging.Filter on a logger skips records from child loggers. Put the redaction filter on the handler so a sume_live_ key never reaches the log file.
Written by Sume