Developers6 min read

Video generation API: a practical guide for developers

A video generation API turns a brief (topic or script, length, voice, language, look) into a rendered MP4. The integration is a short loop: estimate, generate, wait for a webhook, download.

A video generation API lets your code create videos the way a person would in a web editor: you send a brief (a topic or a script, a length, a voice, a language, a look), the service writes, narrates, animates and renders it, and you get back a file. For narrated explainers, the integration usually boils down to four calls: estimate the price, start the generation, wait for completion (polling or webhook), download the result. This guide walks through each step with the Motiongist REST API as the example, and covers the details that make an integration reliable in production.

When an API beats a web editor

If you make one video a week, use the studio. An API pays off when videos are a by-product of something your system already does:

  • Content pipelines: every new article, changelog entry or lesson gets a short explainer.
  • Product features: your users generate videos inside your own app.
  • Agencies and batch work: dozens of videos with the same look and voice, from a spreadsheet of topics.
  • Agents: an AI agent that needs a reliable, typed interface. (For chat assistants, the MCP server is usually simpler.)

The basics: base URL, keys and responses

The Motiongist API is a JSON REST API under https://motiongist.com/api/v1, available from the Studio plan. A few conventions apply everywhere:

  • Authentication: a REST key (it starts with mg_live_) in the X-API-Key header, or as Authorization: Bearer. Keys are created in your account’s developer settings and carry scopes such as jobs:write or videos:read.
  • Responses are wrapped as data plus a requestId; errors come back as error plus requestId, with a stable code (VALIDATION_ERROR, QUOTE_INVALID, INSUFFICIENT_CREDITS, RATE_LIMITED…). Log the requestId: it’s what support needs.
  • Lists use cursor pagination: pass the nextCursor you received to get the next page.
  • GET /api/v1 works without a key and returns the version, the endpoint list and a link to the OpenAPI document, handy for generating a typed client.

Start by calling GET /me (your workspace, plan, credit balance and rate limits) and GET /catalog (voices with audio samples, looks, lengths, the 10 narration languages and the credit costs). Your code should read voice names and options from the catalog instead of hardcoding them.

Step 1: build the brief

The brief describes the video. The fields you’ll use most:

FieldWhat it doesExample
subjectThe topic, in one lineHow a CDN works
inputWhere the content comes from: an idea, your script, or a document’s text{ kind: "script", text: "…" }
lengthshort (45–60 s), standard (90–120 s) or full (150–180 s)standard
languageNarration, captions and on-screen texten, es, pt, fr, de, it, nl, pl, tr, id
voiceOne of the eight catalog voicesGeorge
lookA preset (clean, night, toon), your brand, or a custom description{ mode: "preset", preset: "night" }
dimension2d, or 3d for perspective, depth and camera moves2d
notesThings that must appear or must be avoided{ mustShow: ["edge cache"] }

To start from a file, upload it with POST /assets (PDF, DOCX, TXT or Markdown, up to 20 MB): the response includes the extracted text, which you pass as a document input. Logos for the brand look are uploaded the same way.

Step 2: estimate and get a signed quote

Never start a paid job blind. POST /jobs/estimate with your brief returns the exact price in credits, line by line, an estimated generation time, whether your balance allows it, and a signed quote valid for one hour. Nothing is charged.

A typical estimate for a standard-length video with the final voice reads: video 700 credits, final voice 147 credits (1,470 characters at 1 credit per 10), total 847. Show that number to whoever approves spending, or compare it with a budget in code. You can also sanity-check costs in advance with the price calculator.

Step 3: start the generation

POST /jobs with the same brief and the quote. The response is a 202 with the job (status queued, stage script, progress 0) and its queue position. Two guarantees matter here:

  • The price is locked: with a quote, the job can’t cost more. If the brief changed since the estimate, the call fails with QUOTE_INVALID and you estimate again.
  • Failures are refunded: if a render fails after retries, its credits go back to the balance automatically. Cancelling with POST /jobs/{id}/cancel before rendering starts refunds too.

