Generate

REST API

Call the MachGen REST API directly over HTTPS.

API is hosted at https://api.machgen.ai.

The general task lifecycle is always the same: submit a task, poll its status until it completes, then download the result.

Depending on the exact task, the task input may require specific fields. Please refer to the following example for details.

Provide your API key

Send your key (see Get Started) as a bearer token on every request:

# Key starts with `MGA_`
export MACHGEN_API_KEY=".."

curl -H "Authorization: Bearer $MACHGEN_API_KEY" https://api.machgen.ai/..

Submit a task

POST /api/v0/generate with a JSON TaskInput describing the task. It returns immediately with a task_id - it does not wait for generation. The body fields differ per task type.

Check the following examples to get a quick start.

For detailed API input/output reference, refer to the API Reference page.

Text to video
curl https://api.machgen.ai/api/v0/generate \
  -H "Authorization: Bearer $MACHGEN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "prompt": "A red panda exploring a misty forest at dawn",
    "model": "Wan2.2-A14B",
    "task_type": "T2V",
    "video_config": {"duration_secs": 5, "height": 720, "aspect_ratio": "16:9", "fps": 16, "infer_steps": 30}
  }'
Image to video
curl https://api.machgen.ai/api/v0/generate \
  -H "Authorization: Bearer $MACHGEN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "prompt": "slow dolly-in as the leaves drift in the wind",
    "model": "Vidu-Q3-Turbo",
    "task_type": "I2V",
    "src_image_urls": ["https://example.com/first-frame.png"],
    "video_config": {"duration_secs": 5, "height": 720}
  }'
Reference to video
# Note: the following example shows how multimodality can be supported
# Not all models support this and one also does not need to provide all of them.
# Some models may support input images only, in which case modify the example as needed.

curl https://api.machgen.ai/api/v0/generate \
  -H "Authorization: Bearer $MACHGEN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "prompt": "use @video as input reference, @alice waves to @bob across a busy street",
    "model": "MiniMax-H3",
    "task_type": "R2V",
    "src_image_urls": [
      "https://example.com/alice.png",
      "https://example.com/bob.png"
    ],
    "src_video_urls": [
      "https://example.com/src.mp4"
    ],
    "subject_to_image_ids": {"alice": [0], "bob": [1]},
    "subject_to_video_ids": {"video": [0]},
    "video_config": {"duration_secs": 5, "height": 768, "aspect_ratio": "16:9"}
  }'
Video upscale
curl https://api.machgen.ai/api/v0/generate \
  -H "Authorization: Bearer $MACHGEN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "prompt": "",
    "model": "Topaz-Video-Precision",
    "task_type": "UPSCALE",
    "src_task_ids": ["<completed-video-task-id>"],
    "upscale_config": {"engine": "proteus"},
    "video_config": {"height": 2160}
  }'
Text to image
curl https://api.machgen.ai/api/v0/generate \
  -H "Authorization: Bearer $MACHGEN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "prompt": "An isometric illustration of a cozy reading nook, soft lighting",
    "model": "Nano-Banana-Pro",
    "task_type": "T2I",
    "image_config": {"aspect_ratio": "1:1", "height": 1024, "infer_steps": 30, "guidance_scale": [5.0]}
  }'
Image editing
curl https://api.machgen.ai/api/v0/generate \
  -H "Authorization: Bearer $MACHGEN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "prompt": "make the sky a dramatic sunset",
    "model": "Nano-Banana-Pro",
    "task_type": "I2I",
    "src_image_urls": ["https://example.com/landscape.png"],
    "image_config": {"aspect_ratio": "1:1", "height": 1024}
  }'

Config values are model-specific

duration_secs, height, and aspect_ratio must be one of the values the target model supports - the duration_secs: 5 and height above are representative, not universal.

A value outside a model's grid is rejected at submit with HTTP 400.

See Available Models for each model, and API Reference for the config fields.

The response carries the task_id you poll and download with:

{ "task_id": "t-abc123", ... }

Check status and download

Poll GET /api/v0/tasks/{task_id} until status is COMPLETED or FAILED.

