What this endpoint does
Your platform already produces written content: blog posts, reports, customer updates, campaign recaps. Call this endpoint when one of those should become a short-form video, without owning script writing, voice, captions, rendering, retries or hosting yourself.
It is not a CMS writer and it never publishes. Your CMS, autoblog, script or AgenticFlow owns the write; ReelsBuilder renders the video and returns a durable job.
Endpoint
POST https://api.reelsbuilder.ai/api/v1/blog-to-videoJob creation requires Idempotency-Key; requests without it return 400 IDEMPOTENCY_KEY_REQUIRED. First preview the same body with dry_run: true to validate the input and read data.price without a job, charge or required key. See Dry runs.
Quickstart
curl -X POST https://api.reelsbuilder.ai/api/v1/blog-to-video \
-H "Authorization: Bearer $REELSBUILDER_API_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"article_url": "https://example.com/blog/acme-q3-recap",
"article_title": "How Acme turned creator traffic into measurable pipeline",
"article_summary": "A campaign recap showing how creator reach became attributed signups.",
"article_text": "Acme launched a two-week creator campaign...",
"platforms": ["youtube_shorts", "tiktok", "instagram"],
"duration_target_sec": 30,
"aspect_ratio": "9:16",
"content_style": "professional",
"agent_run_id": "autoblog-2026-10-06-001"
}'Accepted response (202)
{
"success": true,
"data": {
"job_id": "6a0f...",
"generation_id": "6a0f...",
"video_id": "6a0f...",
"status": "queued",
"poll_url": "/api/v1/jobs/6a0f...",
"source_article": {
"url": "https://example.com/blog/acme-q3-recap",
"title": "How Acme turned creator traffic into measurable pipeline",
"summary": "A campaign recap showing how creator reach became attributed signups.",
"categories": [],
"tags": []
},
"distribution": {
"mode": "external_agent_distribution",
"requested_platforms": ["youtube_shorts", "tiktok", "instagram"]
},
"output_contract": {
"video_url": "available after completion on GET /api/v1/jobs/{job_id}.data.job.artifacts.videoUrl",
"blog_embed": {
"type": "html_video_embed",
"source_url": "https://example.com/blog/acme-q3-recap",
"title": "How Acme turned creator traffic into measurable pipeline",
"placement": "recommended near the first or second article section"
},
"headless_cms_payload": {
"component": "reelsbuilder_video",
"video_url": "<completed videoUrl>",
"caption_tracks": [],
"source_article_url": "https://example.com/blog/acme-q3-recap"
},
"social_metadata": { "title": "...", "description": "...", "platforms": ["..."] }
}
},
"meta": {
"agent_run_id": "autoblog-2026-10-06-001",
"key_mode": "live",
"retryable": false,
"estimated_completion_seconds": 180
}
}Request fields
Send the article content you want narrated — article_text or article_markdown gives the best result. ReelsBuilder does not fetch article_url for you; it is kept for attribution.
| Field | Type | Description |
|---|---|---|
article_url | URL | Canonical article URL. Preserved for attribution and embeds. |
article_title | string | Used as the video topic and in the embed/social metadata. |
article_text | string | Extracted plain text. Best source for accurate narration. Only the first 6,000 characters are used. |
article_markdown | string | Markdown body, used when article_text is absent. Markup is stripped; same 6,000-character limit. |
article_summary | string | Short summary for the social description and fallback narration. |
script | string | Final narration. Overrides automatic script extraction. Scripts built from the article are capped at 2,800 characters. |
content_purpose | enum | education, sales, awareness, product_demo, case_study, news or entertainment. |
asset_urls | URL[] | Images or clips to consider during the render. |
categories, tags | string[] | Echoed back in source_article. |
platforms | string[] | Intended downstream targets, echoed for your pipeline. Nothing is posted. |
duration_target_sec | number | 15–120. |
aspect_ratio | enum | 9:16, 16:9 or 1:1. |
content_style, voice_id | string | Optional style hint and narration voice. |
agent_run_id | string | Your trace id, echoed in meta. |
brand_id | — | Not supported yet. Returns 422 CUSTOMER_CONTEXT_UNAVAILABLE. Renders use your account's default brand context. |
webhook_url | — | HTTPS URL for a signed terminal callback. Polling remains the source of truth; see Webhooks. |
Autoblog agent pattern
// `article` comes from your own CMS.
const runId = crypto.randomUUID();
const start = await fetch("https://api.reelsbuilder.ai/api/v1/blog-to-video", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.REELSBUILDER_API_KEY}`,
"Content-Type": "application/json",
"Idempotency-Key": runId,
},
body: JSON.stringify({
article_url: article.url,
article_title: article.title,
article_text: article.bodyText,
article_summary: article.excerpt,
agent_run_id: runId,
}),
});
const accepted = await start.json();
const jobId = accepted.data.job_id;
let job;
for (;;) {
const r = await fetch(`https://api.reelsbuilder.ai/api/v1/jobs/${jobId}`, {
headers: { Authorization: `Bearer ${process.env.REELSBUILDER_API_KEY}` },
});
job = (await r.json()).data.job;
if (["completed", "failed", "cancelled", "dead_lettered"].includes(job.status)) break;
await new Promise((resolve) => setTimeout(resolve, 15000));
}
if (job.status === "completed") {
await yourCms.attachVideoEmbed(article.id, {
video_url: job.artifacts.videoUrl,
source_article_url: article.url,
});
}Pricing, sandbox and limits
- Live keys are charged $0.50 per accepted request from your prepaid balance. Failed jobs are refunded.
- Insufficient balance returns
402 INSUFFICIENT_API_CREDITS. - Sandbox keys (
rb_test_) return a 202 fixture withsandbox: true, create no job and charge nothing. - At most 5 heavy render jobs can be active per account; the sixth returns
429 RENDER_CONCURRENCY_LIMIT_EXCEEDED. - Polling is free but counts toward the 60 requests/minute read limit; poll every 10–15 seconds.