Shorts series: one Idempotency-Key per season and episode
Timeline renders require an Idempotency-Key. Name it from season and episode, so a retried upload script for episode 4 does not queue a second render.

Short answer
Build the key from the series, season and episode, for example shorts-s1-e04-r1. POST /v1/timeline-1.0/render requires an Idempotency-Key header, and the video-frames docs describe the header as what stops a retry from queueing a second job. A stable key per episode gives your script the same protection when it crashes and starts over.
Why a series needs it
A season of Shorts is a loop over episodes, and loops get interrupted: a network drop, a laptop closing, a job that timed out on your side. Without a key, a rerun renders every episode again and you pay for each one. YouTube's series page says a season number is required and that episodes are numbered by publish date (read 2026-10-05), so your own episode list already has the fields you need for a key.
What the docs say, and what I did not find
The timeline page says Idempotency-Key is required on render and that the plan call does not need one. The video-frames page says to send the key on REST so a retry does not queue a second extract, and the MCP tools take it as idempotency_key. I did not find documented behavior for reusing a key with a different body, so do not rely on a changed body under the same key. Add a revision suffix, r1, r2, when you change an episode on purpose.
| Situation | Key | Why |
|---|---|---|
| First render of season 1, episode 4 | shorts-s1-e04-r1 | Stable across retries |
| Retry after a crash | shorts-s1-e04-r1 | Same key, no second job |
| You edited the cut on purpose | shorts-s1-e04-r2 | A new revision is a new request |
| Episode 4 of season 2 | shorts-s2-e04-r1 | Season is part of the key |
A loop with stable keys
The script builds one request per episode and prints the key. It sends only when SEND is set, so a first run is a dry run. The spine is silent with a declared length, and each episode is one slot from its own source clip. Replace the URLs with media.sume.com artifacts you imported.
import json, os, urllib.request
SEND = os.environ.get("SEND") == "1"
key = os.environ.get("SUME_API_KEY", "")
if SEND and not key:
raise SystemExit("set SUME_API_KEY")
for ep in range(1, 4):
body = {"audio": {"mode": "silence", "duration_seconds": 60},
"video": [{"source_url": f"https://media.sume.com/artifacts/artf_demo/e{ep}.mp4",
"start": 0, "duration": 60}]}
idem = f"shorts-s1-e{ep:02d}-r1"
print(idem)
if SEND:
req = urllib.request.Request(
"https://api.sume.com/v1/timeline-1.0/render",
data=json.dumps(body).encode(),
headers={"Authorization": "Bearer " + key,
"Content-Type": "application/json",
"Idempotency-Key": idem})
print(json.load(urllib.request.urlopen(req))["request_id"])After submit
A render is async by default, so you get a job back and poll GET /v1/jobs/:id/status, then read /result when it is ready. Store the job id next to the episode so a later run can look it up before it submits. Each 60-second episode is 1 billable minute, $0.10 at the documented rate, so three episodes are $0.30. Run the plan call first if you want the number from the API.
Two habits help. Keep the key in the episode file, not in the script, so a different machine reuses the same one. And log the job id the first time you see it. If a run fails midway, resume from the log: skip episodes that already have a result, and resubmit only the rest with their original keys.
The same pattern works for a bulk of any size. For an eight-episode season the loop is eight keys, eight job ids and eight results, and the plan call for each is free.
Sources
Related posts
More in Developers
- Shrink an AI image to a byte budget: JPEG quality search in Pillow
Platforms cap image files at 1 MB or 5 MB. Binary-search the JPEG quality in Pillow for the sharpest file under your byte limit, from a Sume image.
- silence_split_seconds: tune caption line breaks from STT segments
Sume STT sentence segmentation can split on silence. silence_split_seconds takes 0.2 to 3 and returns gapless segments you can use as caption lines.
- Captions fail on a silent Short with caption_no_speech: send cues
A silent clip has no speech to transcribe, so Sume's captions API returns caption_no_speech. Send cues with text, start and end to burn overlay text instead.
- 16 AI shots in one Sume Timeline render: the 8-fade cap
A 16-shot cut fits one Timeline render, but fades are capped at 8 in a row and renders chunk past 12 slots. Plan the cuts, with the doc limits.
Written by Sume