Skip to content

Videos

The Videos API generates a video clip from a text prompt, optionally conditioned on uploaded media — one or two images as first/last frames, or reference images, video clips, and audio clips. Rendering takes minutes rather than seconds, so it is an asynchronous job resource: you create a job, poll it until it reaches a terminal status, then download the output.

POST /v1/videos
GET /v1/videos
GET /v1/videos/models
GET /v1/videos/acceptance
POST /v1/videos/acceptance
GET /v1/videos/{video_id}
GET /v1/videos/{video_id}/content
GET /v1/videos/{video_id}/thumbnail

Output is MP4 with audio at 768p. Every request must specify aspect_ratio.

MiniMax H3 video generation runs on hardware in Japan.

Japanese scene descriptions are translated to English before generation. The translator is instructed to leave dialogue inside <d>…</d> and text depicted in the scene unchanged.

A person must agree to the Video Service Terms in the console playground before they can start a job. After that, an API key used by that same person works. A teammate who has not agreed cannot start a job, even in the same organization.

Creating a job never records agreement. The only way to record it is POST /v1/videos/acceptance from a console login. An API key cannot agree.

GET /v1/videos/acceptance returns the current terms version and whether this person, in this organization, has agreed to it.

A missing agreement returns 403 with code agreement_required. Listing jobs, reading a job, and listing models stay open.

Terminal window
curl https://api.aiand.com/v1/videos \
-H "Authorization: Bearer $AIAND_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "minimaxai/minimax-h3",
"prompt": "A paper boat drifting down a rain-soaked gutter at dusk",
"seconds": 8,
"aspect_ratio": "16:9"
}'
FieldRequiredNotes
modelyesA model id from GET /v1/videos/models
promptyesUp to 7000 characters
secondsyesClip duration, 4–15
aspect_ratioyesOne of Auto, 21:9, 16:9, 4:3, 1:1, 3:4, 9:16
image_referencenoOne or two images (purpose: "vision") with unique first_frame/last_frame roles (image-to-video)
referencesnoReference media to condition on (reference-to-video): images, videos, audio. Cannot be combined with image_reference

Upload each image first and use the returned file_id. Each image must be a PNG, JPEG, or WebP file with purpose: "vision", at most 30 MiB — the engine rejects larger inputs even though the Files API accepts uploads up to 100 MiB.

Terminal window
curl https://api.aiand.com/v1/files \
-H "Authorization: Bearer $AIAND_API_KEY" \
-F "purpose=vision" \
-F "file=@first.jpg"

Then submit one or two file references in customer-defined order:

Terminal window
curl https://api.aiand.com/v1/videos \
-H "Authorization: Bearer $AIAND_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "minimaxai/minimax-h3",
"prompt": "A cinematic scene inspired by the references, natural motion",
"seconds": 5,
"aspect_ratio": "16:9",
"image_reference": [
{ "file_id": "file-first", "role": "first_frame" },
{ "file_id": "file-last", "role": "last_frame" }
]
}'

The API validates that every file belongs to your organization and has purpose: "vision". It converts the stored R2 objects to short-lived signed URLs immediately before engine submission; public URLs are never required. Either role may be used alone, so a single first_frame does not require a last_frame. When both are supplied, their roles must be unique.

Reference images, video, and audio (reference-to-video)

Section titled “Reference images, video, and audio (reference-to-video)”

To condition on reference media instead of frames, upload each file with the matching purpose — vision for images, video for clips, audio for sound — and pass the file_ids inside a references object:

Terminal window
curl https://api.aiand.com/v1/videos \
-H "Authorization: Bearer $AIAND_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "minimaxai/minimax-h3",
"prompt": "A cinematic scene inspired by the references, natural motion",
"seconds": 5,
"aspect_ratio": "16:9",
"references": {
"images": [{ "file_id": "file-img1" }],
"videos": [{ "file_id": "file-clip1" }],
"audio": [{ "file_id": "file-aud1" }]
}
}'

Counts, per-file sizes, and the total size are validated at request time. A reference video or audio clip longer than the video you request is trimmed: only its first part, as long as the requested seconds, is used. For an 8-second video, that is the first 8 seconds of each clip. For best results, use clips of 2–15 seconds.

ReferencePurposeMax countMax sizeRecommended length
imagevision830 MiB—
videovideo250 MiB2–15 s
audioaudio315 MiB2–15 s

These rules apply:

  • Frames and references cannot be mixed. A request uses image_reference or references — never both. They are separate request shapes.
  • Audio needs an anchor. references must include at least one image or video; audio alone is rejected.
  • At most 12 references in total. Within the per-type limits above, references may hold no more than 12 files altogether.
  • At most 256 MiB in total. Within the per-file limits above, all input files of one request may add up to no more than 256 MiB. A file used in two slots counts twice.
  • Input files need an hour left. A frame or reference file that expires within the next hour is rejected. Upload it again.
  • Each slot accepts certain formats. First and last frames and reference images: PNG, JPEG, or WebP. Reference videos: MP4, MOV, or WebM. Reference audio: WAV, MP3, FLAC, OGG, M4A, or audio in an MP4 or WebM file. Other formats, such as GIF or HEIC, are rejected.
  • Input files must not be empty. A frame or reference file of 0 bytes is rejected.
  • Images must be between 1:4 and 4:1. A frame or reference image whose long side is more than four times its short side is rejected.
  • Images must have a standard, readable header. JPEGs must be 8-bit baseline or progressive; multi-picture JPEGs (such as some iPhone photos) and CMYK JPEGs are rejected. Ultra HDR photos are accepted. The image header must lie within the first 64 KiB; very large metadata can push it past that. Convert such files to a standard PNG or JPEG.
  • Images must be still. Animated PNG and WebP files are rejected.

