Get your API key
API & MCP

Build with GoMotion

Turn a script, or your own voiceover, into a finished documentary-style video from your code, from n8n or Make, or from an AI agent like Claude or Cursor. Same videos, same credits as the studio.

Plans
Included in every paid plan (Creator, Studio and Agency)
REST API
https://gomotionai.com/v1
MCP server
https://gomotionai.com/mcp

Quickstart

Your first video in three steps. Each program creates a short video, waits for it, exports the MP4 and prints the download link. These exact files run in our test suite before every release.

  1. Create an API key

    In the app, open API & MCP and click Create API key. It is shown once: store it somewhere safe.

    Open API & MCP
  2. Put it in your environment
    export GOMOTION_API_KEY="gmk_live_..."
  3. Run one of these
    #!/usr/bin/env bash
    # GoMotion API quickstart (curl + jq): script -> video -> MP4 download link
    set -euo pipefail
    API=https://gomotionai.com/v1
    AUTH="Authorization: Bearer $GOMOTION_API_KEY"
    
    # 1. Create the video (the Idempotency-Key makes a retry safe: it never charges twice)
    VIDEO=$(curl --fail-with-body -sS -X POST "$API/videos" -H "$AUTH" -H "Content-Type: application/json" \
      -H "Idempotency-Key: quickstart-$(date +%s)" \
      -d '{"script": "In 1969, three astronauts left Earth on a journey nobody had made before. Four days later, two of them walked on the Moon while the whole planet watched.", "title": "Apollo 11 in 30 seconds"}')
    ID=$(echo "$VIDEO" | jq -r .id)
    echo "video $ID: $(echo "$VIDEO" | jq -r .status), $(echo "$VIDEO" | jq -r .cost_credits) credits"
    
    # 2. Wait until it is ready
    while true; do
      STATUS=$(curl --fail-with-body -sS "$API/videos/$ID" -H "$AUTH" | jq -r .status)
      echo "status: $STATUS"
      case "$STATUS" in succeeded) break;; failed|cancelled|interrupted) exit 1;; esac
      sleep 10
    done
    
    # 3. Export it to MP4 (the first export of a video is free) and wait for the link
    EXPORT=$(curl --fail-with-body -sS -X POST "$API/videos/$ID/export" -H "$AUTH" -H "Content-Type: application/json" \
      -H "Idempotency-Key: quickstart-export-$ID" -d '{"quality": "medium"}')
    EID=$(echo "$EXPORT" | jq -r .id)
    while true; do
      E=$(curl --fail-with-body -sS "$API/exports/$EID" -H "$AUTH")
      case "$(echo "$E" | jq -r .status)" in succeeded) break;; failed|cancelled|interrupted) exit 1;; esac
      sleep 10
    done
    echo "download: $(echo "$E" | jq -r .download_url)"
    

    Needs jq.

    # GoMotion API quickstart (Python 3 + requests): script -> video -> MP4 download link
    import os, time, uuid
    import requests
    
    API = "https://gomotionai.com/v1"
    s = requests.Session()
    s.headers["Authorization"] = "Bearer " + os.environ["GOMOTION_API_KEY"]
    
    
    def call(method, path, **kw):
        """One request, retrying politely on 429/503 (the API tells you how long to wait)."""
        for _ in range(6):
            r = s.request(method, API + path, timeout=60, **kw)
            if r.status_code in (429, 503) and "Retry-After" in r.headers:
                time.sleep(min(int(r.headers["Retry-After"]), 120))
                continue
            if r.status_code >= 400:
                raise SystemExit("API error %s: %s" % (r.status_code, r.json().get("error")))
            return r.json()
        raise SystemExit("API still busy (%s): try again later" % r.status_code)
    
    
    def wait(path):
        while True:
            obj = call("GET", path)
            print(obj["object"], obj["id"], obj["status"])
            if obj["status"] == "succeeded":
                return obj
            if obj["status"] in ("failed", "cancelled", "interrupted"):
                raise SystemExit("%s %s: %s" % (obj["object"], obj["status"], obj["error"]))
            time.sleep(10)
    
    
    video = call("POST", "/videos", headers={"Idempotency-Key": str(uuid.uuid4())}, json={
        "script": "In 1969, three astronauts left Earth on a journey nobody had made before. "
                  "Four days later, two of them walked on the Moon while the whole planet watched.",
        "title": "Apollo 11 in 30 seconds",
    })
    print("video", video["id"], "costs", video["cost_credits"], "credits")
    wait("/videos/" + video["id"])
    
    export = call("POST", "/videos/%s/export" % video["id"],
                  headers={"Idempotency-Key": "export-" + video["id"]}, json={"quality": "medium"})
    done = wait("/exports/" + export["id"])
    print("download:", done["download_url"])
    

    pip install requests

    // GoMotion API quickstart (Node.js 18+, no dependencies): script -> video -> MP4 download link
    import { randomUUID } from "node:crypto";
    const API = "https://gomotionai.com/v1";
    const headers = { Authorization: `Bearer ${process.env.GOMOTION_API_KEY}`, "Content-Type": "application/json" };
    const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
    
    async function call(method, path, body, idempotencyKey) {
      for (let i = 0; i < 6; i++) {
        const h = idempotencyKey ? { ...headers, "Idempotency-Key": idempotencyKey } : headers;
        const r = await fetch(API + path, { method, headers: h, body: body ? JSON.stringify(body) : undefined });
        if ((r.status === 429 || r.status === 503) && r.headers.get("retry-after")) {
          await sleep(Math.min(Number(r.headers.get("retry-after")), 120) * 1000);
          continue;
        }
        const data = await r.json();
        if (!r.ok) throw new Error(`${r.status}: ${data.error}`);
        return data;
      }
      throw new Error("still busy, try again later");
    }
    
    async function wait(path) {
      for (;;) {
        const obj = await call("GET", path);
        console.log(obj.object, obj.id, obj.status);
        if (obj.status === "succeeded") return obj;
        if (["failed", "cancelled", "interrupted"].includes(obj.status)) throw new Error(obj.error);
        await sleep(10000);
      }
    }
    
    const video = await call("POST", "/videos", {
      script: "In 1969, three astronauts left Earth on a journey nobody had made before. " +
              "Four days later, two of them walked on the Moon while the whole planet watched.",
      title: "Apollo 11 in 30 seconds",
    }, randomUUID());
    console.log("video", video.id, "costs", video.cost_credits, "credits");
    await wait(`/videos/${video.id}`);
    const exp = await call("POST", `/videos/${video.id}/export`, { quality: "medium" }, `export-${video.id}`);
    const done = await wait(`/exports/${exp.id}`);
    console.log("download:", done.download_url);
    

    Node.js 18+, save as quickstart.mjs.

AI agents (MCP)

GoMotion has an MCP server, so AI agents such as Claude Code, Claude Desktop and Cursor can make videos for you. Tell your agent "make me a 10-minute history video about the fall of Rome" and it writes or fetches the script, creates the video, waits for it and hands you the download link.

1. Get an API key

