CaptureBeam API
POST a YAML, poll the job, get an MP4. Bearer-authed, JSON in / JSON out.
Base URL
https://capturebeam.com/api/v1Authentication
Every request needs an API key. Create one in your dashboard; the key is shown once and stored hashed. Send it as a Bearer token:
Authorization: Bearer cb_live_xxxxxxxxxxxxxx-api-key: cb_live_… is also accepted for clients that don't play nicely with Authorization.
Endpoints
/v1/rendersSubmit a demo for rendering. Returns immediately with a job ID and a link to edit the demo in the dashboard.
Three body shapes are accepted:
application/json—{ "yaml": "title: ...", "name"?: "..." }application/json—{ "projectId": "pj_..." }text/yaml— raw YAML body
Raw-YAML submissions auto-create a Project so the user can tweak the demo and re-render from the dashboard. The response includes editUrl pointing at that project.
Example
curl -X POST https://capturebeam.com/api/v1/renders \
-H "Authorization: Bearer cb_live_xxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{"projectId": "pj_a1b2c3d4"}'
# 202 Accepted
{
"id": "rj_a1b2c3d4e5f6",
"status": "pending",
"projectId": "pj_a1b2c3d4",
"editUrl": "https://capturebeam.com/dashboard/projects/pj_a1b2c3d4",
"statusUrl": "https://capturebeam.com/api/v1/renders/rj_a1b2c3d4e5f6",
"docsVersion": "1.2.0"
}/v1/renders/{id}Poll a job's status. Poll every 2-3 seconds; jobs typically take 20-40 seconds.
Example
curl https://capturebeam.com/api/v1/renders/rj_a1b2c3d4e5f6 \
-H "Authorization: Bearer cb_live_xxxxxxxxxxxxx"
# 200 OK — running, with live progress
{
"id": "rj_…",
"status": "running",
"videoUrl": null,
"manifestUrl": null,
"steps": null,
"error": null,
"durationMs": null,
"createdAt": "2026-01-15T12:00:00Z",
"projectId": "pj_…",
"editUrl": "https://capturebeam.com/dashboard/projects/pj_…",
"progress": { "current": 3, "total": 8, "stepType": "click" },
"docsVersion": "1.2.0"
}
# 200 OK — succeeded
{
"id": "rj_…",
"status": "succeeded",
"videoUrl": "https://media.capturebeam.com/render-….mp4",
"manifestUrl": "https://media.capturebeam.com/manifest-….json",
"durationMs": 22400,
"steps": [
{ "index": 0, "type": "goto", "status": "ok", "durationMs": 1240 },
{ "index": 1, "type": "click", "status": "ok",
"resolvedSelector": "getByRole(button, name=\"Sign in\")",
"durationMs": 380 }
],
"createdAt": "2026-01-15T12:00:00Z",
"projectId": "pj_…",
"editUrl": "https://capturebeam.com/dashboard/projects/pj_…",
"progress": null,
"docsVersion": "1.2.0"
}
# 200 OK — succeeded with one step skipped
{
"id": "rj_…", "status": "succeeded",
"videoUrl": "https://…",
"steps": [
{ "index": 0, "type": "goto", "status": "ok" },
{ "index": 1, "type": "click", "status": "failed",
"error": "Could not resolve target { role: \"button\", name: \"Buy\" } …" }
]
}
# 200 OK — failed
{
"id": "rj_…",
"status": "failed",
"error": "Render timed out after 600000ms (last phase: remotion-render)…",
"steps": null
}videoUrl and manifestUrl are signed URLs valid for 24 hours. steps is the compact per-step summary — index, type, status (ok/skipped/failed), optional error, optional resolved selector. progress is non-null while status === "running" so dashboards can show “Step 3 of 8 · click”.
/v1/probeDiscover interactive elements on a URL. Use to author valid targets before submitting a render.
Returns the page's buttons, links, inputs, and ARIA-tagged elements with their accessible names — exactly the shapes you can use as target values.
curl -X POST https://capturebeam.com/api/v1/probe \
-H "Authorization: Bearer cb_live_xxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{"url": "https://app.example.com"}'
# 200 OK
{
"url": "https://app.example.com",
"status": 200,
"title": "Example",
"elements": [
{ "role": "button", "name": "Sign in",
"target": { "role": "button", "name": "Sign in" } },
{ "role": "textbox", "name": "Email",
"target": { "role": "textbox", "name": "Email" } }
],
"truncated": false
}/v1/schemaJSON Schema for demo.yaml plus a quick-reference summary. Use to validate client-side or to pin against the live contract.
Public — no auth required. Includes docsVersion and a changelog so cached agent skills can detect when the contract has changed.
Status codes
| Code | Meaning |
|---|---|
| 202 | Render queued. Poll the job ID. |
| 200 | Job state retrieved. |
| 400 | Missing or malformed body. Check yaml / projectId. |
| 401 | Missing or invalid API key. |
| 402 | No active subscription. Upgrade. |
| 404 | Project not found, or doesn't belong to your account. |
| 429 | Concurrency limit reached. |
| 500 | Server error. Retry safe — the job stays in pending. |
Render-and-wait
Most CI integrations want a single blocking call. This shell pattern submits and polls until done:
#!/usr/bin/env bash
set -euo pipefail
KEY=${CAPTUREBEAM_KEY:?}
BASE=https://capturebeam.com
JOB_ID=$(curl -sS -X POST $BASE/api/v1/renders \
-H "Authorization: Bearer $KEY" \
-H "Content-Type: application/json" \
-d '{"projectId":"pj_a1b2c3d4"}' | jq -r .id)
while :; do
RES=$(curl -sS $BASE/api/v1/renders/$JOB_ID \
-H "Authorization: Bearer $KEY")
STATUS=$(echo "$RES" | jq -r .status)
case "$STATUS" in
succeeded) echo "$RES" | jq -r .videoUrl; exit 0 ;;
failed) echo "$RES" | jq -r .error >&2; exit 1 ;;
*) sleep 3 ;;
esac
doneLimits
- Concurrency: 3 simultaneous renders per account. The 4th returns 429 — back off and retry.
- Polling: no rate limit on the status endpoint. Two- to three-second intervals are typical.
Versioning
Every API response includes a docsVersion field (semver). Compare against the version your client was written against. PATCH bumps are clarifications; MINOR adds new optional fields; MAJOR may change semantics. Read the changelog on GET /v1/schema to see what changed.