Jupyter: move Sora cells to Sume, where a cell rerun is a retry

Re-running a notebook cell resubmits the request. Hold one Idempotency-Key per take in a variable so Sume returns the first video job, and show the file inline.

4 min readSume
All posts

In a notebook, you re-run cells all the time, and each re-run of a cell that posts to /v1/videos is a retry from the API's point of view. Put a TAKE value in its own variable and send it as the Idempotency-Key, so running the cell again returns the first Sume job instead of billing another one; bump TAKE only when you want a new render.

OpenAI's deprecations page, read 2026-10-08, lists the Videos API as removed on September 24, 2026, which is why old notebook cells that call it now fail.

The cell rules

Split the work so the expensive step is repeatable and the cheap steps are free to repeat.

Notebook cell plan for Sume video jobs, docs read 2026-10-08
CellDoesSafe to re-run
1set TAKE, PROMPT, read SUME_API_KEYyes
2POST /v1/videos with Idempotency-Key = TAKEyes, same body returns the first job
3poll GET /v1/videos/{id} every 30 secondsyes
4fetch /content and write out.mp4yes, overwrites the file
5display the videoyes

One cell that does it all

If you prefer a single cell, this works in any recent Jupyter or VS Code notebook with requests installed. Changing PROMPT without changing TAKE makes Sume answer 409 idempotency_conflict, which is the signal that you meant a new take.

import os, time, requests
from IPython.display import Video, display

TAKE = "harbor-take-1"
PROMPT = "A kite over a harbor at golden hour"
BASE = "https://api.sume.com"
HEAD = {"Authorization": "Bearer " + os.environ["SUME_API_KEY"]}

res = requests.post(BASE + "/v1/videos", timeout=60,
                    headers={**HEAD, "Idempotency-Key": TAKE},
                    json={"model": "sume/auto", "prompt": PROMPT, "duration": 8})
res.raise_for_status()
job = res.json()
while job["status"] in ("pending", "in_progress"):
    time.sleep(30)
    job = requests.get(BASE + "/v1/videos/" + job["id"], headers=HEAD, timeout=60).json()
print(job["id"], job["status"])
if job["status"] == "completed":
    url = BASE + "/v1/videos/" + job["id"] + "/content?index=0"
    with open("out.mp4", "wb") as f:
        f.write(requests.get(url, headers=HEAD, timeout=300).content)
    display(Video("out.mp4", embed=True))

Interrupting a cell

Stopping the kernel while the loop is sleeping does not cancel the job; it keeps running and reserving credits. Print the job id as above, and on the next run the same TAKE brings it back. If you want a progress bar during the wait, the tqdm post shows one without a fake percentage.

Do not commit the notebook with the API key in an output cell. Read it from the environment, as the first lines do.

Sources

Related posts

More in Developers

All Developers posts

Written by Sume