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.

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
| Part | Tool | Detail |
|---|---|---|
| Submit | curl POST /v1/videos | Returns 202 with id and polling_url |
| Parallelism | xargs -P4 | Four submissions at once |
| Retry safety | Idempotency-Key header | Hash of model and prompt, so a rerun does not double-submit |
| Poll | GET /v1/videos/{id} | Stops on completed, failed or cancelled |
| Download | unsigned_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.tsvPoll 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.tsvCost 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
- Read the motion clip length with video inspect before Kling duration
Kling motion control on Sume reserves money from the duration_seconds you declare. Probe the reference clip with video inspect first, so the number is measured.
- Read the Sume Music Router catalog: auto, Lyria 3.5 and Lyria 3 Pro
GET /v1/music-router/models returns three ids, their prompt limits and prices in micro-dollars. A Node script prints the table so you do not hard-code it.
- Read the Sume TTS Router catalog in one request: Sonic ids, prices
GET /v1/tts-router/models lists the Sonic ids, the per-character list price, the margin and the 20,000-character cap. A small Node script prints them.
- Reconcile Sume jobs after a deploy or outage: poll what is open
After downtime, read status for every job your own table still shows as open, honor terminal and result_ready, and never resubmit. Python with sqlite.
Written by Sume