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.

5 min readSume
All posts

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.

Key naming for a Shorts series, read 2026-10-05
SituationKeyWhy
First render of season 1, episode 4shorts-s1-e04-r1Stable across retries
Retry after a crashshorts-s1-e04-r1Same key, no second job
You edited the cut on purposeshorts-s1-e04-r2A new revision is a new request
Episode 4 of season 2shorts-s2-e04-r1Season 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

All Developers posts

Written by Sume