curl -H "Authorization: Bearer $MACHGEN_API_KEY" \
  https://api.machgen.ai/api/v0/tasks/t-abc123
{
  "task_id": "t-abc123",
  "status": "COMPLETED",
  "metadata": {
    "prompt": "...",
    "height": 720,
    "width": 1280,
    "fps": 24,
    "duration_secs": 5,
    ...
  },
  "task_output": {
    "video": "https://api.machgen.ai/api/v0/assets/t-abc123"
  }
}
{
  "task_id": "t-abc123",
  "status": "COMPLETED",
  "metadata": {
    "prompt": "...",
    "height": 720,
    "width": 1280,
    ...
  },
  "task_output": {
    "image": "https://api.machgen.ai/api/v0/assets/t-abc123"
  }
}
{
  "task_id": "t-abc123",
  "status": "FAILED",
  "error_msg": "Your task failed because ..."
}
curl -H "Authorization: Bearer $MACHGEN_API_KEY" \
  https://api.machgen.ai/api/v0/assets/t-abc123 -o output.mp4

A task moves through these statuses; COMPLETED and FAILED are terminal:

StatusTerminalMeaning
PENDINGAccepted and queued, not yet started.
RUNNINGGeneration in progress.
COMPLETEDyesGeneration completed; ready to download.
FAILEDyesGeneration failed; see error_msg.

Errors

A FAILED status (above) reports a task that was accepted but could not be generated. A request that is rejected before acceptance instead returns a non-2xx HTTP status with a JSON body carrying a single detail string:

{ "detail": "Wan2.2-T2V-A14B does not support height=1080; allowed heights: [480, 720]" }
StatusMeaningExample detail
400 Bad RequestInvalid body: unknown model/task_type, missing config, or a shape/duration outside the model's grid.Invalid model
401 UnauthorizedMissing, malformed, or unknown API key.Invalid API key
403 ForbiddenKey is valid but the account may not use this feature or model.The 'generate' feature is not enabled for your account.
404 Not FoundNo task with the given id (e.g. when polling).Task t-abc123 not found
429 Too Many RequestsRate limit exceeded; retry later.Too many submissions; please try again later.

Sync mode

For self-hosted models you can skip the poll-and-download round trip and receive the finished result inline. POST /api/v0/generate/sync takes the same JSON TaskInput body, but the request blocks until the worker streams back the encoded result and returns those bytes directly.

Sync mode is available for:

  • T2I and I2I tasks on self-hosted (non-passthrough) models, returning image/jpeg;
  • selected self-hosted video surfaces that generate fast enough to answer within the wait, returning video/mp4. On a model with a prompt enhancer, POST /api/v0/generate/sync enhances by default exactly as POST /api/v0/generate does - that call runs inside your wait, so send "enhance_prompt": false if you would rather start generating sooner.

Everything else returns 400.

Sync text to image
curl https://api.machgen.ai/api/v0/generate/sync \
  -H "Authorization: Bearer $MACHGEN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "prompt": "An isometric illustration of a cozy reading nook, soft lighting",
    "model": "FLUX.2-dev",
    "task_type": "T2I",
    "image_config": {"width": 1024, "height": 1024}
  }' -o output.jpg

On success the response is 200 OK with the raw encoded bytes as the body - image/jpeg for an image task, video/mp4 for a video one. The X-Machgen-Delivery-Mode header says which (sync-image or sync-video), and X-Machgen-Task-Id carries the task id: the durable task may still be finalizing when you receive the bytes, so use that id to poll status or fetch the canonical asset later, exactly as with an async task.

If the result cannot be delivered inline - generation runs past the timeout, the result is oversize, or an inline error occurs - the server falls back to the async path and returns 202 Accepted with the normal GenerateResponse JSON body plus an X-Machgen-Sync-Fallback-Reason header (timeout, oversize, or inline_error).

A fallback is not a failure: the task continues on the async path and can be polled and downloaded exactly like one submitted to POST /api/v0/generate.

Inline source images (sync only)

To skip the source-upload round trip on latency-sensitive I2I requests, the sync endpoint also accepts inline base64 entries in src_image_urls using the standard data-URL form:

{
  "task_type": "I2I",
  "src_image_urls": ["data:image/png;base64,iVBORw0KGgo..."]
}

The server verifies the bytes at admission, stores a durable copy in your upload space, and the task's metadata echoes a normal @input/... ref - after acceptance an inline source is indistinguishable from an uploaded one. Each inline image is capped at 1 MB of raw bytes (about 1.37 MB encoded - base64 inflates by about a third); larger images must be uploaded instead. The Python SDK ships a size-guarded helper, machgen.client.inline_image_source(path), that encodes a local file into this form.

Inline sources are accepted only on /v0/generate/sync; POST /api/v0/generate rejects them with a 400.

Source images

For I2V, R2V, and I2I, every entry in src_image_urls must be a public http(s):// URL. (On POST /api/v0/generate/sync only, an entry may instead be an inline data:image/...;base64, URL - see Inline source images.)

