2026-11-05 · 4 min read
A Caption API for E-Learning Platforms
If you run an e-learning platform, captions are not a nice-to-have. Learners watch on mute, many are not native speakers of the course language, and accessibility expectations keep rising. The problem is operational: every instructor uploads video, and nobody wants to caption hours of lessons by hand. A caption API moves that work into the upload pipeline, so every lesson video can come out the other side already captioned.
What burned-in captions buy a course platform
- Every player shows the captions, including embeds and downloaded files, with no per-player caption track support required.
- Learners on mute or in noisy environments can follow the lesson.
- Consistent styling across instructors, instead of whatever each instructor's editor produced.
- No change to your video hosting: the captioned file replaces or sits next to the original.
How the API fits an upload pipeline
The flow is simple: an instructor uploads a lesson, your backend stores the raw file and hands a URL to the caption API, and when the job finishes your system swaps the captioned render into the course. caption.sh accepts a JSON POST with a source_url and returns a job_id, so integration is a submit call plus a status poll.
curl -X POST https://api.caption.sh/v1/videos \
-H "Authorization: Bearer $CAPTION_API_KEY" \
-H "Content-Type: application/json" \
-d '{"source_url": "https://example.com/talk.mp4"}'
Submissions are rate limited per API key. If you get a 429 during a bulk import of a course library, wait and retry rather than hammering the endpoint.
Handling jobs at course scale
Each video is its own job with its own job_id, and you track them the same way you track any asynchronous work in your system. Poll each job until it is done or failed, with a timeout so a stuck job surfaces instead of hanging your pipeline.
import time
import requests
KEY = "your_api_key"
H = {"Authorization": f"Bearer {KEY}"}
job = requests.post(
"https://api.caption.sh/v1/videos",
headers=H,
json={"source_url": "https://example.com/talk.mp4"},
).json()
job_id = job["job_id"]
# A 429 means the transcription throughput ceiling is reached. Wait and retry.
deadline = time.time() + 1800
status = None
while time.time() < deadline:
r = requests.get(f"https://api.caption.sh/v1/jobs/{job_id}", headers=H)
status = r.json()["status"]
if status in ("done", "error"):
break
time.sleep(10)
if status == "done":
r = requests.get(f"https://api.caption.sh/v1/jobs/{job_id}/result", headers=H, allow_redirects=False)
print(r.headers["Location"]) # signed URL; results are stored for 24 hours, then deleted
elif status == "error":
raise SystemExit("captioning failed")
else:
raise SystemExit("timed out waiting for the job")
When a job completes, the result endpoint redirects to a signed download URL; results are stored for 24 hours, then deleted. Copy the captioned file into your own storage as part of the completion step. The API documents no webhook surface, so the completion step hangs off your poll loop.
Cost shape
Captioning is $0.10 per minute of video, prepaid. A 10-hour course library backfill runs about $60, and new lessons cost a few dollars each as they arrive. That makes it practical to caption the entire catalog, not just flagship courses.