> ## Documentation Index
> Fetch the complete documentation index at: https://novita.ai/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# MiniMax H3 Video Generation

> Create and query MiniMax H3 asynchronous video generation tasks

MiniMax H3 is an asynchronous video generation API. Create a task, then query its status with the returned task ID. When a task succeeds, `task.content.url` contains the generated video URL.

<Tip>
  The parameters and usage align with the official MiniMax API. Refer to the official documentation for detailed parameter descriptions.
</Tip>

## Endpoints

* Create task: `POST https://api.novita.ai/v3/minimax/v2/video_generation`
* Query task: `GET https://api.novita.ai/v3/minimax/v2/query/video_generation/{task_id}`

## Request headers

<ParamField header="Authorization" type="string" required={true}>
  Bearer API key: `Bearer &lt;YOUR_API_KEY&gt;`.
</ParamField>

<ParamField header="Content-Type" type="string" required={true}>
  Required for task creation. Set to `application/json`.
</ParamField>

## Create a video task

### Request body

<ParamField body="model" type="string" required={true}>
  Fixed value: `MiniMax-H3`.
</ParamField>

<ParamField body="content" type="array" required={true}>
  Multimodal input items. Every request must include a non-empty `text` item.
</ParamField>

<ParamField body="content[].type" type="string" required={true}>
  Item type: `text`, `image_url`, `video_url`, or `audio_url`.
</ParamField>

<ParamField body="content[].text" type="string">
  Required for `text` items. A non-empty prompt; each text item supports up to 7,000 characters.
</ParamField>

<ParamField body="content[].image_url.url" type="string">
  Required for image items. A public URL, `mm_file://{file_id}`, or an image Base64 data URI.
</ParamField>

<ParamField body="content[].video_url.url" type="string">
  Required for video items. A public URL, `mm_file://{file_id}`, or an MP4 Base64 data URI.
</ParamField>

<ParamField body="content[].audio_url.url" type="string">
  Required for audio items. A public URL, `mm_file://{file_id}`, or an audio Base64 data URI.
</ParamField>

<ParamField body="content[].role" type="string">
  Required according to the scenario. Values: `first_frame`, `last_frame`, `reference_image`, `reference_video`, or `reference_audio`. A single image without a role is treated as the first frame.
</ParamField>

<ParamField body="resolution" type="string" required={true}>
  Output resolution: `768P` or `2K`.
</ParamField>

<ParamField body="duration" type="integer" required={true}>
  Output duration in seconds. An integer from `4` to `15`.
</ParamField>

<ParamField body="ratio" type="string">
  Aspect ratio: `adaptive`, `21:9`, `16:9`, `4:3`, `1:1`, `3:4`, or `9:16`. Required for text-to-video and cannot be `adaptive`; image-to-video always uses `adaptive` and ignores other valid values.
</ParamField>

<ParamField body="callback_url" type="string">
  Optional task-status callback URL. On initial configuration, return the verification request's `challenge` unchanged within 3 seconds.
</ParamField>

### Content combinations

| Input                | Constraint                                                                                                                                                    |
| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Text                 | Required; each text item is limited to 7,000 characters.                                                                                                      |
| First and last frame | At most one of each. First-frame only, last-frame only, and first-and-last-frame modes are supported.                                                         |
| Reference images     | Up to 9, using `role=reference_image`.                                                                                                                        |
| Reference videos     | Up to 3, using `role=reference_video`; each is 2-15 seconds and total duration is at most 15 seconds.                                                         |
| Reference audio      | Up to 3, using `role=reference_audio`; each is 2-15 seconds and total duration is at most 15 seconds. It can be the only reference-media type used with text. |
| Media modes          | First/last-frame mode cannot be combined with reference-image, reference-video, or reference-audio mode.                                                      |

### Input media limits

The complete request body must not exceed 64 MB. Base64 increases payload size by about 33%; use public URLs or `mm_file://{file_id}` for large media.

| Media           | Formats                                                   | Per-file limit | Other limits                                                                                                               |
| --------------- | --------------------------------------------------------- | -------------- | -------------------------------------------------------------------------------------------------------------------------- |
| Image           | JPG, JPEG, PNG, WEBP, HEIC, HEIF                          | 30 MB          | Width and height: 256-5760 px; aspect ratio: 0.4-2.5. One first frame, one last frame, or up to 9 reference images.        |
| Reference video | MP4, MOV; H.264/AVC or H.265/HEVC video, AAC or MP3 audio | 50 MB          | Up to 3; each 2-15 seconds, total at most 15 seconds; width and height: 256-5760 px; aspect ratio: 0.4-2.5; 23.976-60 fps. |
| Reference audio | WAV, MP3                                                  | 15 MB          | Up to 3; each 2-15 seconds, total at most 15 seconds.                                                                      |

