2026-11-03 · 4 min read
Captions for Sermon Videos with an API
Most sermon clips get watched on a phone, often in places where sound is not an option. If the clip depends on audio alone, part of the message never lands. Burned in captions keep the words visible without asking the viewer to change anything.
For a media team or a volunteer handling this every week, the caption.sh API turns captioning into two calls: submit the video, download the captioned render. It works the same whether you caption the full sermon or just the weekly clip.
The captioning workflow
Submit the video URL, poll the job until the render finishes, then save 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.
If you only clip a few minutes for social, caption just those segments rather than the full recording, since billing is per rendered minute. The captioned file is a normal mp4, ready for YouTube, Facebook or your church app.