Open gomotionai.com/app → API & MCP in the sidebar → Create API key. Copy it (it starts with gmk_live_); it is shown only once. API keys need a paid plan. You can give a key a daily credit cap to limit what an agent can spend per day.

2. Connect your agent

Replace gmk_live_YOUR_KEY with your key.

Claude Code

One command:

claude mcp add --transport http gomotion https://gomotionai.com/mcp \
  --header "Authorization: Bearer gmk_live_YOUR_KEY"

claude mcp list should say Connected (that only checks the connection, not the key): to check the key, ask Claude "How many GoMotion credits do I have?". Add --scope user to use it in every project.

Claude Desktop

Settings → Developer → Edit Config, then put this in claude_desktop_config.json (macOS: ~/Library/Application Support/Claude/claude_desktop_config.json, Windows: %APPDATA%\Claude\claude_desktop_config.json) and restart Claude Desktop. It needs Node.js 20 or newer.

{
  "mcpServers": {
    "gomotion": {
      "command": "npx",
      "args": [
        "-y",
        "mcp-remote",
        "https://gomotionai.com/mcp",
        "--header",
        "Authorization:${GOMOTION_AUTH}"
      ],
      "env": {
        "GOMOTION_AUTH": "Bearer gmk_live_YOUR_KEY"
      }
    }
  }
}

Claude Desktop runs local servers, so this uses mcp-remote (it needs Node.js 20 or newer) to connect to the remote server with your key. Restart Claude Desktop after saving.

Cursor

Put this in ~/.cursor/mcp.json (all projects) or .cursor/mcp.json (one project):

{
  "mcpServers": {
    "gomotion": {
      "url": "https://gomotionai.com/mcp",
      "headers": { "Authorization": "Bearer gmk_live_YOUR_KEY" }
    }
  }
}

Other MCP clients

Any client that lets you set an HTTP header for a remote MCP server: URL https://gomotionai.com/mcp, header Authorization: Bearer gmk_live_YOUR_KEY. For apps that only run local servers, use mcp-remote as in Claude Desktop above.

Quick check from a terminal (asks for your credit balance, so it also checks the key):

curl -s https://gomotionai.com/mcp -H "Authorization: Bearer gmk_live_YOUR_KEY" -H "Content-Type: application/json" -H "Accept: application/json, text/event-stream" -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"get_credits","arguments":{}}}'

3. Ask for a video

Example prompts:

Videos take several minutes to render. The agent gets a video id at once and then checks on it (about once a minute, or by waiting up to 50 seconds per check). If your agent cannot wait, it gives you the video id: ask again later, or find the video in the web app.

Tools

Tool What it does Spends credits
get_credits Balance, credits held by running jobs, this key's daily cap no
get_credit_history Recent credit movements no
get_account Plan, this key's limits and today's usage no
estimate_cost Price of a video by length, language and voice no
list_languages Narration languages no
list_voices Narrator voices with audio samples no
list_music_tracks Background music catalog by mood no
find_reference_videos Videos of a YouTube channel to imitate no
generate_script / get_script AI script writer (imitates 1-3 reference videos) plan allowance, then a few credits
create_video / get_video Create a video from a script, a generated script or your own voice-over; follow its progress yes
list_videos Your videos (web and API) no
cancel_video Cancel a queued or running video or export (refunded) no
export_video / get_export 1080p MP4 and its download link (the first export of a video is normally free) re-exports
list_exports Your exports with fresh download links no
create_upload Upload link for your own voice-over or music no
upload_file Upload a local audio file (local package only) no
generate_thumbnail / get_thumbnail AI YouTube thumbnail plan allowance, then a few credits

The agent also gets a ready-made prompt, make_video, with the whole flow.

Billing, limits and safety

Keys & authentication

Send the key on every request as Authorization: Bearer gmk_live_... (or X-API-Key: gmk_live_...). All calls are HTTPS to https://gomotionai.com/v1 with JSON bodies.

How a video is made

  1. POST /v1/videos with your script (or audio_key). The price is reserved from your credits; you get an id.
  2. Poll GET /v1/videos/{id} every 20–30 s. status: queued → running (with progress.percent) → succeeded, or failed/cancelled (credits come back: refunded: true). A 1-minute video usually takes a few minutes.
  3. POST /v1/videos/{id}/export renders the 1080p MP4 (the first export of each video is free).
  4. Poll GET /v1/exports/{id} until succeeded: download_url is a signed link valid 12 h (it is re-signed on every GET); the file is kept 48 h.

Instead of polling you can get a webhook when each step ends. Polling is free, but every call counts toward your requests-per-minute limit, so poll every 20–30 seconds.

Your own voiceover

Ask for an upload URL, PUT your audio file there, then create the video with audio_key and the length in seconds. Do the same with kind: "music" and music_key to use your own background track.

#!/usr/bin/env bash
# Your own voiceover (any common audio file) -> video
set -euo pipefail
API=https://gomotionai.com/v1
AUTH="Authorization: Bearer $GOMOTION_API_KEY"
FILE=${1:-voiceover.mp3}

# 1. Ask for an upload URL and PUT the file there (the URL is valid for 1 hour)
UP=$(curl --fail-with-body -sS -X POST "$API/uploads" -H "$AUTH" -H "Content-Type: application/json" -d '{"kind": "audio"}')
curl --fail-with-body -sS -X PUT --upload-file "$FILE" "$(echo "$UP" | jq -r .put_url)"

# 2. Create the video: duration_sec = length of your audio (you are billed on the real length)
SECONDS_LONG=$(ffprobe -v error -show_entries format=duration -of csv=p=0 "$FILE" | cut -d. -f1)
curl --fail-with-body -sS -X POST "$API/videos" -H "$AUTH" -H "Content-Type: application/json" \
  -H "Idempotency-Key: voice-$(date +%s)" \
  -d "{\"audio_key\": \"$(echo "$UP" | jq -r .key)\", \"duration_sec\": $SECONDS_LONG, \"title\": \"My narrated video\"}" | jq '{id, status, cost_credits}'

AI scripts & thumbnails

Scripts and thumbnails are written in the style of a YouTube channel you pick (channel_url, or up to 3 video_ids). With only a topic, we search YouTube for it and use 3 videos as references (same price; references_source: "topic_search"). They run in the background: POST returns 202 with an id; poll GET /v1/scripts/{id}.

# Write a script with AI in the style of a YouTube channel, then turn it into a video
import os, time, uuid
import requests

API = "https://gomotionai.com/v1"
H = {"Authorization": "Bearer " + os.environ["GOMOTION_API_KEY"]}


def ok(r):
    if r.status_code >= 400:
        raise SystemExit("API error %s: %s" % (r.status_code, r.json().get("error")))
    return r.json()


task = ok(requests.post(API + "/scripts", headers=dict(H, **{"Idempotency-Key": str(uuid.uuid4())}), json={
    "channel_url": "https://www.youtube.com/@NASA",
    "topic": "How the James Webb telescope unfolded in space",
    "minutes": 1,
}, timeout=60))
while task["status"] == "running":
    time.sleep(10)
    task = ok(requests.get(API + "/scripts/" + task["id"], headers=H, timeout=60))
