← All posts

Automate Video Creation with the Wavemaker API

A practical guide to generating, rendering, and downloading video programmatically with the Wavemaker REST API — endpoints, auth, the generate→render flow, webhooks vs polling, and a copy-paste script.

If you generate video regularly — per-product ads, weekly recaps, localized cuts — doing it by hand doesn’t scale. The Wavemaker REST API lets you generate, render, and download finished video from your own code, a backend job, or CI. This guide walks the whole flow with runnable examples.

Authentication

Create an API key in the dashboard at /keys (you’ll need a plan with API access). Every request carries it as a Bearer token:

Authorization: Bearer mcp_your_api_key
Content-Type: application/json

The base URL is https://api.wavemakr.com. Errors come back as { "error": "<message>" } with a meaningful status (400 invalid input, 402 insufficient credits, 403 wrong scope, 404 not found / not yours).

The core flow: generate → render → download

Four steps take you from a brief to an MP4:

  1. Start a generationPOST /api/v1/videos
  2. Wait for it — poll GET /api/v1/videos/:job_id (or use a webhook) until complete; it returns a composition_id
  3. Render to MP4POST /api/v1/renders with that composition_id
  4. Download — poll GET /api/v1/renders/:render_id until done; use the download_url

1. Start a generation

curl -X POST https://api.wavemakr.com/api/v1/videos \
  -H "Authorization: Bearer $WAVEMAKR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "prompt": "30s vertical ad for Driftwood Coffee cold brew, upbeat, end on driftwood.com",
    "aspect_ratio": "9:16",
    "duration_seconds": 30
  }'
# → 202 { "job_id": "wf_..." }

Optional body fields: skill_type (a treatment id from GET /api/v1/treatments), model_overrides (per-run model pins from GET /api/v1/models), and webhook_url / webhook_secret.

2–4. The whole loop in Node (zero dependencies)

const BASE = "https://api.wavemakr.com";
const KEY = process.env.WAVEMAKR_API_KEY;
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));

async function api(method, path, body) {
  const res = await fetch(`${BASE}${path}`, {
    method,
    headers: { Authorization: `Bearer ${KEY}`, "Content-Type": "application/json" },
    body: body ? JSON.stringify(body) : undefined,
  });
  const json = await res.json();
  if (!res.ok) throw new Error(`${method} ${path} → ${res.status}: ${json.error}`);
  return json;
}

// 1. generate
const { job_id } = await api("POST", "/api/v1/videos", {
  prompt: "30s vertical ad for Driftwood Coffee, end on driftwood.com",
  aspect_ratio: "9:16",
  duration_seconds: 30,
});

// 2. poll until complete
let compositionId;
for (;;) {
  await sleep(10_000);
  const s = await api("GET", `/api/v1/videos/${job_id}`);
  if (s.status === "complete") {
    compositionId = s.composition_id;
    break;
  }
  if (s.status === "errored") throw new Error(s.error);
}

// 3. render
const { render_id } = await api("POST", "/api/v1/renders", { composition_id: compositionId });

// 4. poll render, get the MP4
let downloadUrl;
for (;;) {
  await sleep(5_000);
  const r = await api("GET", `/api/v1/renders/${render_id}`);
  if (r.status === "done") {
    downloadUrl = r.download_url;
    break;
  }
  if (r.status === "failed") throw new Error(r.error);
}
console.log("MP4:", downloadUrl);

Prefer webhooks in production

Polling is fine for a script, but in a real backend pass a webhook_url. On completion Wavemaker POSTs a JSON event (video.completed, render.completed, …). Provide a webhook_secret and verify the X-Webhook-Signature: v1,<hmac-sha256> header before trusting the payload. Webhook URLs must be HTTPS and carry no credentials.

Estimate cost before you generate

POST /api/v1/cost-estimates returns a credit estimate from rough inputs (scene_count, video_clips, ai_images, wants_voiceover, has_url, has_topic) — useful for budgeting a batch. GET /api/v1/account returns your current balances.

Edit without regenerating

The pipeline reviews every scene as it generates. GET /api/v1/videos/:job_id/reviews returns those per-scene verdicts (status, score, issues, recommended action) for free — no re-review. To fix something, POST /api/v1/videos/:composition_id/refine with a natural-language instruction (“scene 3 has a third arm — regenerate it”). For structured, identity-preserving edits, pull the composition into a workspace and call targeted tools.

Useful catalogs

  • GET /api/v1/treatments — valid skill_type values (formats/styles).
  • GET /api/v1/models — valid model_overrides values.
  • GET /api/v1/brand-kits — brand kit ids to seed brand context.

Beyond single videos

For per-scene control, identity-locked characters, and custom chains, see the workspace tools and the end-to-end automation guide. Prefer working from an AI assistant? Connect Wavemaker over MCP.

Full endpoint reference lives in the developer docs. Grab an API key and build →

Frequently asked questions

Does Wavemaker have an API?
Yes. Wavemaker exposes a REST API at https://api.wavemakr.com (API-key auth) and an MCP endpoint at https://api.wavemakr.com/mcp. The REST API covers managed generation, refinement, rendering, reviews, compositions, uploads, catalogs, and account/billing.
How do I generate a video via the API?
POST /api/v1/videos with a prompt (and optional aspect_ratio, duration_seconds, skill_type) and a Bearer API key; you get back a job_id. Poll GET /api/v1/videos/:job_id until status is complete (it returns a composition_id), then POST /api/v1/renders to produce an MP4.
Should I poll or use webhooks?
Use webhooks in production. Pass a webhook_url (and webhook_secret to verify the HMAC signature) on generation and render requests; you'll get a POST when the job completes instead of polling.
How do I get an API key?
Create one in the dashboard at /keys (requires a plan with API access). Keys begin with mcp_ and go in the Authorization: Bearer header.