2026-11-04 · 4 min read
Captions for Zoom Recordings with an API
Meeting recordings pile up fast, and almost nobody rewatches them start to finish. The people who do watch often play them muted at their desk or while commuting, which makes captions the difference between a useful recording and a skipped one.
Zoom gives you a transcript, but the transcript stays in Zoom. If you want the words inside the video file itself, so they survive being downloaded, shared or uploaded anywhere else, burn them in. The caption.sh API does that from a URL in two calls.
The captioning workflow
Download the recording, host it somewhere reachable, then submit it. This example captions one recording and saves the result next to the original.
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 recurring meetings, caption only the segments worth sharing instead of full hour-long calls. Billing is per rendered minute, so clipping first keeps the credit spend small and the turnaround fast.