Use an Idempotency-Key

Networks fail at the worst moment. If your POST times out, did the job start or not? Send an Idempotency-Key header (a UUID per logical operation) on mutating POSTs: repeating the same key with the same body within 24 hours replays the first response instead of creating a second job. Generate the key once per video in your own database, not per HTTP attempt.

Projects: when a human reviews first

If someone should read the script or the storyboard before rendering, use projects instead of one-shot jobs. POST /projects creates a draft that also appears in the web studio. From there: POST /projects/{id}/script writes the script, POST /projects/{id}/narration returns a free audio preview, POST /projects/{id}/storyboard asks the engine for a free scene-by-scene plan that you can edit with PATCH /projects/{id}/storyboard, then POST /projects/{id}/estimate and POST /projects/{id}/generate with the quote. A new version of an already rendered project is billed as a revision, at half the base price.

Step 4: wait for the result

Polling

GET /jobs/{id} returns the status (queued, running, succeeded, failed, cancelled), the current stage (script, voice, storyboard, review, render), a progress percentage and, once finished, the videoId. Poll every 5 to 10 seconds at most. Renders take from a few minutes to about fifteen depending on length and queue, so polling is fine for scripts and prototypes.

Webhooks

For production, register a webhook with POST /webhooks: a URL and the events you want, among job.started, job.progress, job.succeeded, job.failed, job.cancelled, video.deleted, post.published and post.failed. The response includes a signing secret (whsec_…), shown only once. Each delivery is a JSON event with an id, a type, a timestamp and data (jobId, projectId, videoId…).

Verify every delivery. The X-Motiongist-Signature header has the form t=timestamp,v1=signature, where the signature is an HMAC-SHA256 of the timestamp, a period and the raw body, keyed with your secret. Recompute it on the raw body (before JSON parsing), compare in constant time, reject old timestamps, and deduplicate on the event id: your handler should be safe to run twice.

Step 5: download and use the files

GET /videos/{id} returns the metadata (duration, 1080×1920 size, voice, whether a watermark or a draft voice was used) and signed links valid for 15 minutes to the MP4, the SRT captions, a contact sheet, a poster and the storyboard. Download the files to your own storage right away, or fetch the video again for fresh links when you need them. The MP4 carries metadata marking it as AI-generated.

For distribution, GET /videos/{id}/post-copy returns a title, description and hashtags per platform within each platform’s limits, and PATCH /videos/{id}/share turns on an unlisted share page with an embed code.

Production checklist

  1. Keys live in a secret manager, with the narrowest scopes your integration needs.
  2. Every paid call goes through estimate → quote → generate, with a budget check in between.
  3. Mutating POSTs send an Idempotency-Key stored with your own record.
  4. Webhooks are verified, deduplicated and acknowledged fast (do the heavy work in a queue).
  5. You handle RATE_LIMITED (60 requests per minute per key) and INSUFFICIENT_CREDITS gracefully.
  6. Generated scripts are reviewed, by a person or by your own checks, before they’re published. You’re accountable for the content.
  7. You disclose AI-generated voice and visuals where platforms ask for it.

Common pitfalls

  • Treating the render as synchronous and holding an HTTP connection open for minutes.
  • Generating a new Idempotency-Key on each retry, which defeats its purpose.
  • Storing the 15-minute signed URLs in your database instead of the files or the video id.
  • Hardcoding voices or credit costs instead of reading GET /catalog.
  • Changing the brief after estimating and being surprised by QUOTE_INVALID: estimate again after any change.

The hard part of automated video isn’t the HTTP calls; it’s the content. Feed the API good inputs: a clear subject, the right length, and, when you can, a script written for the ear (script template). For examples of what comes out, browse the examples.

Your next video starts with one sentence

Type an idea, paste a script or a link: see what Motiongist makes of it in minutes.

Create my video