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.

4 min readSume
All posts

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.

Writes per minute by plan (read 2026-10-03)
PlanWrites per minuteReads per minute
Free1204,800
Pro30012,000
Startup60024,000
Scale1,20048,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

All Use cases posts

Written by Sume