Go: net/http client for a 30-second Wan 3.0 clip, 30 lines
A 30-line Go program using only the standard library: submit wan-3.0 for 30 seconds, poll the job, write clip.mp4. Reserve and per-second rate included.

A Go client for a 30-second Wan 3.0 clip fits in 30 lines of standard library: net/http for three calls and encoding/json to read id and status. The program below requests 16:9 at 720p, which Sume reserves at $3.75 ($0.125 per second).
main.go
It passes go vet as written. One call helper sets the bearer token and sends the optional idempotency key; it panics on any status of 300 or above so a failed submit is never read as a job.
package main
import ("encoding/json"; "io"; "net/http"; "os"; "strings"; "time")
func call(method, url, payload, idem string) []byte {
req, _ := http.NewRequest(method, url, strings.NewReader(payload))
req.Header.Set("Authorization", "Bearer "+os.Getenv("SUME_API_KEY"))
req.Header.Set("Content-Type", "application/json")
if idem != "" {
req.Header.Set("Idempotency-Key", idem)
}
res, err := http.DefaultClient.Do(req)
if err != nil || res.StatusCode >= 300 {
panic("request failed")
}
out, _ := io.ReadAll(res.Body)
return out
}
func main() {
const base = "https://api.sume.com/v1/videos"
var job struct{ ID, Status string }
json.Unmarshal(call("POST", base, `{"model":"wan-3.0","prompt":"A drone-style pass over a harbor at dawn",
"duration":30,"resolution":"720p","aspect_ratio":"16:9"}`, "wan-go-001"), &job)
for job.Status == "pending" || job.Status == "in_progress" {
time.Sleep(5 * time.Second)
json.Unmarshal(call("GET", base+"/"+job.ID, "", ""), &job)
}
if job.Status != "completed" { panic("job ended as " + job.Status) }
os.WriteFile("clip.mp4", call("GET", base+"/"+job.ID+"/content?index=0", "", ""), 0o644)
}Design choices
struct{ ID, Status string }matches the JSON keysidandstatusbecause Go's decoder compares field names without regard to case.- Only the POST carries an
Idempotency-Key; if the process dies between submit and poll, rerun with the same key and you get the same job back instead of a second reserve. - The loop ends on any status except
pendingandin_progress, then the program checks forcompleted.
What the same call costs elsewhere in the ladder
Only the resolution string changes the price:
| resolution | 30-second clip | Clips per $20 balance |
|---|---|---|
| 480p | $1.875 | 10 |
| 720p | $3.75 | 5 |
| 1080p | $7.50 | 2 |
Running several at once
Goroutines make it easy to fire ten submits together. Sume's plan limits decide how many jobs are accepted at once, so a burst can meet 429 queue_full while the balance is fine. Cap the number of in-flight submits with a buffered channel, give every clip its own Idempotency-Key, and treat a 429 as a reason to wait and retry the same key.
The balance still has to cover the sum of the holds: ten 720p clips need $37.50 available, and the shortfall check happens per submit.
Next step
Put the call behind a worker that records each job id and its usage.cost once the job completes, and set a client timeout suited to your environment. For a batch, give every clip its own idempotency key and submit in small groups so a 429 only delays the tail.
Sources
Related posts
More in Developers
- Go net/http: a Sume video submit retry loop with one Idempotency-Key
A 30-line Go function using net/http that retries a Sume video submit on 429 and 503, reads Retry-After, and reuses one Idempotency-Key. Run on a stub.
- Google made Omni Flash its default video model: pin it or sume/auto?
Google's docs now say to use Gemini Omni Flash as the default video model. On Sume you can pin gemini-omni-flash-1.1 or send sume/auto. What each one fixes.
- Google says Imagen is shut down: test your Imagen call on Sume now
Google's Imagen page says Imagen models are shut down. Sume lists Imagen 4 Fast and Ultra separately. One catalog check and one request tell you if yours works.
- GPT Image 2.5 inpainting with mask_url on Sume: steps and cost
Edit part of an image with mask_url and up to 16 references on openai/gpt-image-2.5. Billed about $0.066 an image on Sume. Steps and the limits.
Written by Sume