Check a Format run's video with ffprobe: duration and audio stream
Before you publish a Format run's clip, run ffprobe on primary_output_url, confirm an audio stream exists and compare its length with duration_ms.

Before you publish a Format run's video, run ffprobe on primary_output_url and check two things: that the file has an audio stream, and that its duration matches the duration_ms on the matching artifact. A completed receipt proves the run finished. It does not prove the file is the one you meant to ship.
Why probe a finished run
The Sume docs say it plainly for video: read the file, not the number next to it. ffprobe takes about two seconds and is the only method that shows the difference between an assembled cut and a clip that merely has the shape of one. This matters most for voiceover work, where a silent export looks fine in a thumbnail.
What Sume checks and what it does not
Sume already guards some of this. A duration_ms inside a SumeMediaFile must agree with the artifact ledger within 10%, and a receipt that fills an assembled-video field with one of its own scene clips is rejected when two or more scenes succeeded. Those checks cover the number you are told. They do not decode the file, so the probe is your own last gate.
| Check | Source of truth | Fails when |
|---|---|---|
| Run finished | status completed | Any other terminal status |
| Deliverable exists | primary_output_url not null | Null on every non-completed run |
| Schema satisfied | output_error is null | output_error set, even on completed |
| Audio present | ffprobe stream list | No audio codec_type in the file |
| Length agrees | ffprobe duration vs artifact duration_ms | More than a tenth apart |
A publish gate
The script reads the receipt, finds the artifact whose URL equals primary_output_url, probes the file and prints one verdict. It exits non-zero on a problem so a CI step or a publish job can stop. ffprobe can open an HTTPS URL directly, so there is no download step.
import json, os, subprocess, sys, urllib.request
def receipt(run_id):
req = urllib.request.Request(
"https://api.sume.com/v1/format-runs/" + run_id,
headers={"Authorization": "Bearer " + os.environ["SUME_API_KEY"]})
with urllib.request.urlopen(req) as r:
return json.load(r)["data"]
def probe(url):
out = subprocess.run(["ffprobe", "-v", "error", "-show_entries",
"format=duration:stream=codec_type", "-of", "json", url],
capture_output=True, text=True, check=True).stdout
info = json.loads(out)
return float(info["format"]["duration"]), [s["codec_type"] for s in info["streams"]]
def main(run_id):
run = receipt(run_id)
url = run["primary_output_url"]
if run["status"] != "completed" or run["output_error"] or not url:
sys.exit("not a deliverable: " + run["status"])
seconds, kinds = probe(url)
art = next((a for a in run["artifacts"] if a["url"] == url), {})
ms = art.get("duration_ms")
ok = "audio" in kinds and (ms is None or abs(seconds * 1000 - ms) <= 0.1 * ms)
print("OK" if ok else "CHECK", round(seconds, 1), kinds)
sys.exit(0 if ok else 1)
main(sys.argv[1])Null means not measured
Treat a null duration_ms as not measured, not as zero. The script skips the length comparison in that case and still enforces the audio check. If you need to compare a standalone voiceover against the video, probe both files and require the audio to be no longer than the video.
Every clip, not only the first
Run the gate on every variant clip, not only the primary. A loop over artifacts[] with type equal to video costs seconds, and a silent or truncated variant is much cheaper to catch before it is queued for an ad account.
Sources
Related posts
More in Formats
- Continue a Sume Format run with previous_run_id after a review gate
Send previous_run_id on a new POST .../runs so the agent redoes one part. A yes/no review model decides whether to continue; each new run gets its own webhook.
- Cost per Reel from Format run receipts: sum billable_amount_usd_micros
Each Sume Format run receipt reports usage.billable_amount_usd_micros. Divide by 1,000,000 for dollars. A Python loop prices a Reel batch from its run ids.
- Cyber Monday price changed after render: rerun one SKU or continue it?
One SKU's price changed after its video rendered. Send a new run with new input, or continue the old run with previous_run_id. Here is when each fits.
- ElevenLabs Flows templates API vs a Sume Format run with a webhook
ElevenLabs added Flows template endpoints on Sep 28: list, details and run with a webhook. Sume Formats give the same three steps for image, video and audio.
Written by Sume