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.

4 min readSume
All posts

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 job statuses and the loop's action (Sume docs, read 2026-10-07)
sume_statusTerminalLoop action
queuedNoSleep, poll again
processingNoSleep, poll again
completedYesFetch the result
failedYesRead the job's error, do not resubmit blindly
canceledYesStop

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

All Developers posts

Written by Sume