MiniMax Image to Video (i2v)
One reference image and a text prompt in — a short, cinematic video clip out. Test it live with your own API key, or read how it works.
API key
Enter your API key to submit or inspect tasks.
Create a task
Fields map directly onto POST /aigc/task with task_type: "minimax_i2v".
Or inspect an existing task
Request log
Every call this page makes to the API, in order, most recent first.
Nothing sent yet.
Overview
MiniMax Image to Video (minimax_i2v) turns one reference image into a short video clip driven by a text prompt — camera moves, action, style. Unlike the Music Video pipeline, this is a single call: you get a task_id back immediately and the clip is ready within roughly a minute.
Basic configuration
| Setting | Value |
|---|---|
| Base URL | https://api.foxaihub.com/api/v2 |
| Auth header | api-key: YOUR_API_KEY |
| task_type | minimax_i2v |
Warning: The header is api-key, not api_key. Any account with a balance greater than $0 can submit — there is no separate minimum-balance gate like the Music Video pipeline has.
Endpoints at a glance
| Endpoint | Purpose |
|---|---|
| POST /aigc/task | Submit an image-to-video task |
| POST /aigc/task/wait | Submit and block until the task finishes (or times out) |
| GET /aigc/task?ids= | Read the status and result of one or more tasks |
Parameters & constraints
All parameters go in the params object of the create call. Every one of them is validated before you are charged — an invalid value returns 400 with no deduction.
| Field | Type | Required | Constraint |
|---|---|---|---|
| image | string (URL) | yes | Must be a publicly downloadable http(s) URL — base64 is not accepted here, unlike the Music Video API |
| last_frame_image | string (URL) | no | Optional end frame: a publicly downloadable http(s) URL, same base64 restriction as image. Set it to generate a clip that transitions from the first to the last frame; omit it for the original first-frame-only behavior |
| prompt | string | yes | Describes the camera move, action or style you want |
| resolution | string | yes | "768p" or "1080p" |
| aspect_ratio | string | yes | One of "1:1", "4:3", "3:4", "16:9", "9:16" |
| duration | integer (seconds) | yes | 3–15 |
Note: The actual pixel size is looked up server-side from resolution + aspect_ratio, not passed directly — the same combination always maps to the exact same size. At 768p: 1:1 is 768x768, 4:3 is 1024x768, 3:4 is 768x1024, 16:9 is 1344x768, and 9:16 is 768x1344. At 1080p the same ratios scale up to 1:1 = 1088x1088, 4:3 = 1440x1088, 3:4 = 1088x1440, 16:9 = 1920x1088, and 9:16 = 1088x1920. Every size is already a multiple of 32, so there's nothing for you to align.
End frame: landing the clip on a specific shot
Without last_frame_image the behavior is unchanged: image is used as the first frame and the camera move / ending are left entirely to prompt. With it set, the model tries to transition from image to the frame in last_frame_image — useful when you know exactly where the shot should land.
- last_frame_image only applies to minimax_i2v; lip-sync (minimax_ia2v) and LTX (ltx_i2v) don't support it yet and will silently ignore it if sent.
- The end frame is resized/cropped to the final output size the same way the first frame is — two images with very different compositions can produce an unnatural transition in the middle.
- No extra charge: pricing still depends only on resolution and duration, whether or not you pass an end frame.
Why duration must be a whole number of seconds
Generation runs at 24 frames per second, and the frame count is computed as duration x 24 + 1. A fractional duration would misalign the model's time dimension, so only integers from 3 to 15 are accepted.
API reference
POST /aigc/task
Submit an image-to-video task. Charged immediately at submit time.
Parameters
| Field | Type | Required | Notes |
|---|---|---|---|
task_type |
string | Yes | Must be "minimax_i2v" |
params.image |
string (URL) | Yes | Publicly downloadable http(s) URL |
params.last_frame_image |
string (URL) | No | Optional end frame, publicly downloadable http(s) URL |
params.prompt |
string | Yes | Camera move / action / style |
params.resolution |
string | Yes | "768p" or "1080p" |
params.aspect_ratio |
string | Yes | "1:1" / "4:3" / "3:4" / "16:9" / "9:16" |
params.duration |
integer | Yes | 3-15 seconds |
Request Body
{
"task_type": "minimax_i2v",
"params": {
"image": "https://example.com/ref.jpg",
"prompt": "a cinematic dolly shot pushing in on the subject",
"resolution": "768p",
"aspect_ratio": "16:9",
"duration": 5
}
}
Response
{ "success": true, "task_id": "a42e2c77-ded8-48aa-8aa4-dfd9daa43801" }
Errors
| Status | When | Body |
|---|---|---|
400 |
A param fails validation (bad resolution/aspect_ratio, duration out of range, non-URL image) | {"success":false,"error":"Invalid params for minimax_i2v: resolution: Invalid enum value"} |
400 |
task_type is unknown or disabled | {"success":false,"error":"Unsupported task type: minimax_i2v"} |
401 |
Missing/unknown api-key, or balance is 0 | {"message":"Unauthorized"} |
402 |
The deduction itself failed (insufficient balance) | {"success":false,"error":"Insufficient balance"} |
500 |
Pricing configuration for this task_type is invalid | {"success":false,"error":"Invalid price configuration for task type: minimax_i2v"} |
POST /aigc/task/wait
Same as POST /aigc/task, but blocks the HTTP response until the task reaches a terminal state or wait_timeout elapses. Convenient for scripts that don't want to poll.
Parameters
| Field | Type | Required | Notes |
|---|---|---|---|
task_type |
string | Yes | "minimax_i2v" |
params |
object | Yes | Same shape as above |
wait_timeout |
integer (seconds) | No | 1-600, default 60 |
Request Body
{
"task_type": "minimax_i2v",
"params": {
"image": "https://example.com/ref.jpg",
"prompt": "a cinematic dolly shot pushing in on the subject",
"resolution": "768p",
"aspect_ratio": "16:9",
"duration": 5
},
"wait_timeout": 120
}
Response
The full task object (same shape as GET /aigc/task) once it reaches completed or failed.
Errors
| Status | When | Body |
|---|---|---|
408 |
The task is still running when wait_timeout elapses | {"success":false,"task_id":"...","status":"processing","message":"Task wait timeout after 60s"} |
400 / 401 / 402 / 500 |
Same as POST /aigc/task | See above |
GET /aigc/task?ids=
Read one or more tasks by id. Free. Response is a bare array, not an object.
Parameters
| Field | Type | Required | Notes |
|---|---|---|---|
ids |
string | Yes | Comma-separated task ids, up to 25 at once |
Response
[ { "task_id": "...", "task_type": "minimax_i2v", "status": "completed", "params": {...}, "data": ["https://cdn.../result.mp4"], "created_at": "...", "queue_time_sec": 3, "generation_time_sec": 41 } ]
Errors
| Status | When | Body |
|---|---|---|
400 |
ids is missing, or more than 25 ids were requested | {"error":"Task IDs required"} |
401 |
Missing or unknown api-key | {"message":"Unauthorized"} |
Webhook
There are two ways to track video task progress: poll GET /aigc/task?ids= (or use POST /aigc/task/wait for short scripts), or configure a webhook. We recommend webhooks in production — your server is pushed whenever a task completes or fails instead of holding connections open or polling constantly.
Note: A webhook is configured in your Dashboard (one URL per account), not per task — configure it once and all AIGC tasks (including minimax_i2v and minimax_ia2v) will be pushed to your endpoint upon completion or failure.
Delivery format
Every delivery is a POST with a standard envelope:
{
"event": "task.completed",
"type": "minimax_i2v",
"delivery_id": "9f2c1a3e-abcd-4d11-8f00-0123456789ab",
"created_at": "2026-08-26T09:31:02.000Z",
"data": {
"task_id": "a42e2c77-ded8-48aa-8aa4-dfd9daa43801",
"task_type": "minimax_i2v",
"status": "completed",
"params": {
"image": "https://example.com/ref.jpg",
"prompt": "a cinematic dolly shot",
"resolution": "768p",
"aspect_ratio": "16:9",
"duration": 5
},
"data": ["https://cdn.../result.mp4"],
"created_at": "2026-08-26T09:30:10.000Z",
"queue_time_sec": 3,
"generation_time_sec": 41
}
}
| Header | Value |
|---|---|
| Content-Type | application/json |
| User-Agent | FoxAI-Webhook/1 |
| X-Webhook-Event | Same as event in the body (task.completed or task.failed) |
| X-Webhook-Type | Same as type in the body |
| X-Webhook-Delivery | Same as delivery_id in the body |
| X-Webhook-Timestamp | Unix seconds at delivery time |
Result type
Since one URL receives every product on your account, event tells you what happened and type tells you which kind of result it is. type always matches the task_type you passed when creating the task, so you can dispatch on it directly:
| type | Result |
|---|---|
| minimax_i2v | Image-to-video task reached a terminal state |
| minimax_ia2v | Lip-sync (image + audio to video) task reached a terminal state |
| asr-serverless | Music transcription task reached a terminal state |
| mv | Music video pipeline event |
| ping | Test delivery triggered from the Dashboard |
Events
| Webhook event | Trigger condition | data contents |
|---|---|---|
| task.completed | Task successfully finished | Full task object with video URL in data array |
| task.failed | Task failed (charge refunded automatically) | Task object with error details |
Retry policy
- 10 second timeout per attempt.
- Retries at 5s, 10s, 20s — up to 4 attempts total, wrapping up within ~35 seconds.
- Only retried on network errors, timeouts, HTTP 408, 429 and 5xx. Non-retryable 4xx codes are not retried.
- The same delivery keeps the same X-Webhook-Delivery (delivery_id) across retries — use it to deduplicate.
Security
Warning: Add a long random path segment or token query parameter to your webhook URL. Always treat an incoming webhook as a notification signal and confirm with GET /aigc/task?ids= when processing critical business logic.
Limits & lifecycle
Task lifecycle
A task only ever has four states, and it only moves forward:
pending -> processing -> completed
-> failed
| Status | Meaning |
|---|---|
| pending | Accepted, waiting for a GPU worker to pick it up |
| processing | A worker is running the generation |
| completed | data holds the result — an array with the video URL as its first element |
| failed | Something went wrong; the charge was refunded automatically |
There is no fixed SLA, but under normal load a task typically finishes in well under a minute. Heavier queues, or a cold GPU scaling up from zero, can extend that — poll GET /aigc/task?ids= or use POST /aigc/task/wait rather than assuming a fixed time.
Queue time and generation time
The task object carries two timing fields, both in seconds. queue_time_sec is the time from creation (created_at) until the task was first picked up by a worker (entered processing) — how long it waited in the queue. generation_time_sec is the time from that first pickup until it reached completed or failed — how long the actual generation took. Both are null before the corresponding milestone happens: for example, both are null while pending, and generation_time_sec stays null while processing, only getting a value once the task is terminal.
Note: minimax_i2v actually runs as a two-stage pipeline internally (a draft stage produces a latent, then a refine stage turns it into the final clip, passing through an internal streaming state that is never exposed to you). generation_time_sec spans the whole thing — first pickup to final completed/failed — including that internal handoff, so it isn't split into two numbers or missing a stage just because the pipeline runs in two parts.
Retries and timeouts
A worker that claims a task but stops responding is reclaimed and retried, up to 3 attempts total. If every attempt is exhausted the task is marked failed and the charge is refunded — you don't need to build your own timeout-and-refund logic.
Task visibility
Warning: GET /aigc/task does not check ownership — anyone who has a task_id can read its status and result with any valid api-key. Treat a task_id as semi-sensitive if the prompt or result is private, and don't expose it in a public URL.
Webhooks instead of polling
See the Webhook section above for the full configuration endpoints, delivery format and retry policy.
Client database schema
If you are storing task states in your own backend (e.g. MySQL / PostgreSQL), here is the recommended table schema:
Tasks table
CREATE TABLE aigc_tasks (
task_id CHAR(36) NOT NULL PRIMARY KEY,
task_type VARCHAR(32) NOT NULL,
status VARCHAR(20) NOT NULL,
params JSON NOT NULL,
data JSON NULL,
created_at DATETIME(3) NULL,
synced_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
CONSTRAINT chk_aigc_tasks_status CHECK (status IN ('pending', 'processing', 'completed', 'failed'))
) ENGINE=InnoDB;
CREATE INDEX idx_aigc_tasks_status ON aigc_tasks (status);
CREATE INDEX idx_aigc_tasks_task_type ON aigc_tasks (task_type);
Webhook deduplication table
Pair this table with INSERT IGNORE or ON DUPLICATE KEY UPDATE so retried webhook deliveries are never processed twice:
CREATE TABLE webhook_deliveries (
delivery_id CHAR(36) NOT NULL PRIMARY KEY,
task_id CHAR(36) NULL,
event VARCHAR(64) NOT NULL,
payload JSON NOT NULL,
received_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3)
) ENGINE=InnoDB;
CREATE INDEX idx_webhook_deliveries_task_id ON webhook_deliveries (task_id);
Sync strategy
- Insert a row into aigc_tasks as soon as POST /aigc/task returns task_id.
- When a webhook arrives or polling returns updated data, upsert aigc_tasks using task_id as the primary key.
- Extract the resulting video URL from data[0] once status becomes completed.
Billing
minimax_i2v is billed by resolution tier and per second, deducted immediately when you submit. There is no separate reservation/settlement step like the Music Video pipeline. If the task fails or times out, the full amount is refunded automatically.
Formula
price = unit_price_by_resolution[resolution] x duration
| resolution | Per-second price | Meaning |
|---|---|---|
| 768p | $0.025 | Per-second price at 768p — also the practical minimum charge (duration's minimum is 3s, i.e. $0.075) |
| 1080p | $0.05 | Per-second price at 1080p, independent of aspect_ratio |
Worked examples
| resolution | aspect_ratio | Duration | Calculation | Price |
|---|---|---|---|---|
| 768p | 16:9 (landscape) | 5s | $0.025 x 5 | $0.125 |
| 768p | 9:16 (portrait) | 3s (shortest) | $0.025 x 3 | $0.075 |
| 1080p | 16:9 (landscape) | 15s (longest) | $0.05 x 15 | $0.75 |
What happens on failure
- The charge happens before the worker starts, at submit time.
- If the task ends in failed, the full amount is refunded automatically — no action needed on your side.
- A worker that stops responding is retried up to 3 times; if every attempt is exhausted the task is marked failed and refunded the same way.