A Go client for Sume from OpenAPI, with Retry-After

The docs list only a TypeScript SDK. Generate a Go client from the live OpenAPI schema, send x-api-key, and back off on 429 with retry-after.

4 min readSume
All posts

The Sume docs list a TypeScript SDK and no Go package. The docs name the live OpenAPI schema at https://api.sume.com/reference/json as the source of truth, so a Go client is a generate step plus a thin retry wrapper. OpenAI's Go SDK reached v3.71.1 and v3.71.0 on Oct 2, 2026 (v3.70.0 on Oct 1), which is a reminder that vendor SDKs move fast and a generated client lets you pin your own.

Generate from the schema

The official TypeScript SDK is @sume-com/sdk. For other languages the docs point at the OpenAPI schema. Fetch it, run the Go generator you already use, and commit the output. Regenerate when the schema changes rather than editing generated files.

curl -fsS https://api.sume.com/reference/json -o sume-openapi.json

Send one credential

Sume accepts either Authorization: Bearer or x-api-key, but rejects a request that carries both with 401 unauthorized. The TypeScript client sends x-api-key only; do the same in Go and make sure no proxy layer adds an Authorization header.

Back off on 429 with one idempotency key

On 429 the API sends retry-after in seconds. Reuse the same Idempotency-Key on each retry so a replay returns the original job rather than billing twice. Reads have a separate, larger budget than writes, and the response also carries ratelimit-remaining.

Poll and fetch with the generated client

Keep two budgets in mind. A 429 on submit spends from the write budget, while polling GET /v1/jobs/{id}/status draws on a separate read budget that is forty times larger by default. Poll with exponential backoff and stop on completed, failed or canceled. Never resubmit a paid job because a local timeout fired; resubmit only with the same idempotency key.

After submit, the response carries status_url and result_url. Wrap those two reads as ordinary typed calls from the generated client and keep the retry logic in one place, an http.RoundTripper or a small helper, so every call shares the same rules.

This standalone program submits one Video Router job and retries only on 429:

package main

import ("bytes"; "fmt"; "io"; "net/http"; "os"; "strconv"; "time")

func main() {
	key := os.Getenv("SUME_API_KEY")
	if key == "" {
		panic("SUME_API_KEY is empty")
	}
	body := []byte(`{"model":"seedance-2.5","prompt":"A product clip on a desk","duration":8,"resolution":"720p","aspect_ratio":"9:16","mode":"async"}`)
	for attempt := 0; attempt < 5; attempt++ {
		req, _ := http.NewRequest("POST", "https://api.sume.com/v1/video-router/generate", bytes.NewReader(body))
		req.Header.Set("x-api-key", key)
		req.Header.Set("Content-Type", "application/json")
		req.Header.Set("Idempotency-Key", "go-demo-001")
		res, err := http.DefaultClient.Do(req)
		if err != nil { panic(err) }
		out, _ := io.ReadAll(res.Body)
		res.Body.Close()
		if res.StatusCode != 429 { fmt.Println(res.StatusCode, string(out)); return }
		wait, err := strconv.Atoi(res.Header.Get("Retry-After"))
		if err != nil || wait < 1 { wait = 1 << attempt }
		time.Sleep(time.Duration(wait) * time.Second)
	}
}

Sources

Related posts

More in Developers

All Developers posts

Written by Sume