▥ Seedance 2.5 is here! ›
Documentation

MLMux API

One API key and one request format for image and video generation models. Images come back in the response; videos run as tasks that you poll or receive by webhook.

Quickstart

  1. 01

    Create an API key

    Create a key in Console → API keys. The full key is shown only once, so store it right away, for example as MLMUX_API_KEY.

  2. 02

    Send a request

    Call an image or video endpoint with a model ID and a prompt. Usage is charged to the account that owns the key.

  3. 03

    Get the result

    Image requests return URLs in the response. Video requests return a task: poll its poll_url or subscribe to a webhook.

Authentication

Send your key in the Authorization: Bearer header on every request to https://api.mlmux.com. Keys start with sk-. The API does not accept keys in other headers or in the query string.

To check that a key works, list the models it can call:

GEThttps://api.mlmux.com/v1/models
curl https://api.mlmux.com/v1/models \
  -H "Authorization: Bearer $MLMUX_API_KEY"
Response
{
  "object": "list",
  "data": [
    { "id": "google/nano-banana-2", "object": "model", "owned_by": "platform" },
    { "id": "bytedance/seedance-2.5", "object": "model", "owned_by": "platform" }
  ]
}

Endpoints

All endpoints are under https://api.mlmux.com. Request and response bodies are JSON unless noted.

MethodPathDescription
GET/v1/modelsList the model IDs you can call.
POST/v1/images/generationsGenerate images from a prompt.
POST/v1/images/editsEdit or combine input images. Accepts JSON or multipart.
GET/v1/images/generations/{task_id}Get an async image task. Edits use /v1/images/edits/{task_id}.
POST/v1/videos/generationsCreate a video task.
GET/v1/videos/generations/{task_id}Get a video task.
GET/v1/tasks/{task_id}Get any task by ID.
POST/v1/tasks/{task_id}/cancelCancel a task that has not finished.
POST/v1/uploadsUpload a reference file and get a URL.
GET/v1/account/balanceGet your credit balance.

Route mode

Set route_mode in the request body to choose the upstream tier. A request stays in its tier and never silently moves to another one; if the tier is turned off for the model, the request fails with route_mode_disabled. When the field is omitted, MLMux uses the route modes set on the API key, then the model default, which is official-cheap.

ValueDescription
officialThe provider's official route, billed at the provider's list price.
official-cheapA lower-cost official route. Default for new requests and API keys.

Billing

Usage is charged in credits, and 1 credit equals 1 US dollar. Video models are billed per output token; image models are billed per upstream token or per image, depending on the model. See the pricing page for rates on each route.

Task responses show estimated_charge_credits when the task is created and actual_charge_credits after it succeeds, both in credits. Webhook payloads show actual_charge_nano_credits as an integer, where 1,000,000,000 nano-credits equal 1 credit.

Check your remaining balance with GET /v1/account/balance.

Tasks and polling

Video requests, and image requests sent with "async": true, return HTTP 202 with a task. Poll the task's poll_url every few seconds until its status is succeeded, failed, expired, or canceled.

StatusDescription
queuedAccepted and waiting to start.
submittedSent to the upstream provider.
runningGenerating. progress runs from 0 to 100.
succeededFinished. The response now includes the output URLs.
failedFailed. message explains why.
expiredDid not finish within the task's time limit.
canceledCanceled through the cancel endpoint.

Cancel a task

POSThttps://api.mlmux.com/v1/tasks/{task_id}/cancel

Tasks that are queued, submitted, or running can be canceled, and their held credits are released. The response is {"id": "task_…", "status": "canceled"}. Tasks in any other status return 409 task_not_cancelable.

Safe retries

Async video and image generation requests accept an Idempotency-Key header of up to 160 characters. Sending the same key with the same body again returns the original task instead of creating a new one. Sending the same key with a different body returns 409 idempotency_key_reused. Synchronous requests and image edits do not accept this header.

Uploads

To use a local file as a reference, upload it as the multipart field file. Images, videos, and audio up to 50 MB are accepted and kept for 3 days. Pass the returned url in any URL field of a generation request.

POSThttps://api.mlmux.com/v1/uploads
Request
curl https://api.mlmux.com/v1/uploads \
  -H "Authorization: Bearer $MLMUX_API_KEY" \
  -F "file=@start-frame.jpg"