if task["status"] != "succeeded":
    raise SystemExit(task["error"])
print(task["result"]["title"], "-", task["result"]["words"], "words")

video = ok(requests.post(API + "/videos", headers=dict(H, **{"Idempotency-Key": str(uuid.uuid4())}),
                         json={"script": task["result"]["script"], "title": task["result"]["title"]}, timeout=60))
print("video", video["id"], video["status"])

n8n & Make templates

Ready-to-import workflows for topic in a Google Sheet → GoMotion video → download link back in the sheet.

Download the n8n workflow Download the Make blueprint

The sheet needs columns topic, channel_url, script, title, status, video_id, download_url, error and (optional) voice — a voice id from GET /v1/voices. Rows with a script go straight to a video; rows with only a topic get an AI script first, written in the style of the YouTube channel in channel_url (required for topic rows). If a row fails, the template writes failed and the reason in error; clear status to try that row again.

Webhooks

Get a POST on your server when a video, export, script or thumbnail created by your key ends. Add an endpoint with POST /v1/webhooks (up to 3 per key). The answer includes the signing secret: it is shown only there and on rotate-secret.

curl --fail-with-body -sS -X POST "https://gomotionai.com/v1/webhooks" \
  -H "Authorization: Bearer $GOMOTION_API_KEY" \
  -H "Content-Type: application/json" -d '{"url": "https://yourshop.com/gomotion/webhook", "events": ["video.succeeded", "video.failed", "export.succeeded", "export.failed"]}'

Events: video.succeeded, video.failed, video.cancelled, export.succeeded, export.failed, export.cancelled, script.succeeded, script.failed, thumbnail.succeeded, thumbnail.failed (default: all). Each POST body is an event whose data.object is the same object GET /v1/videos/{id}, /v1/exports/{id}, /v1/scripts/{id} or /v1/thumbnails/{id} returns:

{
  "api_version": "2026-11-01",
  "created_at": 1793535293,
  "data": {
    "object": {
      "cost_credits": 0,
      "created_at": 1793535290,
      "download_filename": "Acme Films - order 0.mp4",
      "download_url": "https://<account>.r2.cloudflarestorage.com/gomotion/t/t_5f3a91c2e4b7/downloads/j_73776a5038734181.mp4?X-Amz-Signature=...&response-content-disposition=attachment%3B%20filename%3D%22Acme%20Films%20-%20order%200.mp4%22",
      "download_url_expires_in_s": 43200,
      "error": null,
      "external_id": "ord-0",
      "id": "j_73776a5038734181",
      "metadata": {
        "n": "0",
        "order_id": "o0",
        "shop": "acme"
      },
      "object": "export",
      "progress": {
        "eta_s": 0,
        "percent": 100,
        "phase": "Completed"
      },
      "project_id": "p_d47e376ac26f",
      "queue_position": null,
      "refunded": false,
      "status": "succeeded",
      "updated_at": 1793535291
    }
  },
  "id": "evt_5f3a91c2e4b74e64916c",
  "object": "event",
  "type": "export.succeeded"
}

Verify the signature

Header GoMotion-Signature: t=<unix>,v1=<hex>, where v1 = HMAC-SHA256 of "<t>.<raw body>" with your secret. Use the raw body bytes, compare in constant time and reject t older than 5 minutes. This exact code runs in our test suite.

import crypto from "node:crypto";

// rawBody: the request bytes exactly as received (e.g. express.raw({ type: "application/json" })).
// header: the GoMotion-Signature header.
export function verifyGoMotion(rawBody, header, secret, toleranceSec = 300) {
  const parts = Object.fromEntries(header.split(",").map((p) => p.split("=", 2)));
  const t = Number(parts.t);
  if (!t || Math.abs(Date.now() / 1000 - t) > toleranceSec) return false;
  const expected = crypto.createHmac("sha256", secret).update(`${t}.`).update(rawBody).digest("hex");
  const a = Buffer.from(expected), b = Buffer.from(parts.v1 || "");
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}
import hashlib, hmac, time


def verify_gomotion(raw_body: bytes, header: str, secret: str, tolerance: int = 300) -> bool:
    """raw_body: the request bytes exactly as received. header: GoMotion-Signature."""
    parts = dict(p.split("=", 1) for p in header.split(",") if "=" in p)
    t = parts.get("t", "")
    if not t.isdigit() or abs(time.time() - int(t)) > tolerance:
        return False
    expected = hmac.new(secret.encode(), t.encode() + b"." + raw_body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, parts.get("v1", ""))

Build on GoMotion / resell

Sell videos under your own brand. Your customer talks to your site; only your backend talks to GoMotion.

  1. Your site takes the order. Your backend (it holds the API key, never the browser) calls POST /v1/videos with external_id (your order id) and metadata (up to 20 keys). Both come back on every object and webhook.
  2. Wait for the video.succeeded webhook, or poll GET /v1/videos/{id}.
  3. POST /v1/videos/{id}/export with download_filename (e.g. "Acme Films - order 1001"): the MP4 downloads with your name, not ours.
  4. On export.succeeded, download the MP4 from download_url and store it on your own storage. The link lasts 12 h and the file 48 h: never give our link to your customer.

Find orders later with GET /v1/videos?external_id=order-1001 or ?metadata[shop]=acme (also on GET /v1/exports). Page with starting_after = next_starting_after of the previous page.

Reseller credit pack

0.0125 USD per credit, minimum 20,000 credits (250 USD). A video costs the same credits as in the app: voice quality, length, thumbnails and AI scripts change the total, so quote each order with GET /v1/quote before you set your price. Questions and bigger limits: support@gomotionai.com.

Good to know

Starter kit — a ready white-label site (order form, status page, download) you deploy yourself.
  1. Download gomotion-reseller-kit.zip.
  2. Put your API key in the server environment (GOMOTION_API_KEY), never in the page.
  3. Set your brand and price per credit in kit.config.js.
  4. Deploy it (Vercel, Netlify or Cloudflare) and add its webhook URL with POST /v1/webhooks.

Download the starter kit

Credits per call

CallCredits
Create a videoSame price as the studio: by length, voice quality and language. Ask first with GET /v1/quote. Refunded if it fails or you cancel it.
Export to MP4First export of a video: free. Re-exports: 4.
AI script / thumbnail4 each, or free while your plan's monthly allowance lasts.
Everything else (status, lists, voices, quote, uploads, references)Free

Your own voiceover is billed on its real length, measured by our server. Using the API or MCP costs nothing extra.

Limits per plan

Generous by design: these are safety rails, not a paywall.

LimitCreatorStudioAgency
Videos per day per key1003001,000
API videos per day (all keys)1504001,500
Rendering at once (shared with the studio)51020
Active keys3510
Requests per minute per key6060120
Requests per minute (all keys)120180300
Paid requests per minute102030
Scripts and thumbnails at once123

When you hit one you get 429 with a Retry-After header. GET /v1/account shows your current limits and today's usage of the key.

