2026-11-01 · 6 min read
Captions for Online Ads at Scale: A Programmatic Workflow for High-Volume Creative
Paid social runs on volume. A testing cadence that works means new hooks, new cuts, and new aspect ratios every week, and every one of those variants needs to survive muted autoplay in the feed. Captions are not a polish step in that workflow; they are the difference between a variant that gets judged on its message and one that gets skipped.
Hand-captioning breaks first. When the count of active variants climbs past what an editor touches in a day, the options are shipping uncaptioned creative or automating the step. caption.sh is the automated option: POST each final cut, get back an MP4 with word-timed, styled captions burned in, and drop it into the ad account.
A batch caption step for ad variants
import time, requests, sys
KEY = "YOUR_CAPTION_API_KEY"
HEADERS = {"Authorization": f"Bearer {KEY}"}
API = "https://api.caption.sh/v1"
def caption_variant(video_url: str) -> str:
r = requests.post(
f"{API}/videos",
headers=HEADERS,
json={"source_url": video_url, "options": {"font": "inter"}},
timeout=30,
)
job_id = r.json().get("job_id")
if not job_id:
sys.exit(f"submit failed: {r.status_code} {r.text}")
deadline = time.time() + 15 * 60
while time.time() < deadline:
job = requests.get(f"{API}/jobs/{job_id}", headers=HEADERS, timeout=30).json()
if job.get("status") == "done":
r = requests.get(
f"https://api.caption.sh/v1/jobs/{job_id}/result",
headers=HEADERS, allow_redirects=False, timeout=30,
)
return r.headers["Location"]
if job.get("status") == "error":
sys.exit(f"caption job failed: {job}")
time.sleep(5)
sys.exit("timed out waiting for caption job")Submissions are rate-limited per key, which is the right behavior when a batch of fifty variants lands at once: wait briefly after a 429 and resubmit rather than treating it as a failure. Results are stored for 24 hours and then deleted, so a nightly batch should download its outputs as part of the same run.
Why burned-in wins for ads
- Feed placements autoplay muted, so the captioned version is the version the impression actually buys.
- Burned-in captions render identically across every placement and device, with no dependence on a platform's caption support.
- One style setting across all variants keeps the account visually consistent even when five people cut the creative.
At $0.10 per minute, captioning a week of ad variants costs less than the impressions the first one will buy. Get an API key at caption.sh and run last week's batch through it.