Worker crashed mid-poll: list Sume jobs and join on idempotency_key
After a restart, GET /v1/jobs?status=processing lists your in-flight video jobs. Each row carries the idempotency_key you sent, so match your records on it.

If a worker died after submitting Sume video jobs but before saving their ids, list the jobs with GET /v1/jobs?status=queued and status=processing and match each row to your own record by its idempotency_key. The list is newest first, capped at 100 per page, and an ordinary API key sees the jobs its owner created.
Do not match by position. The OpenAPI text says newest-first is not your submission order once a wave retries, and that the idempotency_key on each row is the column to join on.
The query parameters
These are the ones that matter for recovery. An unknown query parameter is a 400 unknown_parameter, not a silently unfiltered page.
| Parameter | Values | Use |
|---|---|---|
| status | queued, processing, completed, failed, canceled | one status per call |
| limit | 1 to 100 | page size |
| starting_after | the next_cursor from the last page | next page; absent cursor means done |
| scope | thread or workspace | an ordinary key defaults to its owner's jobs |
| type | any non-empty string | narrow by job type |
Walk the pages
Do not invent a cursor from the last job id. When data.next_cursor is missing you are on the last page. Run this with SUME_API_KEY set; it prints the id, status and key of every unfinished job.
import json, os, urllib.request
def page(status, cursor):
url = "https://api.sume.com/v1/jobs?limit=100&status=" + status
if cursor:
url += "&starting_after=" + cursor
req = urllib.request.Request(
url, headers={"Authorization": "Bearer " + os.environ["SUME_API_KEY"]})
return json.load(urllib.request.urlopen(req))["data"]
def main():
for status in ("queued", "processing"):
cursor = None
while True:
data = page(status, cursor)
for job in data["jobs"]:
print(job["id"], job["status"], job["idempotency_key"], job["created_at"])
cursor = data.get("next_cursor")
if not cursor:
break
main()Why this replaces OpenAI's list call
OpenAI's video guide, read 2026-10-08, includes list and delete operations on videos, and the Videos API itself is removed on September 24, 2026. Sume's list is a read-only view over jobs of all kinds, not only video, so keep your own table as the source of truth and use the list for recovery.
Choose your Idempotency-Key from your record id at submit time, which makes this join possible; a random key per attempt leaves you with rows you cannot match. The storage post covers the columns to keep, and the list and delete post covers the removed routes.
Sources
Related posts
More in Developers
- Reserve, capture, refund: a 20-clip batch with 3 failures, 2 cancels
Worked example of how a Sume balance moves across a 20-job batch when 3 jobs fail and 2 are canceled while queued: what is held, captured and released.
- Retry a timed-out Omni Flash submit without paying twice (Node)
Node 18+ code that retries a Gemini Omni Flash 1.1 POST to Sume's /v1/videos on timeout or 429 with one Idempotency-Key, so a retry returns the same job.
- Retry a timed-out POST /v1/videos with one Idempotency-Key, pay once
A Python submit wrapper that retries network errors, 429 and 5xx with a stable Idempotency-Key, so a replay returns the original job instead of a second charge.
- Review a 30-second Wan 3.0 clip: video inspect caps at 24 stills
Video inspect returns at most 24 stills per call, so a 30-second clip needs a sample rate of 0.8 fps or an explicit list of 24 times. Python builds the request.
Written by Sume