Format status_url never holds output: poll it, then fetch the result

A Sume Format run's status_url returns a small poll payload with no output or artifacts. Poll it with backoff up to expires_at, then call result_url once.

4 min readSume
All posts

The status_url on a Sume Format run receipt never holds output, artifacts or primary_output_url. It returns a small poll payload with status, next_action, cancelable, expires_at and queue. Poll it until the status is terminal, then call result_url once to get the full receipt.

Why the split exists

If your code polls status_url and then reads output from the same response, it reads nothing, and the run can look empty even after it completed. The docs describe the split plainly. The small payload is cheap to poll, and the full receipt is returned by the receipt itself at GET /v1/format-runs/{run_id} or by result_url, which gives a 409 run_not_completed while the run is in progress.

The URLs on a receipt

Use the URLs that are on the receipt, and do not build paths yourself. Here is the set of them, with what each one is for.

Format run URLs and what each returns, from docs.sume.com/formats/runs (read 2026-10-05)
URL on the receiptReturnsUse it for
status_urlstatus, next_action, cancelable, expires_at, queueCheap polling
result_urlFull receipt when terminal, 409 run_not_completed beforeOne fetch at the end
events_urlA polled phase timelineProgress display
cancel_urlStops the runA user pressing stop

How to poll

Back off while you poll. Long-form video is 15 to 30 minutes of work, so a poll each second gives you nothing. Double the gap up to one minute. A 429 or 503 during the loop is temporary: the run continues to execute and to spend, so wait and poll again, and do not think that the run failed. Use expires_at as the ceiling, since it is the deadline after which Sume force-finalizes the run as failed, and it is null once the run is terminal.

A poll loop that fetches once

The script below follows that pattern. It takes a run id from RUN_ID, polls the status URL with doubling gaps, and fetches the result URL once at the end. It stops on any terminal status, and it reads output only from the result.

import json, os, time, urllib.request
BASE = "https://api.sume.com/v1/format-runs/" + os.environ["RUN_ID"]
HEAD = {"Authorization": "Bearer " + os.environ["SUME_API_KEY"]}

def get(path):
    req = urllib.request.Request(BASE + path, headers=HEAD)
    with urllib.request.urlopen(req) as r:
        return json.load(r)["data"]

gap = 5
while True:
    status = get("/status")
    if status["status"] not in ("queued", "processing"):
        break
    time.sleep(gap)
    gap = min(gap * 2, 60)

result = get("/result")
print(result["status"], result.get("primary_output_url"))
print(result.get("output_error"))

Stop conditions

The terminal statuses are completed, failed, canceled and skipped. Note that canceled is spelled with one l. If your code compares against the wrong spelling, a canceled run will keep your loop alive until its own timeout. Branch on status, and on outcome in a webhook, to answer "did I get usable output".

Webhook first, poll as backup

If you can, use a webhook as the main signal and keep this loop as a backup. The webhook carries the same terminal receipt for completed and failed runs. A canceled or skipped run never delivers one, and the poll also covers the case where your endpoint was down.

Sources

Related posts

More in Formats

All Formats posts

Written by Sume