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.
- 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.
- Captions covering Shorts stickers: move the line with anchor_ratio
Burned-in captions can sit where a Shorts sticker will land. Sume's caption design.placement.anchor_ratio moves the line, from 0.05 to 0.95 of frame height.
Written by Sume