### Examples

#### Text to video

```bash theme={"system"}
curl -X POST 'https://api.novita.ai/v3/minimax/v2/video_generation' \
  -H 'Authorization: Bearer <YOUR_API_KEY>' \
  -H 'Content-Type: application/json' \
  -d '{
    "model": "MiniMax-H3",
    "content": [{"type": "text", "text": "A cinematic product video in a clean studio"}],
    "resolution": "768P",
    "duration": 4,
    "ratio": "16:9"
  }'
```

#### First-frame to video

```json theme={"system"}
{
  "model": "MiniMax-H3",
  "content": [
    {"type": "text", "text": "Animate the subject naturally and keep the composition stable"},
    {"type": "image_url", "image_url": {"url": "https://your-cdn.example.com/first-frame.png"}, "role": "first_frame"}
  ],
  "resolution": "768P",
  "duration": 4,
  "ratio": "adaptive"
}
```

#### Reference media to video

```json theme={"system"}
{
  "model": "MiniMax-H3",
  "content": [
    {"type": "text", "text": "Create a product demonstration while preserving the reference style"},
    {"type": "image_url", "image_url": {"url": "https://your-cdn.example.com/product.png"}, "role": "reference_image"},
    {"type": "video_url", "video_url": {"url": "https://your-cdn.example.com/reference.mp4"}, "role": "reference_video"},
    {"type": "audio_url", "audio_url": {"url": "https://your-cdn.example.com/reference.mp3"}, "role": "reference_audio"}
  ],
  "resolution": "2K",
  "duration": 6,
  "ratio": "16:9"
}
```

### Create response

```json theme={"system"}
{"task_id": "427916141998479"}
```

Save this ID; it is the unique identifier for status queries and results.

## Query a task

```bash theme={"system"}
curl -X GET 'https://api.novita.ai/v3/minimax/v2/query/video_generation/427916141998479' \
  -H 'Authorization: Bearer <YOUR_API_KEY>'
```

Only tasks created in the last 7 days can be queried (UTC window `[T-7d, T)`). Querying an older ID returns `invalid task_id`. Poll at a 10-30 second interval; do not query at high frequency.

### Task statuses

| Status      | Meaning                                                            |
| ----------- | ------------------------------------------------------------------ |
| `queued`    | Created and waiting for processing.                                |
| `running`   | Generation is in progress.                                         |
| `succeeded` | Generation succeeded; read `task.content.url`.                     |
| `failed`    | Generation failed; see `task.error.code` and `task.error.message`. |
| `cancelled` | Task was cancelled.                                                |

### Successful response

```json theme={"system"}
{
  "task": {
    "id": "427916141998479",
    "model": "MiniMax-H3",
    "status": "succeeded",
    "created_at": 1785125529,
    "updated_at": 1785125946,
    "content": {"url": "https://your-result-cdn.example.com/video.mp4"},
    "resolution": "768P",
    "duration": 4,
    "usage": {"total_seconds": 4, "input_seconds": 0, "output_seconds": 4, "input_image_count": 0},
    "ratio": "16:9",
    "task_type": "generation",
    "modality": "video"
  }
}
```

`content.url` is a time-limited download URL. Download or save the video promptly; query the task again for a new URL after it expires.

### Failed response

```json theme={"system"}
{
  "task": {
    "id": "427916141998479",
    "model": "MiniMax-H3",
    "status": "failed",
    "error": {"code": "1026", "message": "video description contains sensitive content"},
    "created_at": 1785125529,
    "updated_at": 1785125700,
    "resolution": "768P",
    "duration": 4,
    "usage": {},
    "ratio": "16:9",
    "task_type": "generation",
    "modality": "video"
  }
}
```

For `failed` or `cancelled` tasks, do not continue waiting on the same task ID. Correct the request and create a new task.

### Query response fields

