bash wait -n fails on macOS bash 3.2: cap curl jobs at four
On macOS /bin/bash 3.2 'wait -n' is an invalid option, so a throttled curl loop spins. A portable version that caps Sume video submits at four in flight.

The standard recipe for 'run these curl calls, but only four at a time' in bash is a loop that checks jobs -rp | wc -l and calls wait -n when the pool is full. wait -n (wait for any one job) arrived in Bash 4.3. The /bin/bash that macOS ships is 3.2.57, and there the same line prints wait: -n: invalid option and returns 2. Inside a loop that means the throttle never blocks: it spins or, with set -e, kills the script.
The version below falls back to a short sleep when wait -n is not available, so the same file works on Bash 3.2 and on 4.3 or later. It posts 12 five-second Wan 3.0 clips at 480p to POST /v1/videos, keeps at most four requests in flight, and gives each shot its own Idempotency-Key. It was run on bash 3.2.57 against a local stand-in server, not the live API.
The script (16 lines)
curl -w '%{http_code}' prints the status after the body goes to a file, so the log line is one short string per shot. Set SUME_BASE and SUME_API_KEY before running.
#!/usr/bin/env bash
set -u
: "${SUME_API_KEY:?set SUME_API_KEY}"
MAX=4
submit() { # $1 = shot number
code=$(curl -s -o "out-$1.json" -w '%{http_code}' -X POST "$SUME_BASE/v1/videos" \
-H "x-api-key: $SUME_API_KEY" -H 'content-type: application/json' \
-H "Idempotency-Key: trailer-v3-shot-$1" \
-d "{\"model\":\"wan-3.0\",\"prompt\":\"shot $1\",\"duration\":5,\"resolution\":\"480p\"}")
echo "shot $1 -> $code"
}
for i in $(seq -w 1 12); do
while [ "$(jobs -rp | wc -l)" -ge "$MAX" ]; do wait -n 2>/dev/null || sleep 0.2; done
submit "$i" &
done
waitWhy not xargs or GNU parallel
xargs -P is the better tool when your input is a file of prompts (see the related xargs post). A loop like this one earns its place when each job needs a different key, a different body or a log line of its own. GNU parallel is not installed by default on macOS or most CI images, which is the other reason to keep to builtins.
After the loop
Each out-NN.json holds a 202 body with the job id and polling_url. A non-202 code in the log is the cue to rerun just that shot with the same key. The throttle is client-side pacing: Sume itself accepts queued work up to your plan's accepted capacity (24 jobs on Pro) before it answers 429 queue_full.
Sources
Related posts
More in Developers
- How do I make a bilingual English and Spanish audio announcement?
Make one bilingual announcement file: two TTS jobs, one per language, joined by a $0.01 Timeline audio concat with no re-synthesis. About 10 cents in total.
- Bottle: verify a Sume webhook with request.body.read()
A single-file Bottle 0.13 app that verifies the Sume webhook HMAC with request.body.read(), aborts 401 on a bad signature and exits on an empty secret.
- Bun and Deno load .env for a Sume script: Deno needs --allow-env
One submit script, three runtimes. Bun reads .env on its own; Deno needs --env-file plus --allow-env for each variable. Tested on Bun 1.4.0 and Deno 2.9.7.
- C with libcurl: POST /v1/images on Sume and read the status code
A 29-line C program that posts to Sume's image API with libcurl, prints the JSON body, and exits non-zero unless the status is 200 or 202.
Written by Sume