A request that breaks one of these rules is rejected with 400 invalid_value. Where possible, param names the input at fault.

The response is a job object with status: "moderating":

{
"id": "video_9f2c1b7e4a3d4e8fa1b0c9d8e7f6a5b4",
"object": "video",
"model": "minimaxai/minimax-h3",
"prompt": "A paper boat drifting down a rain-soaked gutter at dusk",
"seconds": 8,
"status": "moderating",
"error": null,
"cost": "0.64000000",
"currency": "usd",
"created_at": 1755302400,
"completed_at": null,
"thumbnail": false
}

status is one of moderating, translating, queued, in_progress, completed, failed, or canceled. No job ends as canceled today. Treat any status you do not recognize as still running — new intermediate stages may be added. error is populated only when the job failed.

Every job starts as moderating while its prompt and reference media are checked against our content policy. This normally takes a few seconds — up to about a minute when references are attached — and allowed jobs move to translating while the prompt is translated to English, then to queued, and render as usual. A request that violates the policy fails with error code moderation_blocked and is never billed:

{
"status": "failed",
"error": {
"code": "moderation_blocked",
"message": "Your request was rejected by our content moderation system."
}
}

If the moderation system itself is unavailable, the job fails closed with error code moderation_unavailable — nothing was judged and nothing is billed, so resubmit later. Likewise, if a Japanese prompt cannot be translated, the job fails closed with error code translation_unavailable — nothing is billed, so resubmit later.

Terminal window
curl https://api.aiand.com/v1/videos/video_9f2c1b7e4a3d4e8fa1b0c9d8e7f6a5b4 \
-H "Authorization: Bearer $AIAND_API_KEY"

A typical clip takes several minutes. Poll every 10–30 seconds until status is terminal — polling faster does not make a render finish sooner.

Once status is completed, fetch the bytes. The response is the raw MP4, not JSON:

Terminal window
curl https://api.aiand.com/v1/videos/video_9f2c1b7e4a3d4e8fa1b0c9d8e7f6a5b4/content \
-H "Authorization: Bearer $AIAND_API_KEY" \
-o clip.mp4

Requesting content for a job that is not completed returns 400.

A completed job usually also has a JPEG still, 480 pixels wide, taken from near the start of the clip. thumbnail is true when the job has one to download; otherwise this returns 404, or 400 if the job isn’t completed:

Terminal window
curl https://api.aiand.com/v1/videos/video_9f2c1b7e4a3d4e8fa1b0c9d8e7f6a5b4/thumbnail \
-H "Authorization: Bearer $AIAND_API_KEY" \
-o clip.jpg

The thumbnail is free and is deleted together with the video.

Terminal window
curl "https://api.aiand.com/v1/videos?limit=20" \
-H "Authorization: Bearer $AIAND_API_KEY"

Returns newest first. limit defaults to 20 and caps at 100; pass after=<video_id> to page.

Authentication is optional. With an API key, prices are shown in your organization’s billing currency; without one, prices are shown in USD. Requests without a key are rate-limited to 600 per minute per IP, one budget shared by every catalog endpoint callable without a key, including GET /v1/models.

Terminal window
curl https://api.aiand.com/v1/videos/models

With an API key:

Terminal window
curl https://api.aiand.com/v1/videos/models \
-H "Authorization: Bearer $AIAND_API_KEY"

Each entry carries per-second pricing, one row per output resolution:

{
"object": "list",
"data": [
{
"id": "minimaxai/minimax-h3",
"object": "video_model",
"name": "MiniMaxAI/MiniMax-H3",
"description": null,
"pricing": [
{
"resolution": "768p",
"per_second": "0.080000",
"currency": "usd"
}
]
}
]
}

A render is priced as per_second × seconds and charged only when it completes. A job that fails or times out is never charged. A job cannot be canceled once submitted.

Because a render costs dollars and cannot be aborted part-way, its exact cost is fixed when the job is submitted: per_second × requested seconds. A job is refused unless your balance covers that quote plus the quoted costs of renders already in flight. This is stricter than the chat endpoints, which admit any request while the balance is above zero.

cost and currency contain that quote from the first response. The balance is deducted only after the render completes successfully.

StatusCodeMeaning
400invalid_valueBad body, out-of-range seconds, invalid or oversized references, or content requested before completion
403agreement_requiredThis person has not agreed to the current Video Service Terms in the console playground
402insufficient_creditsBalance does not cover the quoted cost of this render
402monthly_budget_reachedYour organization’s remaining monthly budget does not cover this render plus renders already in flight
404model_not_foundUnknown model, or one your plan is not entitled to
429concurrency_limit_exceededYou already have the maximum number of renders in progress for your plan
429rate_limit_exceededMore than 10 submit attempts in one minute from your organization; retry after Retry-After seconds
409conflictThe job is still running, or is under review and cannot be deleted
503provider_unavailableNo video engine is configured for this model

An admitted job can also end failed, for example with one of these error codes. Failed jobs are never charged.

  • engine_unavailable: the video engine was unavailable. Resubmit later.
  • engine_rejected: the engine could not process the job. Retrying with the same inputs will fail the same way; contact support if it persists.

A finished job — the generated video and the job record, including its prompt — is deleted automatically 30 days after it finishes. Download anything you want to keep before then.

DELETE /v1/videos/{video_id} deletes one immediately:

Terminal window
curl -X DELETE https://api.aiand.com/v1/videos/video_abc123 \
-H "Authorization: Bearer $AIAND_API_KEY"
{ "id": "video_abc123", "object": "video", "deleted": true }

This is permanent and cannot be undone. A job that is still running has to finish first — poll it until its status is terminal, then delete.