Retry an avatar video submit without paying twice on Sume

Use one Idempotency-Key per paid avatar video submit and poll the job instead of resubmitting after a timeout. A Python example and the rules from Sume docs.

5 min readSume
All posts

Send an Idempotency-Key header on every paid avatar video submit, keep the key stable across retries of the same clip, and poll the job instead of submitting again when your client times out. Sume's docs say: do not submit a paid request again only because your local worker timed out.

Rules from the docs

The Generation admission page (read 2026-10-08) lists the recommended client behavior for production integrations:

  • Prefer async submit with an idempotency key.
  • Treat queued and processing as normal non-terminal states.
  • Use exponential backoff when polling; avoid tight loops across many jobs.
  • Poll until terminal is true, or until your own deadline.
  • Store status_url, result_url, events_url and cancel_url when present.
  • Check generation_limits; when queue capacity is low, do not add more work.

Key design

The key must identify the clip, not the attempt. Build it from stable inputs such as a campaign id and clip number, and change it only when the request body genuinely changes, for example after you edit the script. If you generate a random key on every retry, you defeat the purpose.

The docs' own batch example uses keys shaped like avatar-batch-001-item-001, one per item.

Idempotency-Key decisions, as of 2026-10-08
SituationKeyAction
Network timeout on submitSame keyResubmit the identical body, or poll if you stored the job
Script editedNew keySubmit as a new paid job
Job reached failed stateDecide per errorFix the cause, then use a new key
Queue fullSame keyBack off, wait for capacity, then retry

Example submit

This standard-library Python script submits one clip and prints the poll URLs. It reads the key from SUME_API_KEY.

import json
import os
import urllib.request

key = os.environ["SUME_API_KEY"]
body = {
    "avatar_handle": "product_host",
    "aspect_ratio": "9:16",
    "script": "Meet the host who never needs a reshoot.",
}
req = urllib.request.Request(
    "https://api.sume.com/v1/avatar-1.0/talking-video",
    data=json.dumps(body).encode(),
    method="POST",
    headers={
        "Authorization": f"Bearer {key}",
        "Content-Type": "application/json",
        "Idempotency-Key": "campaign-12-clip-03",
    },
)
with urllib.request.urlopen(req, timeout=30) as resp:
    job = json.load(resp)
print(job.get("status_url"), job.get("result_url"))

After submit

Poll the status URL with backoff until the job is terminal, then read the result URL. Avatar videos are not something to wait on in one HTTP request: sync and subscribe modes are the same bounded wait of at most 30 seconds, per Jobs and results. Use async plus polling, or a webhook with a poll fallback.

Handling a rejected submit

A 4xx from the API means the request itself is wrong, such as a script outside the 4-60 second window or a bad URL. Fix the body and use a new key. A 5xx or a timeout means you do not know whether Sume accepted the job, which is exactly the case the key protects.

If you stored a job id from an earlier attempt, poll it first. Only resubmit with the same key if you have nothing to poll. Keep logs that tie each key to a clip, so billing questions later are easy to answer.

Sources

Related posts

More in Developers

All Developers posts

Written by Sume