Re-render a prompts.txt of old Sora prompts with curl, jq and xargs

Turn a file of saved Sora prompts into Sume video jobs with four parallel curl calls, an idempotency key per prompt, then poll jobs.tsv and download each mp4.

5 min readSume
All posts

To re-render a file of old Sora prompts on Sume, read prompts.txt one line per job, POST each to /v1/videos with jq building the body, and record the returned id in jobs.tsv. Run four at a time with xargs, give every prompt a stable Idempotency-Key, then poll the ids and download the finished mp4 files.

The plan

The Sora Videos API was shut down on September 24, 2026, per OpenAI's guide read on 2026-10-07. If you saved prompts, you can replay them against a new model. Do it as a batch, and decide the model, duration and resolution once in variables, not per line.

Decide the settings once. The script above hard-codes five seconds, 720p and 9:16 in the jq body, which is a reasonable draft setting. Change those three values together, since duration and resolution both move the price and 9:16 may not suit the shot.

The pieces

What each part uses (read 2026-10-07)
PartToolDetail
Submitcurl POST /v1/videosReturns 202 with id and polling_url
Parallelismxargs -P4Four submissions at once
Retry safetyIdempotency-Key headerHash of model and prompt, so a rerun does not double-submit
PollGET /v1/videos/{id}Stops on completed, failed or cancelled
Downloadunsigned_urls[0]Send the Authorization header again, curl drops it on a cross-host redirect

Submit in parallel

Create prompts.txt with one prompt per line, then save this as rerender.sh part one. The key is derived from the model and prompt text, so running the script twice returns the same job rather than a second charge. The tr and -0 pair is there because macOS xargs has no -d option.

The idempotency key is the part people skip. Without it, a network blip halfway through the xargs run followed by a rerun creates duplicates and reserves credits twice. With it, the second run returns the original job for every prompt it already sent.

#!/usr/bin/env bash
set -euo pipefail
: "${SUME_API_KEY:?set SUME_API_KEY}"
BASE="${SUME_API_BASE:-https://api.sume.com}"; MODEL="${MODEL:-wan-3.0}"
export BASE MODEL SUME_API_KEY

submit() {
  local key; key="$(printf '%s|%s' "$MODEL" "$1" | openssl dgst -sha256 | awk '{print $NF}' | cut -c1-32)"
  jq -n --arg m "$MODEL" --arg p "$1" '{model:$m,prompt:$p,duration:5,resolution:"720p",aspect_ratio:"9:16"}' |
    curl -sS -X POST "$BASE/v1/videos" -H "Authorization: Bearer $SUME_API_KEY" \
      -H "Idempotency-Key: rerender-$key" -H "Content-Type: application/json" -d @- |
    jq -r --arg p "$1" '[.id // "ERROR", $p] | @tsv'
}
export -f submit

grep -v '^[[:space:]]*$' prompts.txt | tr '\n' '\0' | xargs -0 -n1 -P4 bash -c 'submit "$1"' _ >> jobs.tsv

Poll and download

Append this loop to the same file. It reads jobs.tsv, polls each id every 30 seconds by default, and downloads the video once it completes. The curl call repeats the bearer header because curl does not forward credentials after a redirect to another host.

while IFS=$'\t' read -r id prompt; do
  [ "$id" = "ERROR" ] && { echo "skip: $prompt" >&2; continue; }
  while :; do
    s="$(curl -sS "$BASE/v1/videos/$id" -H "Authorization: Bearer $SUME_API_KEY")"
    case "$(jq -r .status <<<"$s")" in
      completed) curl -sSL "$(jq -r '.unsigned_urls[0]' <<<"$s")" \
                   -H "Authorization: Bearer $SUME_API_KEY" -o "$id.mp4"; break ;;
      failed|cancelled) echo "$id: $(jq -r '.error // .status' <<<"$s")" >&2; break ;;
    esac
    sleep "${POLL_SECONDS:-30}"
  done
done < jobs.tsv

Cost and limits

Each submission reserves list price times 1.25 up front, so a 100 line file at 5 seconds and 720p of Wan is 100 x 5 x $0.125 = $62.50 of reservations. Check the figure for your model first, and read the 429 and 402 cases in the errors docs before raising -P above four.

If a line comes back as ERROR in jobs.tsv, the script skips it when polling and prints the prompt to stderr. Fix the cause, put that single line in a new prompts.txt and run the submit part again.

Sources

Related posts

More in Developers

All Developers posts

Written by Sume