Go: poll a Sume job and stop on any terminal state, even a new one
A Go status loop that names completed, failed and canceled, stops on terminal=true for anything else, and never sleeps less than next_poll_after_seconds.

Switch on sume_status for the three outcomes you handle, then add a second branch on terminal for any status your code has never seen. Sume documents five job statuses (queued, processing, completed, failed, canceled), and its docs say to stop polling on the booleans or on sume_status. Doing both means a status added later ends the loop instead of spinning it forever.
What the status call returns
GET /v1/jobs/{id}/status wraps its fields in data. The ones this loop reads are sume_status, terminal and next_poll_after_seconds. The response also has a queue-shaped status field (IN_QUEUE, IN_PROGRESS, COMPLETED, FAILED, CANCELED) that agrees with sume_status; the docs say not to mix the two, so the loop reads only one.
| sume_status | Terminal | Loop action |
|---|---|---|
| queued | No | Sleep, poll again |
| processing | No | Sleep, poll again |
| completed | Yes | Fetch the result |
| failed | Yes | Read the job's error, do not resubmit blindly |
| canceled | Yes | Stop |
The loop
Run it as go run poll.go job_123 with SUME_BASE=https://api.sume.com and SUME_API_KEY set. The sleep is the larger of 2 seconds and the server hint, because the SDK treats next_poll_after_seconds as a minimum that carries queue backoff the client cannot see.
package main
import ("encoding/json"; "fmt"; "net/http"; "os"; "time")
type snap struct{ Data struct {
Status string `json:"sume_status"`
Terminal bool `json:"terminal"`
Next float64 `json:"next_poll_after_seconds"`
} }
func main() {
for {
req, _ := http.NewRequest("GET", os.Getenv("SUME_BASE")+"/v1/jobs/"+os.Args[1]+"/status", nil)
req.Header.Set("Authorization", "Bearer "+os.Getenv("SUME_API_KEY"))
res, err := http.DefaultClient.Do(req)
if err != nil || res.StatusCode != 200 {
fmt.Println("status read failed; the job is untouched:", err)
return
}
var s snap
json.NewDecoder(res.Body).Decode(&s)
res.Body.Close()
switch {
case s.Data.Status == "completed", s.Data.Status == "failed", s.Data.Status == "canceled":
fmt.Println("done:", s.Data.Status)
return
case s.Data.Terminal: // a state this switch has never seen: stop anyway
fmt.Println("terminal, unrecognized:", s.Data.Status)
return
}
time.Sleep(max(2*time.Second, time.Duration(s.Data.Next*float64(time.Second))))
}
}What it leaves out
It does not fetch the result. When the status is completed, call GET /v1/jobs/{id}/result, which answers 409 job_not_completed for any other state. It does not retry the read on a 429 either; a failed status read prints and returns, and the job keeps running. A local give-up never cancels a job, and it never justifies submitting the paid request again.
It also has no deadline. Put one around the loop (a context works) so a stuck job cannot hold a goroutine for ever.
Sources
Related posts
More in Developers
- gpt-image-1.5 is removed Dec 1: a 55-day cutover calendar
OpenAI removes gpt-image-1.5, gpt-image-1-mini and chatgpt-image-latest on December 1, 2026. A week-by-week plan from October 7 to move to GPT Image 2.5.
- GPT Image 2.5 banner: 3840x1280 passes the 3:1 rule
3840x1280 is exactly 3:1, both edges are multiples of 16 and it is 4,915,200 pixels, so Sume accepts it on GPT Image 2.5. The four checks shown.
- Handle every Sume API error with one switch on next_action
Sume errors share one envelope. Branch on next_action, retryable and retry_after_seconds, and your client handles new codes without a code change. JS sample.
- HappyHorse 1.1's five aspect ratios vs the Sume video catalog
HappyHorse 1.1 on Cloudflare takes 16:9, 9:16, 1:1, 4:3 and 3:4. Which Sume video models cover all five, and which do not.
Written by Sume