| Field                 | Type    | Description                                                                             |
| --------------------- | ------- | --------------------------------------------------------------------------------------- |
| `task.id`             | string  | Task ID.                                                                                |
| `task.model`          | string  | Model used, for example `MiniMax-H3`.                                                   |
| `task.status`         | string  | `queued`, `running`, `succeeded`, `failed`, or `cancelled`.                             |
| `task.error.code`     | string  | Business error code for an asynchronous failure. Returned only on failure.              |
| `task.error.message`  | string  | Error message for an asynchronous failure. Returned only on failure.                    |
| `task.created_at`     | integer | Unix timestamp in seconds when the task was created.                                    |
| `task.updated_at`     | integer | Unix timestamp in seconds of the last status update.                                    |
| `task.content.url`    | string  | Time-limited output-video URL, returned after video generation succeeds.                |
| `task.content.prompt` | string  | Structured enhanced prompt from H3-Context-IR, returned only for its successful tasks.  |
| `task.resolution`     | string  | Output resolution.                                                                      |
| `task.duration`       | integer | Output duration in seconds.                                                             |
| `task.usage`          | object  | Usage information; metering fields are returned on success and may be empty on failure. |
| `task.ratio`          | string  | Actual output aspect ratio; may be empty when not applicable.                           |
| `task.task_type`      | string  | `generation`, `h3_context_ir`, or `regeneration`.                                       |
| `task.modality`       | string  | Output modality: `video` for video tasks and `text` for H3-Context-IR.                  |

Usage fields include `total_seconds` (input plus output seconds), `input_seconds` (reference-video seconds), `output_seconds` (output-video seconds), `input_image_count` (all first, last, and reference images), optional `input_audio_seconds`, and token statistics: `total_tokens`, `prompt_tokens`, and `completion_tokens`.

## Billing

MiniMax H3 bills successful generation by actual use; no charge is reserved when submitting a task. Pricing tiers differ for 768P and 2K. Refer to your platform pricing page for unit prices.

| Usage field                                          | Billable          | Rule                                          |
| ---------------------------------------------------- | ----------------- | --------------------------------------------- |
| `output_seconds`                                     | Yes               | Resolution unit price x `output_seconds`.     |
| `input_seconds`                                      | Yes when non-zero | Resolution unit price x `input_seconds`.      |
| `input_image_count`                                  | Above five images | (`input_image_count` - 5) x image unit price. |
| `total_seconds`                                      | No                | Summary check only.                           |
| `total_tokens`, `prompt_tokens`, `completion_tokens` | No                | Reference statistics only.                    |

The charge is the sum of each billable item. The task is settled once it succeeds; failed and cancelled tasks are not charged. Read the resolution from `task.resolution` in the query response.

## Python example

```python theme={"system"}
import os
import time

import requests

api_domain = "api.novita.ai"
api_key = os.environ["PLATFORM_API_KEY"]
headers = {"Authorization": f"Bearer {api_key}", "Content-Type": "application/json"}
payload = {
    "model": "MiniMax-H3",
    "content": [{"type": "text", "text": "A calm cinematic landscape"}],
    "resolution": "768P",
    "duration": 4,
    "ratio": "16:9",
}

created = requests.post(f"https://api.novita.ai/v3/minimax/v2/video_generation", headers=headers, json=payload, timeout=30)
created.raise_for_status()
task_id = created.json()["task_id"]

while True:
    result = requests.get(f"https://api.novita.ai/v3/minimax/v2/query/video_generation/{task_id}", headers=headers, timeout=30)
    result.raise_for_status()
    task = result.json()["task"]
    if task["status"] == "succeeded":
        print(task["content"]["url"])
        break
    if task["status"] in ("failed", "cancelled"):
        raise RuntimeError(task)
    time.sleep(20)
```

## Errors

| HTTP status | Scenario                                                       | Recommended handling                                                                                 |
| ----------- | -------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- |
| `400`       | Invalid JSON, model, ratio, duration, or `content` combination | Correct the request using `error.message`.                                                           |
| `401`       | Missing or invalid API key                                     | Check the `Authorization: Bearer <API_KEY>` header.                                                  |
| `402`       | Insufficient balance or quota when creating a task             | Top up or adjust the quota, then retry.                                                              |
| `422`       | Sensitive input content when creating a task                   | Revise the prompt or media and create a new task.                                                    |
| `429`       | Rate limit exceeded                                            | Reduce creation or polling frequency and use exponential-backoff retries.                            |
| `500`       | Server error                                                   | Retry with exponential backoff; avoid creating duplicate business tasks without idempotency control. |

HTTP errors use this top-level structure, which differs from an asynchronous `task.error` failure:

```json theme={"system"}
{
  "type": "error",
  "error": {
    "type": "bad_request_error",
    "message": "invalid params, content must include a non-empty text item (prompt is required) (2013)",
    "http_code": "400"
  },
  "request_id": "021785229015510a2c883cf675b9804d"
}
```