Response
{
  "url": "https://.../uploads/3f8a....jpg",
  "asset_id": 1024,
  "mime_type": "image/jpeg",
  "size_bytes": 482113,
  "asset_type": "image"
}

Webhooks

Create an endpoint in Console → Webhooks and choose the events to receive. When a request or task finishes, MLMux sends a POST request with a JSON body to your URL.

EventDescription
request.succeededA request or task succeeded.
request.failedA request or task failed. data.error_code and data.error_message explain why.
webhook.testSent when you test the endpoint in Console.
Payload
{
  "event_id": "evt_5b0d...",
  "event_type": "request.succeeded",
  "created_at": "2026-10-26T08:00:00Z",
  "data": {
    "request_id": "task_task_01a9...",
    "task_id": "task_01a9...",
    "public_model_id": "bytedance/seedance-2.5",
    "api_method": "videos.generations.create",
    "image_count": 0,
    "image_resolution": "",
    "video_seconds": 8,
    "video_resolution": "720p",
    "actual_charge_nano_credits": 1790000000,
    "latency_ms": 94210
  }
}

Verify signatures

Each delivery is signed with the endpoint's signing secret (whsec_…), which is shown once when you create or rotate it. The X-MLMux-Signature header holds a Unix timestamp t and a signature v1: the hex HMAC-SHA256 of the timestamp, a period, and the raw request body. Compute the same value, compare it, and reject old timestamps.

Node.js
import crypto from "node:crypto";

// header: X-MLMux-Signature: t=1767225600,v1=5f8c...
export function verifyMLMuxSignature(rawBody, header, secret) {
  const parts = Object.fromEntries(header.split(",").map((part) => part.split("=")));
  const expected = crypto.createHmac("sha256", secret).update(`${parts.t}.${rawBody}`).digest("hex");
  const fresh = Math.abs(Date.now() / 1000 - Number(parts.t)) < 300;
  return fresh && crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1 ?? ""));
}

Return any 2xx status within 10 seconds. MLMux makes up to 6 delivery attempts over about 11 hours. Use X-MLMux-Event-ID to ignore duplicate deliveries.

Errors

Errors share one JSON shape. Branch on code rather than message. Include request_id, which is also returned in the X-Request-ID header, when you contact support.

Error response
{
  "error": {
    "message": "Invalid API key",
    "type": "invalid_request_error",
    "code": "invalid_api_key",
    "request_id": "req_9f2c..."
  }
}
HTTPCodeMeaning
400invalid_requestThe body is not valid JSON or a field has the wrong type, such as an unknown route_mode on an image generation.
400invalid_valueA value such as resolution, size, aspect_ratio, or n is not supported by the model.
400model_not_foundThe model ID does not exist or is not available.
400invalid_video_requestA video mode, reference count, ratio, resolution, or duration is invalid.
400content_policy_violationThe prompt or an input was blocked by content moderation.
401invalid_api_keyThe API key is missing, invalid, expired, or disabled.
402insufficient_creditsThe balance cannot cover the estimated charge. Add credits and try again.
402spend_limit_exceededThe request would exceed a spend limit set in Console.
402api_key_monthly_limit_exceededThe request would exceed this API key's monthly budget.
403email_verification_requiredVerify the account email address before using the API.
403model_not_allowedThis API key is restricted to other models.
403forbidden_asset_referenceAn asset:// reference does not belong to this account.
404task_not_foundThe task does not exist or belongs to another account.
409idempotency_key_reusedThe Idempotency-Key was already used with a different body.
409task_not_cancelableThe task has already finished.
429rate_limit_exceededToo many requests per minute for this key or account.
429capacity_exhaustedEvery upstream on the selected route is busy.
502model_unavailableThe selected route has no available upstream for this model.
502generation_failedThe upstream provider failed to generate an output.
503generation_unavailableThe upstream provider is temporarily unavailable.
503route_mode_disabledThe selected route mode is turned off for this model. Send another route_mode.
504generation_timeoutThe upstream provider timed out.

429 responses include a Retry-After header. Wait that many seconds before retrying, and retry 5xx errors with exponential backoff.