Need higher limits? Email support@gomotionai.com with what you are building and the volume you need, and we will raise them. Then custom_limits is true in GET /v1/account.

Idempotency

Never pay twice for the same request. Every call that spends credits (POST /v1/videos, /export, /v1/scripts, /v1/thumbnails) needs an Idempotency-Key header: any unique string (8–64 letters, digits, - or _), e.g. a UUID or your own row id. If a request times out, send it again with the same key and body: you get the same video back (200) instead of a new charge. The same key with a different body is a 409. Keys are remembered 24 h. If your own id is longer than 64 characters, send a hash of it.

Errors

Errors are JSON: {"error": "message", ...}. Newer errors also carry a stable code.

StatusMeaning
400Bad request: a field is missing or wrong. The message says which.
401Missing, wrong or revoked API key.
402 plan_requiredYour plan doesn't include the API (required_plan says which does).
402Not enough credits (needed, available). Nothing was charged.
403The key doesn't have the scope this call needs.
403 api_not_enabledThe API is not enabled for this account.
404Unknown id (or it belongs to another account).
400 dns / blocked_addressWebhook URL: the host doesn't resolve, or resolves to a private address.
409Idempotency-Key reused with a different body, or the video isn't ready to export.
409 webhook_limitThis key already has 3 webhooks.
410The project expired (videos are kept 48 h): create the video again.
413Too long (over 90 minutes).
422Valid request we can't do: above max_cost_credits, channel without videos.
422 topic_no_referencesTopic-only script: no YouTube videos found for the topic. Nothing charged.
429Rate limit (per key or account) or daily credit cap of the key. Wait Retry-After seconds.
429 key_videos_in_flightThis key already has its maximum of videos in progress.
429 key_videos_per_dayThis key reached its videos per day (resets 00:00 UTC).
429 account_videos_per_dayYour account reached its API videos per day across all keys (resets 00:00 UTC).
429 api_capacityThe API share of our render capacity is full right now. Nothing charged; retry after Retry-After.
502YouTube didn't let us read a channel (references). Nothing charged.
502 topic_search_failedTopic-only script: YouTube search failed. Nothing charged; retry in a minute.
503 api_pausedThe API is paused for maintenance. Nothing charged.
503 webhooks_pausedWebhooks are paused; nothing is lost and polling works as usual.
503An update or a provider outage: new videos are paused for a few minutes. Nothing charged.
{
  "error": "daily credit cap reached for this key",
  "cap": 170,
  "spent": 75,
  "needed": 207,
  "retry_after": 41319
}

Endpoint reference

Base URL https://gomotionai.com. Machine-readable: /openapi.json (OpenAPI 3.1).

Videos

POST/v1/videos

Create a video. Charges the video price when accepted (refunded if it fails). Poll GET /v1/videos/{id}.

Scope: videos:write · Credits: video price (GET /v1/quote)

FieldInTypeDescription
Idempotency-Keyheaderstring · requiredAny unique string per new request. Retrying with the same key and body returns the same object (never charges twice).
scriptbodystring · maxLength 50000The narration text (TTS voice). One of script, script_key, audio_key.
script_keybodystringKey from POST /v1/uploads/script (for long scripts).
audio_keybodystringKey from POST /v1/uploads with kind=audio: your own voiceover (no TTS).
duration_secbodyinteger · minimum 1, maximum 5400Length of your audio in seconds (required with audio_key). You are billed on the real length the server measures.
langbodystringLanguage code from GET /v1/languages, or auto. Default en (script) / auto (audio).
voicebodystringVoice id from GET /v1/voices (script videos only).
titlebodystring · maxLength 150
subtitlesbodyboolean · default true
animationsbodyboolean · default true
backgroundbodystringblue (default), cinematic, vintage, light or grid
no_musicbodyboolean · default false
music_volumebodynumber · minimum 0, maximum 1, default 0.03
music_stylebodyone of: epic, documentary, tension, sad, chill, upbeat
music_trackbodystring'<mood>/<file>.mp3' from the music catalog
music_keybodystringKey from POST /v1/uploads with kind=music: your own background track.
max_cost_creditsbodyinteger · minimum 1, maximum 100000Refuse (422) if the video would cost more; never charged above it. Not with audio_key.
optionsbodyobjectSame fields nested (accepted for web-body compatibility).
external_idbodystringYour own order or customer id: 1-128 printable ASCII characters, no spaces. Filter lists with ?external_id=.
metadatabodyobjectYour own data, echoed on the object and on webhooks: up to 20 keys [A-Za-z0-9_.-]{1,40}, string values up to 500 characters (numbers become strings), 8 KB in total.
curl --fail-with-body -sS -X POST "https://gomotionai.com/v1/videos" \
  -H "Authorization: Bearer $GOMOTION_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" -d '{"script": "In 1969, three astronauts left Earth...", "title": "Apollo 11", "voice": "aurora", "background": "cinematic", "music_style": "documentary"}'

Response 201 (real response from our staging server):

{
  "cost_credits": 75,
  "created_at": 1793535286,
  "error": null,
  "estimated_duration_sec": 32.0,
  "external_id": "ord-0",
  "id": "j_a384eca193e1458f",
  "metadata": {
    "n": "0",
    "order_id": "o0",
    "shop": "acme"
  },
  "object": "video",
  "progress": null,
  "project_id": null,
  "queue_position": null,
  "refunded": false,
  "status": "running",
  "updated_at": 1793535286
}
GET/v1/videos

List videos (newest first)

Scope: videos:read · Credits: free

FieldInTypeDescription
limitqueryinteger
statusqueryone of: queued, running, succeeded, failed, cancelled, interrupted
starting_afterquerystringCursor: the last id of the previous page (next_starting_after).
external_idquerystringOnly objects created with this external_id.
metadataqueryobjectmetadata[key]=value: only objects whose metadata has that value (up to 5 keys; metadata.key=value also works).
curl --fail-with-body -sS "https://gomotionai.com/v1/videos?limit=10&status=succeeded" \
  -H "Authorization: Bearer $GOMOTION_API_KEY"

Response 200 (real response from our staging server):

{
  "data": [
    {
      "cost_credits": 75,
      "created_at": 1793535286,
      "error": null,
      "estimated_duration_sec": 32.0,
      "external_id": "ord-0",
      "id": "j_a384eca193e1458f",
      "metadata": {
        "n": "0",
        "order_id": "o0",
        "shop": "acme"
      },
      "object": "video",
      "progress": {
        "eta_s": 0,
        "percent": 100,
        "phase": "Completed"
      },
      "project_id": "p_d47e376ac26f",
      "queue_position": null,
      "refunded": false,
      "status": "succeeded",
      "updated_at": 1793535287
    },
    {
      "cost_credits": 75,
      "created_at": 1793535286,
      "error": null,
      "estimated_duration_sec": 32.0,
      "external_id": "ord-1",
      "id": "j_a92d9fc8e6ab428c",
      "metadata": {
        "n": "1",
        "order_id": "o1",
        "shop": "acme"
      },
      "object": "video",
      "progress": {
        "eta_s": 0,
        "percent": 100,
        "phase": "Completed"
      },
      "project_id": "p_28f68f8f6ba1",
      "queue_position": null,
      "refunded": false,
      "status": "succeeded",
      "updated_at": 1793535288
    }
  ],
  "has_more": true,
  "next_starting_after": "j_a92d9fc8e6ab428c",
  "object": "list"
}
GET/v1/videos/{id}

