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.
- Scan your repo for retiring Google image and TTS ids (Python)
A 30-line Python scanner that finds Imagen 4, Nano Banana and Gemini TTS preview ids in your files and prints the replacement Google names for each one.
- Seedance 2.5 estimate vs usage.cost: reconcile a job in Node
Compute the Seedance 2.5 price from width, height and seconds, submit one job to Sume, and compare it with usage.cost on the poll. A runnable Node 18 script.
- Semantic Kernel .NET defaults to gpt-image-1: it fails Oct 23
The .NET OpenAI connector in Semantic Kernel defaults to gpt-image-1, which OpenAI removes Oct 23, 2026. What breaks, and a C# call to Sume that does not.
Written by Sume