Cutdown batch for Cyber Monday: check trim rows before posting
Cut a Cyber Monday ad into 6, 15, and 30 second versions: a short Python check catches Sume video-trim range errors in each row before you pay $0.02 a job.

Before you post a batch of Cyber Monday cutdowns to Sume POST /v1/video-trim, run each row through a local check for the rules that return a refusal: a media.sume.com URL, a start of 0 or more, exactly one of end or duration, a range that is not empty, and no output with precision: "keyframe". Each trim costs $0.02, so a row that would be refused is a call you can skip.
The rules and refusal codes are from Video trim (read 2026-10-06). The script below holds them in one function and prints a verdict for each row. It makes no network call, so it runs on a laptop with nothing but Python.
Which rules does a trim request have?
The API also rejects any ffmpeg field such as vf or crf with ffmpeg_fields_rejected, so a row must hold only the trim fields.
| Rule | Refusal code |
|---|---|
| video_url on the Sume media host | unsupported_media_source |
| start 0 or more, in seconds | request refused at admit |
| Exactly one of end or duration | video_trim_range_required or video_trim_range_conflict |
| end greater than start, range up to 900 s | video_trim_range_empty |
| output only with precision exact | video_trim_output_requires_exact |
What does the check look like?
Pipe a JSON list of rows into it. Three cutdown rows of a 45-second master, cut at 6, 15, and 30 seconds, all pass. A row that sends both end and duration, or a URL on another host, is flagged.
import json, sys
def check(row):
errors = []
url = str(row.get("video_url", ""))
if not url.startswith("https://media.sume.com/"):
errors.append("unsupported_media_source")
start = row.get("start")
if not isinstance(start, (int, float)) or start < 0:
errors.append("start must be a number of 0 or more")
end, dur = row.get("end"), row.get("duration")
if (end is None) == (dur is None):
errors.append("send exactly one of end or duration")
elif end is not None and isinstance(start, (int, float)) and end <= start:
errors.append("video_trim_range_empty")
elif dur is not None and not 0.2 <= dur <= 900:
errors.append("duration must be 0.2 to 900")
if row.get("precision") == "keyframe" and "output" in row:
errors.append("output needs precision exact")
return errors
rows = json.load(sys.stdin)
for i, row in enumerate(rows):
print(i, check(row) or "ok")What do I do with the verdicts?
Fix the flagged rows, then post the clean ones with one Idempotency-Key each. The check does not look at the file, so a dead link still returns source_not_found from the API, and a source longer than 1800 seconds returns source_duration_exceeded.
- Import each master first with
POST /v1/media-imports, since the API does not fetch from the open internet. - Three cutdowns of one master cost $0.06 in trims.
- The cut starts at
startand runs forduration, so all three of 6, 15, and 30 seconds can start at 0 and share the hook.
Sources
Related posts
More in Use cases
- Narration budget for a daily short-video channel: 30 videos a month
Thirty 750-character narrations a month cost $1.20 on Sume TTS with per-job rounding. The same characters are about $0.50 on MAI-Voice-2.1 and $0.34 on Flash.
- Dark mode hero image: one Ideogram 4.5 edit keeps the text
Make a dark-theme twin of a light hero on Sume with one Ideogram 4.5 edit: the background changes, the headline and layout stay, and the shape matches.
- Deleting a Shorts episode renumbers the rest: skip burned-in numbers
YouTube renumbers other episodes when you edit or delete one. Keep episode numbers out of rendered pixels, or one deletion means re-rendering a season.
- Digital gift card art via API: generate the art, print the code
Make digital gift card artwork with GPT Image 2.5 on Sume, leave a clear text band, then print the amount and code in Pillow so no model ever types a real code.
Written by Sume