Go 1.27 drains response bodies: a Sume job poll loop
Go 1.27 drains unread HTTP/1 body bytes on Close. A stdlib loop that polls GET /v1/jobs/:id/status and honors next_poll_after_seconds.

Short answer
Poll GET /v1/jobs/:id/status in a plain net/http loop, always decode or close the body, sleep for next_poll_after_seconds, and stop when terminal is true. The Go 1.27 release notes say an HTTP/1 Response.Body now drains unread content when it is closed, up to a conservative limit, to allow better connection reuse, so a loop that closes every response keeps its connection warm.
That matters for polling because a video job can take minutes and the loop issues hundreds of tiny GETs. Sume's jobs and results guide describes the contract: every submit returns a durable job id, async is the default mode, and the status endpoint tells you when to look again.
What changed in Go 1.27 that touches a poll client
The release notes page also lists server-side items. They are worth knowing if the same binary both polls Sume and receives webhooks, but only the body-drain note changes client behavior.
| Item | What the notes say |
|---|---|
| HTTP/1 Response.Body | drains unread content on Close, up to a conservative limit, for better connection reuse |
| HTTP/2 server priority | server accepts client priority signals per RFC 9218; Server.DisableClientPriority restores round-robin |
| Server.MaxHeaderValueCount | new field that caps how many header values a server accepts |
| testing/synctest.Sleep | new helper combining time.Sleep and synctest.Wait |
The envelope the loop reads
Every status read returns a data object. The loop below needs only four fields from it, and it never builds a URL by hand beyond the job id you were given at submit time.
Sume documents the same loop as pseudocode: poll, call a progress callback, stop on terminal, then fetch the result only when the job completed. A 409 job_not_completed from the result route means you asked too early; read the failure off the job record instead.
| Field | What it tells the loop |
|---|---|
| terminal | true once the job is completed, failed or canceled; stop polling |
| result_ready | true when GET /v1/jobs/:id/result will return the result |
| sume_status | queued, processing, completed, failed or canceled |
| next_poll_after_seconds | how long to wait before the next read; null once terminal |
| status_url, result_url, events_url, cancel_url | absolute URLs the envelope hands back, so you never build paths by hand |
A stdlib poll loop
Save the file as poll.go and run SUME_API_KEY=... go run poll.go job_123. The key goes in Authorization: Bearer, which the docs use in every curl example. The loop reads from the read budget, which on the Free plan is 4,800 requests per minute against 120 writes, so a two-second cadence is nowhere near it.
package main
import ("encoding/json"; "fmt"; "net/http"; "os"; "time")
func main() {
url := "https://api.sume.com/v1/jobs/" + os.Args[1] + "/status"
for {
req, _ := http.NewRequest("GET", url, nil)
req.Header.Set("Authorization", "Bearer "+os.Getenv("SUME_API_KEY"))
res, err := http.DefaultClient.Do(req)
if err != nil { fmt.Println(err); os.Exit(1) }
var s struct{ Data struct {
Terminal bool `json:"terminal"`
Status string `json:"sume_status"`
Next *float64 `json:"next_poll_after_seconds"`
} }
err = json.NewDecoder(res.Body).Decode(&s)
res.Body.Close()
if err != nil || res.StatusCode != 200 { fmt.Println("status", res.StatusCode, err); os.Exit(1) }
if s.Data.Terminal { fmt.Println(s.Data.Status); return }
wait := 2.0
if s.Data.Next != nil { wait = *s.Data.Next }
time.Sleep(time.Duration(wait * float64(time.Second)))
}
}What Sume does and does not do
Sume gives every submit a job id in the first response, whichever mode you chose, and keeps the job running if your process dies. It does not push progress: the Developer API has no SSE or WebSocket transport, and GET /v1/jobs/:id/events is a pull snapshot.
A client-side timeout does not cancel anything. If your deadline passes, the job still runs and still bills; store the id and resume from status_url, or call POST /v1/jobs/:id/cancel, which only succeeds before generation starts and otherwise answers 409 job_generation_already_started.
- Never resubmit a paid request because the poll loop timed out; retry the submit only with the same
Idempotency-Key. - Use
webhookmode when you would rather be told, and keep this loop as the backup.
Sources
Related posts
More in Developers
- Go http.Client Timeout covers the body: Sume job poll
Go's Client.Timeout includes redirects and reading the body and keeps running after Do returns. Use it per poll and put the job deadline on a context.
- Go webhook handler: verify the Sume sume-v1 signature
A stdlib Go verifier for x-sume-webhook-signature: HMAC-SHA256 over timestamp.raw_body, five-minute window, constant-time compare, empty secret refused.
- Google Images formats and filenames for Sume output
Google Search supports BMP, GIF, JPEG, PNG, WebP, SVG and AVIF in img src, and wants short filenames and real alt text. Rename Sume downloads first.
- Google Play app icon: 512x512 32-bit PNG with alpha, 1024 KB
Play wants a 512 x 512, 32-bit PNG with alpha, at most 1024 KB. Get a transparent square from ChatGPT Image 2.5 on Sume, then resize and check the size.
Written by Sume