2026-11-04 · 4 min read

A Caption API for Async Video Messages

Async video messages replace meetings, but they inherit the same problem: the recipient is often somewhere they cannot turn the sound on. A quick walkthrough or status update that depends entirely on audio gets deferred or skipped. Captions make it watchable anywhere.

If your team sends these daily, captioning by hand does not fit. The caption.sh API takes the video URL and returns the same message with captions rendered into the frames, ready to drop into Slack, email or your docs.

Submit and poll

One POST starts the render and returns a job id. Poll the job endpoint until it reports done or error, with your own timeout as the third exit.

const API = "https://api.caption.sh/v1";
const headers = {
  Authorization: `Bearer ${process.env.CAPTION_API_KEY}`,
  "Content-Type": "application/json",
};

async function captionClip(sourceUrl) {
  const res = await fetch(`${API}/videos`, {
    method: "POST",
    headers,
    body: JSON.stringify({
      source_url: sourceUrl,
      options: { font: "inter" },
    }),
  });
  if (res.status === 402) throw new Error("out of credits");
  if (!res.ok) throw new Error(`submit failed: ${res.status}`);
  const { job_id } = await res.json();

  for (let i = 0; i < 120; i++) {
    await new Promise((r) => setTimeout(r, 5000));
    const job = await (
      await fetch(`${API}/jobs/${job_id}`, { headers })
    ).json();
    if (job.status === "error") throw new Error("render failed");
    if (job.status === "done") return job_id;
  }
  throw new Error("render timed out");
}

What to know before you batch

  • 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.

Video messages are short, so the per minute pricing stays trivial at this scale. Wire the two calls into whatever tool records the message and the captioned version can be the one you share by default.

Back to all articles