Get a video

Scope: videos:read · Credits: free

FieldInTypeDescription
idpathstring · required
curl --fail-with-body -sS "https://gomotionai.com/v1/videos/$VIDEO_ID" \
  -H "Authorization: Bearer $GOMOTION_API_KEY"

Response 200 (real response from our staging server):

{
  "cost_credits": 75,
  "created_at": 1793535286,
  "error": null,
  "estimated_duration_sec": 32.0,
  "external_id": "ord-0",
  "id": "j_a384eca193e1458f",
  "metadata": {
    "n": "0",
    "order_id": "o0",
    "shop": "acme"
  },
  "object": "video",
  "progress": null,
  "project_id": null,
  "queue_position": null,
  "refunded": false,
  "status": "running",
  "updated_at": 1793535286
}
POST/v1/jobs/{id}/cancel

Cancel a video or export (credits come back)

Scope: videos:write · Credits: free

FieldInTypeDescription
idpathstring · required
curl --fail-with-body -sS -X POST "https://gomotionai.com/v1/jobs/$VIDEO_ID/cancel" \
  -H "Authorization: Bearer $GOMOTION_API_KEY"

Response 200 (real response from our staging server):

{
  "cost_credits": 75,
  "created_at": 1793535298,
  "error": null,
  "estimated_duration_sec": 33.0,
  "external_id": null,
  "id": "j_89ece89df61c47aa",
  "metadata": {},
  "object": "video",
  "progress": null,
  "project_id": null,
  "queue_position": null,
  "refunded": true,
  "status": "cancelled",
  "updated_at": 1793535298
}

Exports

POST/v1/videos/{id}/export

Export the video to MP4 (1080p)

Scope: videos:write · Credits: first export free, then 4

