Ruby Thread and Queue: four workers submit Wan 3.0 clips
Ruby worker pool for POST /v1/videos: a Queue of shots, four Threads, Net::HTTP and one Idempotency-Key per shot. Tested on Ruby with a stub server.

Ruby has no built-in pool class, but Queue plus a handful of Threads is all a submit loop needs. Put the shot numbers on a queue, start four threads that each pop until the queue is empty, and collect [shot, status] pairs on a second queue. Queue is thread-safe, so no lock is required.
This sample submits 12 five-second Wan 3.0 clips at 480p. It uses only net/http, json and uri, and it was run against a local stand-in server that answers 202, not the live API. Set SUME_BASE (for example https://api.sume.com) and SUME_API_KEY.
The code (22 lines)
jobs.pop(true) is the non-blocking form. It raises ThreadError on an empty queue, which the rescue nil turns into the loop's exit condition. Without the true argument the thread would block forever once the queue drains.
require "net/http"
require "json"
require "uri"
uri = URI("#{ENV.fetch('SUME_BASE')}/v1/videos")
jobs = Queue.new
(0...12).each { |i| jobs << i }
results = Queue.new
workers = Array.new(4) do # 4 threads = Pro processing concurrency
Thread.new do
until (i = (jobs.pop(true) rescue nil)).nil?
req = Net::HTTP::Post.new(uri, "x-api-key" => ENV.fetch("SUME_API_KEY"),
"content-type" => "application/json",
"Idempotency-Key" => format("trailer-v3-shot-%02d", i))
req.body = { model: "wan-3.0", prompt: "shot #{i}", duration: 5, resolution: "480p" }.to_json
res = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == "https") { |h| h.request(req) }
results << [i, res.code.to_i]
end
end
end
workers.each(&:join)
puts Array.new(results.size) { results.pop }.sort.map(&:last).inspectIdempotency per shot
The key trailer-v3-shot-07 is built from the shot number and nothing random. Sume documents that an Idempotency-Key replay returns the original job, so a crashed run can simply be restarted. A key reused with a different body is a conflict (409 idempotency_conflict), so change the key if you change the prompt or settings.
Four threads, not forty
Each plan has a processing concurrency (Free 1, Pro 4, Startup 8, Scale 20) and a larger accepted capacity (6, 24, 48, 120). More threads than processing seats just move jobs from your queue into Sume's, and past the accepted capacity you get 429 queue_full. Four is the Pro number and a safe default.
A 429 or 503 in the printed list means that shot needs another attempt with the same key. Retrying a POST without a key is the one thing to avoid.
Sources
Related posts
More in Developers
- Rust reqwest: POST /v1/images on Sume and branch on 200 or 202
Call Sume's image API from Rust with reqwest::blocking and serde_json; one 40-second client timeout and a match on 200, 202 and errors.
- Retry a timed-out video submit without paying twice: Idempotency-Key
Your client timed out after submitting a 30-second video job? Retry with the same Idempotency-Key and Sume returns the original job, not a second bill.
- Save a finished Seedance clip to your own storage, not the result URL
When a Seedance 2.5 or Wan 3.0 job completes, read /v1/jobs/{id}/result, take the artifact url and content_type, and copy the file into your own storage.
- Save a sidecar JSON with every Sume mp4: job id, model, cost, prompt
Export finished video jobs with a Python script that downloads each mp4 and writes a sidecar JSON with job id, model, cost and the prompt you saved.
Written by Sume