For I2V the list is positional: entry 0 is the start frame, and an optional entry 1 is the end frame. To animate from a single image, pass one URL; to pin both ends of a clip, pass two.

{
  "task_type": "I2V",
  "src_image_urls": ["https://example.com/first-frame.png", "https://example.com/last-frame.png"]
}

An end frame is supported by Alibaba-Wan-3.0, the Seedance-2.0 family, Kling-v3, LTX-2.3-Pro, Vidu-Q3-Turbo and Vidu-Q3-Pro. Every other model accepts one image only and returns HTTP 400 for a second, rather than discarding it. A third image is always a 400.

F2F is folded into I2V

task_type: "F2F" is still accepted and behaves identically - the server maps it to a two-image I2V. Prefer I2V; F2F remains only for older clients.

Reference video and audio

FieldMaxPer-clip limits
src_image_urls9JPG / PNG / WebP, 30 MB
src_video_urls3MP4 / MOV, 2-15s each, 15s combined, 50 MB
src_audio_urls3MP3 / WAV, 15s combined

At least one image or video is required - audio alone returns HTTP 400. Seedance-2.0-Fast and Seedance-2.0-Mini also cap the combined image, video, and audio reference count at 12.

A prior MachGen generation can be referenced by task id in src_task_ids instead of a URL: each id is routed to src_video_urls, src_image_urls, or src_audio_urls by the source task's output type, appended after any URLs already in that list. Subject maps (subject_to_*_ids) index the lists as they stand after that routing.

A public http(s):// entry in src_video_urls (a signed link included) is checked for reachability at submit time with a lightweight request; a link that does not answer with success is rejected with HTTP 400 input not accessible: <url> (<reason>) before the task is admitted.

Name a reference with the matching subject map and address it in the prompt as @name. Whitespace in a submitted name is normalized to _ together with its matching prompt mention; names that collide after normalization return HTTP 400. A name may appear in only one map. Once any subject is named, every unquoted @handle in the prompt must match a subject - the request is rejected with HTTP 400 otherwise. To keep a literal @handle as plain prompt text (a screen name, for example), quote it: "@handle".

curl https://api.machgen.ai/api/v0/generate \
  -H "Authorization: Bearer $MACHGEN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "prompt": "@alice dances to @beat in the style of @routine",
    "model": "Seedance-2.0",
    "task_type": "R2V",
    "src_image_urls": ["https://example.com/alice.png"],
    "src_video_urls": ["https://example.com/routine.mp4"],
    "src_audio_urls": ["https://example.com/beat.mp3"],
    "subject_to_image_ids": {"alice": [0]},
    "subject_to_video_ids": {"routine": [0]},
    "subject_to_audio_ids": {"beat": [0]},
    "video_config": {"duration_secs": 5, "height": 720, "aspect_ratio": "16:9", "fps": 24}
  }'

Video-reference intent is capability-driven. Every current R2V surface that accepts video references supports reference_video_operation="reference"; models may additionally advertise edit or extend with their own provider-control policy. Seedance-2.5 supports all three. For its edit, the server uses src_video_urls[0] as the primary clip and derives adaptive framing plus match-source duration from authoritative source metadata; the integer video_config.duration_secs is ignored for provider submission. For extend, the server derives adaptive framing and keeps duration_secs as the requested length of the continuation. Do not round the source duration or try to make it equal the selected output length. Owned MachGen outputs use the source task's declared duration, while uploads use server-probed media duration.

Wan 3.0 file and webpage inputs

Alibaba-Wan-3.0 R2V can use one public HTTPS file or webpage URL in place of ordinary image, video, and audio references:

{
  "prompt": "Turn the creative brief into a cinematic product reveal",
  "model": "Alibaba-Wan-3.0",
  "task_type": "R2V",
  "src_file_urls": ["https://example.com/creative-brief.pdf"],
  "video_config": {
    "duration_secs": 10,
    "height": 1080,
    "aspect_ratio": "adaptive",
    "audio": true
  }
}

Use src_webpage_urls instead when the input is a webpage. Both lists accept at most one URL. Direct URLs must use HTTPS, resolve to public addresses, and return a successful response at submission time. A request must choose exactly one input mode: ordinary media, one file, or one webpage. Mixing the modes returns HTTP 400.

The Python client also accepts a local path in src_file_urls and uploads it before submission. Browser clients can call POST /api/v0/upload first and use the returned @input/<artifact_path> reference. Direct uploads are limited to 32 MB.

Request and response reference

The full field reference for the request body (TaskInput, VideoConfig, ImageConfig) and the status response (TaskStatusResponse) lives on the API Reference page.