Caption 30 clips without a 429: honor retry-after in a Python loop
Submit caption jobs in a loop and wait the `retry-after` seconds on a 429. Reuse the same Idempotency-Key per clip so a retry never creates a second paid job.

Thirty caption submissions are thirty writes, which fits inside the Free plan's 120 writes per minute. If you do get a 429, the response carries a retry-after header and error.details.scope, so sleep that long and send the identical request again.
Write budgets by plan
Writes and reads have separate budgets. Reads are 40 times the write number, so status checks do not compete with submissions.
| Plan | Writes per minute | Reads per minute |
|---|---|---|
| Free | 120 | 4,800 |
| Pro | 300 | 12,000 |
| Startup | 600 | 24,000 |
| Scale | 1,200 | 48,000 |
The loop
The key is derived from the clip index, so a crashed run that restarts with the same list reuses the same keys instead of paying twice. Concurrency is a separate plan limit from the request budget, so accepted jobs may still wait their turn.
import os, time, requests
H = {"Authorization": f"Bearer {os.environ['SUME_API_KEY']}"}
def submit(key, body):
while True:
r = requests.post("https://api.sume.com/v1/video-captions",
headers={**H, "Idempotency-Key": key},
json=body, timeout=30)
if r.status_code != 429:
r.raise_for_status()
return r.json()["request_id"]
time.sleep(float(r.headers.get("retry-after", "5")))
urls = os.environ["CLIP_URLS"].split(",")
ids = [submit(f"batch-caps-{i}", {"video_url": u}) for i, u in enumerate(urls)]
print(len(ids), "jobs accepted")After the submit loop
Do not poll all thirty in a tight loop. Use a webhook so each job reports job.completed or job.failed once, or poll GET /v1/jobs/:id/status and respect next_poll_after_seconds. At $0.20 per job the batch is $6.00 for clips up to 60 seconds.
Sources
Related posts
More in Use cases
- Captions when speakers switch languages: Section 508's (in Spanish)
Section 508 says to mark a language change like (in Spanish) and keep the exact words when speech is untranslated. How to burn that with authored Sume cues.
- Captions on AI video with native sound: what Sume actually transcribes
Gemini Omni Flash 1.1 always generates synced audio on Sume. The captions endpoint transcribes speech only, so sound effects need authored cues.
- Should captions censor profanity? Section 508 and YouTube's [ __ ]
Section 508 says caption audible profanity exactly; YouTube's auto captions swap flagged words for [ __ ] by default. How to burn the wording you intend.
- Unreadable captions on bright Reels: dim 0.45, check for free
Burned-in captions vanish on bright footage. Sume video filter dim 0.45 darkens luma; the /video-filter/check endpoint validates the program at no charge.
Written by Sume