FieldInTypeDescription
idpathstring · required
Idempotency-Keyheaderstring · requiredAny unique string per new request. Retrying with the same key and body returns the same object (never charges twice).
qualitybodyone of: high, medium, fast · default "medium"
download_filenamebodystringFile name of the download (your brand, not GoMotion's); .mp4 is added. Default gomotion_video.mp4.
external_idbodystringDefault: the video's.
metadatabodyobjectDefault: the video's metadata.
curl --fail-with-body -sS -X POST "https://gomotionai.com/v1/videos/$VIDEO_ID/export" \
  -H "Authorization: Bearer $GOMOTION_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" -d '{"quality": "high", "download_filename": "Acme Films - order 1001"}'

Response 201 (real response from our staging server):

{
  "cost_credits": 0,
  "created_at": 1793535290,
  "download_filename": null,
  "download_url": null,
  "download_url_expires_in_s": null,
  "error": null,
  "external_id": "ord-0",
  "id": "j_73776a5038734181",
  "metadata": {
    "n": "0",
    "order_id": "o0",
    "shop": "acme"
  },
  "object": "export",
  "progress": null,
  "project_id": "p_d47e376ac26f",
  "queue_position": null,
  "refunded": false,
  "status": "running",
  "updated_at": 1793535290
}
GET/v1/exports

List exports (newest first)

Scope: videos:read · Credits: free

FieldInTypeDescription
limitqueryinteger
starting_afterquerystringCursor: the last id of the previous page (next_starting_after).
external_idquerystringOnly objects created with this external_id.
metadataqueryobjectmetadata[key]=value: only objects whose metadata has that value (up to 5 keys; metadata.key=value also works).
curl --fail-with-body -sS "https://gomotionai.com/v1/exports?limit=10&external_id=order-1001" \
  -H "Authorization: Bearer $GOMOTION_API_KEY"

Response 200 (real response from our staging server):

{
  "data": [
    {
      "cost_credits": 0,
      "created_at": 1793535290,
      "download_filename": "Acme Films - order 0.mp4",
      "download_url": "https://<account>.r2.cloudflarestorage.com/gomotion/t/t_5f3a91c2e4b7/downloads/j_73776a5038734181.mp4?X-Amz-Signature=...&response-content-disposition=attachment%3B%20filename%3D%22Acme%20Films%20-%20order%200.mp4%22",
      "download_url_expires_in_s": 43200,
      "error": null,
      "external_id": "ord-0",
      "id": "j_73776a5038734181",
      "metadata": {
        "n": "0",
        "order_id": "o0",
        "shop": "acme"
      },
      "object": "export",
      "progress": {
        "eta_s": 0,
        "percent": 100,
        "phase": "Completed"
      },
      "project_id": "p_d47e376ac26f",
      "queue_position": null,
      "refunded": false,
      "status": "succeeded",
      "updated_at": 1793535291
    }
  ],
  "has_more": true,
  "next_starting_after": "j_73776a5038734181",
  "object": "list"
}
GET/v1/exports/{id}

Get an export (download_url when succeeded)

Scope: videos:read · Credits: free

FieldInTypeDescription
idpathstring · required
curl --fail-with-body -sS "https://gomotionai.com/v1/exports/$EXPORT_ID" \
  -H "Authorization: Bearer $GOMOTION_API_KEY"

Response 200 (real response from our staging server):

{
  "cost_credits": 0,
  "created_at": 1793535290,
  "download_filename": null,
  "download_url": null,
  "download_url_expires_in_s": null,
  "error": null,
  "external_id": "ord-0",
  "id": "j_73776a5038734181",
  "metadata": {
    "n": "0",
    "order_id": "o0",
    "shop": "acme"
  },
  "object": "export",
  "progress": null,
  "project_id": "p_d47e376ac26f",
  "queue_position": null,
  "refunded": false,
  "status": "running",
  "updated_at": 1793535290
}

Uploads

POST/v1/uploads/script

Upload a long script

Scope: videos:write · Credits: free

FieldInTypeDescription
textbodystring · required · maxLength 50000
curl --fail-with-body -sS -X POST "https://gomotionai.com/v1/uploads/script" \
  -H "Authorization: Bearer $GOMOTION_API_KEY" \
  -H "Content-Type: application/json" -d '{"text": "In 1969 three astronauts left Earth on a journey nobody had made before. Four days later two of them walked on the Moon while the whole planet watched on television.In 1969 three astronauts left Earth on a journey nobody had made before. Four days later two of them walked on the Moon while the whole planet watched on television.In 1969 three astronauts left Earth on a journey nobody had made before. Four days later two of them walked on the Moon while the whole planet watched on television.In 1969 three astronauts left Earth on a journey nobody had made before. Four days later two of them walked on the Moon while the whole planet watched on television.In 1969 three astronauts left Earth on a journey nobody had made before. Four days later two of them walked on the Moon while the whole planet watched on television."}'

Response 201 (real response from our staging server):

{
  "key": "t/t_5f3a91c2e4b7/uploads/136ae2dbcc2a/script.txt",
  "chars": 825,
  "expires_in_hours": 60
}
POST/v1/uploads

Get a signed URL to upload your own voiceover or music. Then PUT the file bytes to put_url (any common audio format) within 1 hour, and pass key as audio_key or music_key to POST /v1/videos.

Scope: videos:write · Credits: free

FieldInTypeDescription
kindbodyone of: audio, music · required
curl --fail-with-body -sS -X POST "https://gomotionai.com/v1/uploads" \
  -H "Authorization: Bearer $GOMOTION_API_KEY" \
  -H "Content-Type: application/json" -d '{"kind": "audio"}'

Response 201 (real response from our staging server):

{
  "object": "upload",
  "kind": "audio",
  "key": "t/t_5f3a91c2e4b7/uploads/40a30b320493/audio.mp3",
  "put_url": "https://<account>.r2.cloudflarestorage.com/gomotion/t/t_5f3a91c2e4b7/uploads/40a30b320493/audio.mp3?X-Amz-Signature=...",
  "method": "PUT",
  "expires_in_s": 3600,
  "max_bytes": 100663296
}

Voices

GET/v1/languages

Voice languages

Scope: any key · Credits: free

curl --fail-with-body -sS "https://gomotionai.com/v1/languages" \
  -H "Authorization: Bearer $GOMOTION_API_KEY"

Response 200 (real response from our production server):

{
  "languages": [
    {
      "code": "en",
      "name": "English",
      "model": "vega",
      "chars_per_min": 998,
      "quality": "standard"
    },
    {
      "code": "nl",
      "name": "Dutch",
      "model": "nova",
      "chars_per_min": 774,
      "quality": "high"
    },
    {
      "code": "fr",
      "name": "French",
      "model": "nova",
      "chars_per_min": 812,
      "quality": "high"
    }
  ]
}
GET/v1/voices

Voices of a language

Scope: any key · Credits: free

FieldInTypeDescription
langquerystringLanguage code (default en).
qualityqueryone of: standard, high
curl --fail-with-body -sS "https://gomotionai.com/v1/voices?lang=en&quality=standard" \
  -H "Authorization: Bearer $GOMOTION_API_KEY"

Response 200 (real response from our production server):

{
  "lang": "en",
  "voices": [
    {
      "id": "aurora",
      "name": "Aurora",
      "gender": "female",
      "age": "",
      "style": "",
      "region": "US",
      "model": "vega",
      "sample_url": "https://static-files.genaipro.io/vega-samples/aurora.mp3",
      "quality": "standard"
    },
    {
      "id": "luna",
      "name": "Luna",
      "gender": "female",
      "age": "",
      "style": "",
      "region": "US",
      "model": "vega",
      "sample_url": "https://static-files.genaipro.io/vega-samples/luna.mp3",
      "quality": "standard"
    },
    {
      "id": "stella",
      "name": "Stella",
      "gender": "female",
      "age": "",
      "style": "",
      "region": "UK",
      "model": "vega",
      "sample_url": "https://static-files.genaipro.io/vega-samples/stella.mp3",
      "quality": "standard"
    }
  ]
}

Scripts & thumbnails

GET/v1/references

Videos of a YouTube channel (references for scripts and thumbnails)

Scope: scripts:write or thumbnails:write · Credits: free

FieldInTypeDescription
channel_urlquerystring · required
sortqueryone of: recent, popular
curl --fail-with-body -sS "https://gomotionai.com/v1/references?channel_url=https://www.youtube.com/@NASA" \
  -H "Authorization: Bearer $GOMOTION_API_KEY"

Response 200 (real response from our staging server):

{
  "object": "references",
  "type": "channel",
  "sort": "recent",
  "videos": [
    {
      "id": "dQw4w9WgXcA",
      "title": "Reference video 0",
      "url": "https://www.youtube.com/watch?v=dQw4w9WgXcA",
      "thumbnail_url": null,
      "duration_sec": 600,
      "views": 0
    },
    {
      "id": "dQw4w9WgXcB",
      "title": "Reference video 1",
      "url": "https://www.youtube.com/watch?v=dQw4w9WgXcB",
      "thumbnail_url": null,
      "duration_sec": 601,
      "views": 1000
    },
    {
      "id": "dQw4w9WgXcC",
      "title": "Reference video 2",
      "url": "https://www.youtube.com/watch?v=dQw4w9WgXcC",
      "thumbnail_url": null,
      "duration_sec": 602,
      "views": 2000
    }
  ],
  "credits": {
    "script": 4,
    "thumbnail": 4
  },
  "included_in_plan_left": {
    "scripts": 0,
    "thumbnails": 0
  }
}
POST/v1/scripts

Write a script with AI (async). Give channel_url, video_ids or only topic (then GoMotion finds 3 references on YouTube: 422 topic_no_references / 502 topic_search_failed, nothing charged).

Scope: scripts:write · Credits: 4 (or included in plan)

FieldInTypeDescription
Idempotency-Keyheaderstring · requiredAny unique string per new request. Retrying with the same key and body returns the same object (never charges twice).
channel_urlbodystring · maxLength 500YouTube channel link; the first 3 videos are the references. Or pass video_ids.
video_idsbodyarray · maxItems 3
reference_titlesbodyarray · maxItems 3
topicbodystring · maxLength 400, minLength 8What the script is about. Alone (no channel_url / video_ids) GoMotion searches YouTube for the topic and uses 3 videos as style references (same price).
minutesbodyinteger · minimum 1, maximum 90, default 8
langbodystringForce the script language (e.g. en, es).
sortbodyone of: recent, popular · default "recent"
external_idbodystringYour own order or customer id: 1-128 printable ASCII characters, no spaces. Filter lists with ?external_id=.
metadatabodyobjectYour own data, echoed on the object and on webhooks: up to 20 keys [A-Za-z0-9_.-]{1,40}, string values up to 500 characters (numbers become strings), 8 KB in total.
curl --fail-with-body -sS -X POST "https://gomotionai.com/v1/scripts" \
  -H "Authorization: Bearer $GOMOTION_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" -d '{"channel_url": "https://www.youtube.com/@NASA", "topic": "How Webb unfolded in space", "minutes": 1}'

Response 202 (real response from our staging server):

{
  "created_at": 1793535294,
  "error": null,
  "external_id": "scr-1",
  "id": "scr_8c6265cf04fa45fd",
  "metadata": {
    "o": "1"
  },
  "object": "script",
  "references_source": "topic_search",
  "result": null,
  "status": "running",
  "updated_at": 1793535294
}
GET/v1/scripts/{id}

Get a script task

Scope: scripts:write · Credits: free

FieldInTypeDescription
idpathstring · required
curl --fail-with-body -sS "https://gomotionai.com/v1/scripts/$SCRIPT_ID" \
  -H "Authorization: Bearer $GOMOTION_API_KEY"

Response 200 (real response from our staging server):

{
  "created_at": 1793535294,
  "error": null,
  "external_id": "scr-1",
  "id": "scr_8c6265cf04fa45fd",
  "metadata": {
    "o": "1"
  },
  "object": "script",
  "references_source": "topic_search",
  "result": null,
  "status": "running",
  "updated_at": 1793535294
}
POST/v1/thumbnails

Make a thumbnail with AI (async)

Scope: thumbnails:write · Credits: 4 (or included in plan)

FieldInTypeDescription
Idempotency-Keyheaderstring · requiredAny unique string per new request. Retrying with the same key and body returns the same object (never charges twice).
titlebodystring · required · maxLength 300
textbodystring · maxLength 120
subjectbodystring · maxLength 300
scriptbodystring · maxLength 50000
with_textbodyboolean · default true
channel_urlbodystring · maxLength 500
video_idsbodyarray · maxItems 3
sortbodyone of: recent, popular · default "recent"
external_idbodystringYour own order or customer id: 1-128 printable ASCII characters, no spaces. Filter lists with ?external_id=.
metadatabodyobjectYour own data, echoed on the object and on webhooks: up to 20 keys [A-Za-z0-9_.-]{1,40}, string values up to 500 characters (numbers become strings), 8 KB in total.
curl --fail-with-body -sS -X POST "https://gomotionai.com/v1/thumbnails" \
  -H "Authorization: Bearer $GOMOTION_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" -d '{"title": "Webb unfolds", "video_ids": ["dQw4w9WgXcQ"]}'

Response 202 (real response from our staging server):

{
  "created_at": 1793535297,
  "error": null,
  "external_id": "thm-1",
  "id": "thm_09a9e88bc6094f11",
  "metadata": {
    "o": "2"
  },
  "object": "thumbnail",
  "result": null,
  "status": "running",
  "updated_at": 1793535297
}
GET/v1/thumbnails/{id}

Get a thumbnail task

Scope: thumbnails:write · Credits: free

FieldInTypeDescription
idpathstring · required
curl --fail-with-body -sS "https://gomotionai.com/v1/thumbnails/$THUMBNAIL_ID" \
  -H "Authorization: Bearer $GOMOTION_API_KEY"

Response 200 (real response from our staging server):

{
  "created_at": 1793535297,
  "error": null,
  "external_id": "thm-1",
  "id": "thm_09a9e88bc6094f11",
  "metadata": {
    "o": "2"
  },
  "object": "thumbnail",
  "result": null,
  "status": "running",
  "updated_at": 1793535297
}

Webhooks

POST/v1/webhooks

Add a webhook endpoint (max 3 per key). Events for videos, exports, scripts and thumbnails created by THIS key, sent when they end. The secret is shown only here and on rotate-secret.

Scope: videos:read or videos:write · Credits: free

FieldInTypeDescription
urlbodystring · required · maxLength 2000https, port 443, a public host name (no IP, no localhost/.local/.internal); every address it resolves to must be public. 10 s timeout, no redirects.
eventsbodyarrayDefault (or ["*"]): all events.
descriptionbodystring · maxLength 200
curl --fail-with-body -sS -X POST "https://gomotionai.com/v1/webhooks" \
  -H "Authorization: Bearer $GOMOTION_API_KEY" \
  -H "Content-Type: application/json" -d '{"url": "https://yourshop.com/gomotion/webhook", "events": ["video.succeeded", "video.failed", "export.succeeded", "export.failed"]}'

Response 201 (real response from our staging server):

{
  "created_at": 1793535285,
  "description": "drift",
  "disabled_reason": null,
  "events": [
    "video.succeeded",
    "video.failed",
    "video.cancelled",
    "export.succeeded",
    "export.failed",
    "export.cancelled",
    "script.succeeded",
    "script.failed",
    "thumbnail.succeeded",
    "thumbnail.failed"
  ],
  "failed_in_a_row": 0,
  "id": "wh_a0f047599f69410c",
  "last_failure_at": null,
  "last_success_at": null,
  "object": "webhook",
  "secret": "whsec_...",
  "status": "enabled",
  "url": "https://hooks.yourshop.com/drift/hook"
}
GET/v1/webhooks

List this key's webhooks

Scope: videos:read or videos:write · Credits: free

curl --fail-with-body -sS "https://gomotionai.com/v1/webhooks" \
  -H "Authorization: Bearer $GOMOTION_API_KEY"

Response 200 (real response from our staging server):

{
  "data": [
    {
      "created_at": 1793535285,
      "description": "drift",
      "disabled_reason": null,
      "events": [
        "video.succeeded",
        "video.failed",
        "video.cancelled",
        "export.succeeded",
        "export.failed",
        "export.cancelled",
        "script.succeeded",
        "script.failed",
        "thumbnail.succeeded",
        "thumbnail.failed"
      ],
      "failed_in_a_row": 0,
      "id": "wh_a0f047599f69410c",
      "last_failure_at": null,
      "last_success_at": null,
      "object": "webhook",
      "status": "enabled",
      "url": "https://hooks.yourshop.com/drift/hook"
    }
  ],
  "object": "list"
}
GET/v1/webhooks/{id}

Get a webhook

Scope: videos:read or videos:write · Credits: free

FieldInTypeDescription
idpathstring · required
curl --fail-with-body -sS "https://gomotionai.com/v1/webhooks/$WEBHOOK_ID" \
  -H "Authorization: Bearer $GOMOTION_API_KEY"

Response 200 (real response from our staging server):

{
  "created_at": 1793535285,
  "description": "drift",
  "disabled_reason": null,
  "events": [
    "video.succeeded",
    "video.failed",
    "video.cancelled",
    "export.succeeded",
    "export.failed",
    "export.cancelled",
    "script.succeeded",
    "script.failed",
    "thumbnail.succeeded",
    "thumbnail.failed"
  ],
  "failed_in_a_row": 0,
  "id": "wh_a0f047599f69410c",
  "last_failure_at": null,
  "last_success_at": null,
  "object": "webhook",
  "status": "enabled",
  "url": "https://hooks.yourshop.com/drift/hook"
}
POST/v1/webhooks/{id}

Update a webhook (url, events, description, enabled)

Scope: videos:read or videos:write · Credits: free

FieldInTypeDescription
idpathstring · required
urlbodystring · maxLength 2000https, port 443, a public host name (no IP, no localhost/.local/.internal); every address it resolves to must be public. 10 s timeout, no redirects.
eventsbodyarrayDefault (or ["*"]): all events.
descriptionbodystring · maxLength 200
enabledbodybooleantrue re-enables an endpoint disabled after failures.
curl --fail-with-body -sS -X POST "https://gomotionai.com/v1/webhooks/$WEBHOOK_ID" \
  -H "Authorization: Bearer $GOMOTION_API_KEY" \
  -H "Content-Type: application/json" -d '{"enabled": true}'

Response 200 (real response from our staging server):

{
  "created_at": 1793535285,
  "description": "drift 2",
  "disabled_reason": null,
  "events": [
    "video.succeeded",
    "video.failed",
    "video.cancelled",
    "export.succeeded",
    "export.failed",
    "export.cancelled",
    "script.succeeded",
    "script.failed",
    "thumbnail.succeeded",
    "thumbnail.failed"
  ],
  "failed_in_a_row": 0,
  "id": "wh_a0f047599f69410c",
  "last_failure_at": null,
  "last_success_at": null,
  "object": "webhook",
  "status": "enabled",
  "url": "https://hooks.yourshop.com/drift/hook"
}
DELETE/v1/webhooks/{id}

Delete a webhook

Scope: videos:read or videos:write · Credits: free

FieldInTypeDescription
idpathstring · required
curl --fail-with-body -sS -X DELETE "https://gomotionai.com/v1/webhooks/$WEBHOOK_ID" \
  -H "Authorization: Bearer $GOMOTION_API_KEY"

Response 200 (real response from our staging server):

{
  "deleted": true,
  "id": "wh_120f20b244eb403b",
  "object": "webhook"
}
POST/v1/webhooks/{id}/test

Send one signed webhook.test event now

Scope: videos:read or videos:write · Credits: free

FieldInTypeDescription
idpathstring · required
curl --fail-with-body -sS -X POST "https://gomotionai.com/v1/webhooks/$WEBHOOK_ID/test" \
  -H "Authorization: Bearer $GOMOTION_API_KEY"

Response 200 (real response from our staging server):

{
  "delivered": true,
  "error": null,
  "event_id": "evt_5f3a91c2e4b74206a566",
  "object": "webhook_test",
  "status_code": 200
}
POST/v1/webhooks/{id}/rotate-secret

New signing secret (the old one stops at once)

Scope: videos:read or videos:write · Credits: free

FieldInTypeDescription
idpathstring · required
curl --fail-with-body -sS -X POST "https://gomotionai.com/v1/webhooks/$WEBHOOK_ID/rotate-secret" \
  -H "Authorization: Bearer $GOMOTION_API_KEY"

Response 200 (real response from our staging server):

{
  "created_at": 1793535285,
  "description": "drift 2",
  "disabled_reason": null,
  "events": [
    "video.succeeded",
    "video.failed",
    "video.cancelled",
    "export.succeeded",
    "export.failed",
    "export.cancelled",
    "script.succeeded",
    "script.failed",
    "thumbnail.succeeded",
    "thumbnail.failed"
  ],
  "failed_in_a_row": 0,
  "id": "wh_a0f047599f69410c",
  "last_failure_at": null,
  "last_success_at": null,
  "object": "webhook",
  "secret": "whsec_...",
  "status": "enabled",
  "url": "https://hooks.yourshop.com/drift/hook"
}
GET/v1/webhooks/{id}/deliveries

Recent deliveries (newest first, kept 7 days)

Scope: videos:read or videos:write · Credits: free

FieldInTypeDescription
idpathstring · required
limitqueryinteger
curl --fail-with-body -sS "https://gomotionai.com/v1/webhooks/$WEBHOOK_ID/deliveries?limit=10" \
  -H "Authorization: Bearer $GOMOTION_API_KEY"

Response 200 (real response from our staging server):

{
  "data": [
    {
      "attempts": 1,
      "created_at": 1793535285,
      "delivered_at": 1793535285,
      "id": "evt_5f3a91c2e4b74206a566",
      "last_error": null,
      "last_status_code": 200,
      "next_attempt_at": null,
      "object": "webhook_delivery",
      "object_id": null,
      "status": "delivered",
      "type": "webhook.test"
    }
  ],
  "object": "list"
}
POST/v1/webhooks/{id}/deliveries/{evt}/retry

Send an event again (same event id)

Scope: videos:read or videos:write · Credits: free

FieldInTypeDescription
idpathstring · required
evtpathstring · required
curl --fail-with-body -sS -X POST "https://gomotionai.com/v1/webhooks/$WEBHOOK_ID/deliveries/$EVENT_ID/retry" \
  -H "Authorization: Bearer $GOMOTION_API_KEY"

Response 200 (real response from our staging server):

{
  "id": "evt_5f3a91c2e4b74206a566",
  "object": "webhook_delivery",
  "status": "pending"
}

Account

GET/v1/account

Your plan, API limits and this key's usage today

Scope: any key · Credits: free

curl --fail-with-body -sS "https://gomotionai.com/v1/account" \
  -H "Authorization: Bearer $GOMOTION_API_KEY"

Response 200 (real response from our staging server):

{
  "custom_limits": false,
  "key": {
    "credits_spent_today": 0,
    "id": "k_ae85e6baae2b",
    "max_credits_day": 0,
    "scopes": [
      "videos:read",
      "videos:write",
      "scripts:write"
    ],
    "videos_in_progress": 0,
    "videos_today": 0
  },
  "limits": {
    "active_keys_per_account": 10,
    "paid_requests_per_minute_per_account": 30,
    "requests_per_minute_per_account": 300,
    "requests_per_minute_per_key": 120,
    "scripts_and_thumbnails_in_progress_per_account": 3,
    "videos_in_progress_per_account": 3,
    "videos_in_progress_per_key": 5,
    "videos_per_day_per_account": 300,
    "videos_per_day_per_key": 200
  },
  "object": "account",
  "plan": "agency"
}
GET/v1/credits

Credit balance

Scope: credits:read · Credits: free

curl --fail-with-body -sS "https://gomotionai.com/v1/credits" \
  -H "Authorization: Bearer $GOMOTION_API_KEY"

Response 200 (real response from our staging server):

{
  "balance": 2000,
  "held": 0,
  "available": 2000,
  "expires_at": 0,
  "topup_credits": 0,
  "renews_at": 0,
  "key": {
    "id": "k_0d5e1907611a",
    "max_credits_day": 0,
    "spent_today": 0
  }
}
GET/v1/credits/history

Credit movements (newest first)

Scope: credits:read · Credits: free

FieldInTypeDescription
limitqueryinteger
curl --fail-with-body -sS "https://gomotionai.com/v1/credits/history?limit=10" \
  -H "Authorization: Bearer $GOMOTION_API_KEY"

Response 200 (real response from our staging server):

{
  "object": "list",
  "data": [
    {
      "at": 1793457476,
      "type": "credit",
      "credits": 5000,
      "balance_after": 5000,
      "job_id": null
    }
  ]
}
GET/v1/quote

Price of a video in credits

Scope: any key · Credits: free

FieldInTypeDescription
charsqueryintegerScript length in characters (or pass duration_sec).
duration_secqueryintegerLength in seconds (for your own audio).
langquerystring
voicequerystring
curl --fail-with-body -sS "https://gomotionai.com/v1/quote?chars=1200&lang=en" \
  -H "Authorization: Bearer $GOMOTION_API_KEY"

Response 200 (real response from our production server):

{
  "duration_sec": 67,
  "lang": "en",
  "voice": null,
  "quality": "standard",
  "cost_credits": 75
}

OpenAPI

Download openapi.json

OpenAPI 3.1, API version 1.2.0. Import it in Postman, Insomnia or your code generator.