2026-11-02 · 4 min read
A Caption API for YouTube Creators
YouTube's own subtitle track is optional for the viewer and invisible in many embeds and previews. Creators who want the words always visible, especially on Shorts and on clips shared off platform, burn the captions into the video instead.
The caption.sh API does exactly that: it takes your finished video and returns the same video with styled captions rendered into the frames. Nothing about your upload flow changes, because YouTube just sees a normal mp4.
The captioning workflow
Submit the video URL, poll the job, download the result. This example is plain Python with requests.
import requests, time
KEY = "YOUR_CAPTION_API_KEY"
H = {"Authorization": f"Bearer {KEY}"}
API = "https://api.caption.sh/v1"
def caption_video(source_url, out_path):
r = requests.post(f"{API}/videos", headers=H,
json={"source_url": source_url, "options": {}},
timeout=30)
if r.status_code == 402:
raise RuntimeError("out of credits, top up first")
r.raise_for_status()
job_id = r.json()["job_id"]
deadline = time.time() + 60 * 60
while time.time() < deadline:
time.sleep(15)
job = requests.get(f"{API}/jobs/{job_id}", headers=H, timeout=30).json()
if job["status"] == "done":
break
if job["status"] == "error":
raise RuntimeError(f"render failed: {job}")
else:
raise RuntimeError("render did not finish in time")
mp4 = requests.get(f"{API}/jobs/{job_id}/result", headers=H, timeout=60)
open(out_path, "wb").write(mp4.content)Practical notes
- Submissions are rate limited per API key. A 429 response means wait a moment and retry, it does not mean the job failed.
- Rendering is prepaid at $0.10 per minute of video. A 402 response means the balance is empty, so top up before retrying.
- Every render needs a bounded poll loop with three exits: done, error, and your own timeout. Do not poll forever.
- The API documents no webhook surface, so polling is the intended way to learn a job finished.
For Shorts, keep captions inside the vertical safe zone so the interface buttons do not cover them. For long form, burn captions only on the segments you clip for promotion, since billing is per rendered minute and a full episode may not need them.