# MiniMax H3 视频生成

来源: https://platform.lyrouter.com/ai_gateway/docs/video-minimax
更新日期: 2026-10-19

用 MiniMax H3 系列模型生成视频，支持文生视频、首尾帧图生视频，以及用参考图、参考视频、参考音频做多模态参考生视频。除了普通的视频生成，`minimax/minimax-h3` 还提供两个只有它支持的接口：视频升清（`POST /v1/videos/regenerations`，把 768P 视频重新生成为 2K）和上下文增强（`POST /v1/videos/context`，产物是增强后的提示词文本）。三种任务都以异步任务的方式提供，用同一个查询接口跟踪进度。接口形状参照 OpenAI 的 Videos API，网关把统一的参数转换成 MiniMax 的格式。任务生命周期的通用说明见[异步任务](https://platform.lyrouter.com/ai_gateway/docs/async)。

## 支持的模型

| 模型 ID | 特点 | 时长与分辨率 | 计费口径 |
| --- | --- | --- | --- |
| `minimax/minimax-h3` | 标准版：文生视频、首尾帧图生视频、多模态参考（参考图 / 视频 / 音频）；支持视频升清和上下文增强 | 4～15 秒；480P、768P、2K | 视频按秒（输入 + 输出），按分辨率分档；升清单独一档；参考图单独计一项；上下文增强按输入、输出 Token 计 |
| `minimax/minimax-h3-max` | **已被服务商下线，目前不可用，请改用 `minimax/minimax-h3`**。极速版：文生视频、首尾帧图生视频、多模态参考；不支持多张参考图，不支持升清和上下文增强 | 5～15 秒；480P、768P | 视频按秒，按 480P、768P 分档；参考图单独计一项 |

> [!warning]
> `minimax/minimax-h3-max` 已被服务商下线：目前调用它会返回 HTTP 200，但任务对象是空的、没有任务 ID，无法轮询，也不扣费。请改用 `minimax/minimax-h3`。

模型需要在工作空间的模型广场开通后才能调用；具体单价以模型广场显示的为准。

## 端点

| 方法 | 路径 | 用途 |
| --- | --- | --- |
| POST | `/v1/videos` | 提交视频生成任务，立即返回任务 ID |
| GET | `/v1/videos/{id}` | 查询任务状态，三种任务通用 |
| GET | `/v1/videos/{id}/content` | 下载视频（302 跳转到视频地址） |
| POST | `/v1/videos/regenerations` | 视频升清：把 H3 生成的 768P 视频重新生成为 2K，仅 `minimax/minimax-h3` |
| POST | `/v1/videos/context` | 上下文增强：把多模态上下文整理成更丰富的提示词，产物是文本，仅 `minimax/minimax-h3` |
| DELETE | `/v1/videos/{id}` | 删除已结束的任务；不能取消进行中的任务 |

完整地址为 `https://api.lyrouter.com` 加上表中的路径。三个提交接口用的是同一个模型名、同一套任务 ID，返回的任务都用 `GET /v1/videos/{id}` 查询。

## 鉴权

在请求头中携带 API Key：

```text
Authorization: Bearer <API Key>
```

`Bearer` 区分大小写。也可以改用 `x-api-key: <API Key>`，两者同时存在时只读 `Authorization`。API Key 在工作空间的 **API 密钥** 页面创建，只用于 LYRouter 鉴权，不会转发给上游服务商。完整说明见[接入地址与鉴权](https://platform.lyrouter.com/ai_gateway/docs/call-gateway)。

同一工作空间内的任意 API Key 都能查询、下载该工作空间的任务；其他工作空间的 Key 查询时返回 404。

## 请求头

| 请求头 | 必填 | 说明 |
| --- | --- | --- |
| `Content-Type` | 提交时必填 | `application/json`。`POST /v1/videos` 也接受 `multipart/form-data`，见下文「用表单提交」；升清和上下文增强只接受 JSON |
| `X-Gateway-Strict-Parameters` | 否 | 设为 `true` 时，请求中有会被丢弃的参数就直接返回 400，不提交任务、不计费，见[网关扩展请求头与响应头](https://platform.lyrouter.com/ai_gateway/docs/gateway-headers) |

## 提交任务

`POST /v1/videos`

提交一个视频生成任务。网关校验参数、转换成 MiniMax 的格式后提交给服务商，立即返回 `status` 为 `queued` 的任务对象，视频在后台生成。提交请求本身不产生费用。

### 请求体

| 参数 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- |
| `model` | string | 是 | `minimax/minimax-h3` 或 `minimax/minimax-h3-max`（已被服务商下线，目前不可用，请改用 `minimax/minimax-h3`）。必须是具体的模型 ID：虚拟模型 `@vmodel/<名称>` 和 `lyrouter/auto` 返回 403，失败时也不会改用工作空间的默认模型 |
| `prompt` | string | 是 | 提示词 |
| `seconds` | integer | 是 | 视频时长（秒），必须是正整数。H3 为 4～15，H3-Max 为 5～15（H3-Max 已被服务商下线，目前不可用）。MiniMax 要求提供时长，网关不补默认值 |
| `size` | string | 是 | 分辨率：档位名（`480P`、`768P`、`2K`）或 `宽x高`（短边必须正好是 480 或 768），见下文「`size`：短边必须正好落在档位上」。H3-Max 不支持 `2K`（H3-Max 已被服务商下线，目前不可用）。MiniMax 要求提供分辨率 |
| `first_frame` | string | 否 | 首帧图片的 URL（图生视频），1 张 |
| `last_frame` | string | 否 | 尾帧图片的 URL，1 张 |
| `reference_image` | string \| string[] | 否 | 参考图的 URL，单张写字符串，多张写数组，最多 9 张 |
| `input_reference` | string \| string[] | 否 | 参考**视频**的 URL，最多 3 个 |
| `reference_audio` | string \| string[] | 否 | 参考音频的 URL，最多 3 个 |
| `watermark` | boolean | 否 | 是否给视频加水印 |

- **图生视频与多模态参考互斥**：传了 `reference_image`、`input_reference` 或 `reference_audio`，就不能再传 `first_frame` / `last_frame`，反之亦然。这条由服务商校验。
- 以下参数在网关契约里存在（其他模型用得上），但 MiniMax 没有对应概念，传了返回 400 `<参数名> is not supported for this model`：`generate_audio`（有声由参考音频承载）、`output_format`（没有容器格式选项）、`omni_reference_task_type`（没有全模态任务类型）、`source_video`（只属于视频升清接口）。
- 表中没有的其他字段会被丢弃，响应头 `X-Gateway-Unsupported-Parameters` 列出它们的名字；带 `X-Gateway-Strict-Parameters: true` 时改为返回 400。MiniMax 原生的 `duration`、`resolution`、`ratio`、`content` 也属于这一类，请改用 `seconds`、`size` 和上面的素材字段。
- 本接口不支持 `n`，每次请求生成一个视频。

### 用表单提交

`POST /v1/videos` 也接受 `multipart/form-data`，OpenAI 官方 SDK 的 `videos.create()` 就用这种格式。表单中网关读取 `model`、`prompt`、`seconds`、`size`、`input_reference`、`watermark` 等字段：

- **`input_reference` 只能传 URL，传文件不会报错但会被忽略**：请填可公开访问的 `https://` 地址；目前上传文件时，任务按没有参考视频的方式生成，并照常计费。
- `watermark` 只接受字面量 `true` 或 `false`。
- `first_frame`、`last_frame`、`reference_image`、`reference_audio` 请用 JSON 请求体提交。目前表单里的这些字段会被换成占位值转发给服务商，例如带 `reference_audio` 时提交返回 200，任务随后失败，`error.message` 为 `content[1].audio_url: invalid param: audio url must be http(s):// or data:...;base64`（失败不收费）。

### 请求示例

示例从环境变量 `LYROUTER_API_KEY` 读取 API Key，设置方法见[完成第一次模型调用](https://platform.lyrouter.com/ai_gateway/docs/quickstart)。

**请求示例**

cURL:
```bash
curl https://api.lyrouter.com/v1/videos \
  -H "Authorization: Bearer $LYROUTER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "minimax/minimax-h3",
    "prompt": "史诗级太空歌剧预告：女舰长独自站在观景窗前",
    "size": "768P",
    "seconds": 5
  }'
```

Python:
```python
import os
import httpx

resp = httpx.post(
    "https://api.lyrouter.com/v1/videos",
    headers={"Authorization": f"Bearer {os.environ['LYROUTER_API_KEY']}"},
    json={
        "model": "minimax/minimax-h3",
        "prompt": "史诗级太空歌剧预告：女舰长独自站在观景窗前",
        "size": "768P",
        "seconds": 5,
    },
    timeout=60,
)
resp.raise_for_status()
dropped = resp.headers.get("X-Gateway-Unsupported-Parameters")
if dropped:
    print("这些参数没有生效：", dropped)
task = resp.json()
print(task["id"], task["status"])
```

Node.js:
```js
const resp = await fetch("https://api.lyrouter.com/v1/videos", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.LYROUTER_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    model: "minimax/minimax-h3",
    prompt: "史诗级太空歌剧预告：女舰长独自站在观景窗前",
    size: "768P",
    seconds: 5,
  }),
});
if (!resp.ok) throw new Error(await resp.text());
const dropped = resp.headers.get("X-Gateway-Unsupported-Parameters");
if (dropped) console.log("这些参数没有生效：", dropped);
const task = await resp.json();
console.log(task.id, task.status);
```

### 响应

提交成功返回 HTTP 200 和任务对象，`status` 为 `queued`，字段含义见下文「查询任务」。提交响应里没有 `usage`，费用在任务完成时结算。下面的 400 是 `size` 写成了 `1280x720`（短边 720 不在 MiniMax 的档位里），403 是模型没有在本工作空间开通（或用了虚拟模型、`lyrouter/auto`），429 是服务商账户的并发任务已满。

**响应示例**

200:
```json
{
  "id": "aigwt_minimax_videos_Qm3Tz8VbN1sLk0pYc4dReA",
  "object": "video",
  "model": "MiniMax-H3",
  "status": "queued",
  "progress": 0,
  "created_at": 1791950400,
  "expires_at": 1792555200,
  "seconds": 5,
  "size": "768P"
}
```

400:
```json
{
  "error": {
    "message": "this model takes discrete resolution tiers, not arbitrary sizes: 1280x720 (short side 720). Use a size whose short side is 480 or 768, or pass the tier name directly as size (e.g. \"768P\", \"2K\")",
    "type": "invalid_request_error",
    "code": "invalid_parameter"
  }
}
```

403:
```json
{
  "error": {
    "message": "Model is not available in the model plaza",
    "type": "access_denied",
    "code": "model_not_available"
  }
}
```

429:
```json
{
  "error": {
    "message": "upstream concurrency full",
    "type": "rate_limit_error",
    "code": "upstream_concurrency_full"
  }
}
```

- 保存好返回的 `id`。查询时网络出错或超时，用同一个 ID 继续查询，不要重新提交：重新提交会生成新任务并再次计费。
- 429 `upstream_concurrency_full` 的并发限制按服务商账户共享，换一个 API Key 也一样，请稍后重新提交。

## 查询任务

`GET /v1/videos/{id}`

读取任务的当前状态，视频生成、视频升清、上下文增强三种任务都用这个接口。网关在后台替你向服务商轮询进度，这个接口只返回网关记录的状态，查得更频繁不会更早拿到结果。

### 路径参数

| 参数 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- |
| `id` | string | 是 | 提交时返回的任务 ID，以 `aigwt_` 开头。服务商自己的任务 ID 查不到，返回 404 |

### 请求示例

**请求示例**

cURL:
```bash
curl https://api.lyrouter.com/v1/videos/$TASK_ID \
  -H "Authorization: Bearer $LYROUTER_API_KEY"
```

Python:
```python
import os
import httpx

task_id = "aigwt_minimax_videos_Qm3Tz8VbN1sLk0pYc4dReA"
resp = httpx.get(
    f"https://api.lyrouter.com/v1/videos/{task_id}",
    headers={"Authorization": f"Bearer {os.environ['LYROUTER_API_KEY']}"},
    timeout=60,
)
resp.raise_for_status()
task = resp.json()
print(task["status"], task.get("usage"), task.get("error"))
```

Node.js:
```js
const taskId = "aigwt_minimax_videos_Qm3Tz8VbN1sLk0pYc4dReA";
const resp = await fetch(`https://api.lyrouter.com/v1/videos/${taskId}`, {
  headers: { Authorization: `Bearer ${process.env.LYROUTER_API_KEY}` },
});
if (!resp.ok) throw new Error(await resp.text());
const task = await resp.json();
console.log(task.status, task.usage, task.error);
```

### 响应

任务存在时总是返回 HTTP 200，任务本身失败也一样，以 `status` 判断结果。任务 ID 不存在、已超过 7 天或属于其他工作空间时返回 404。

**响应示例**

200 已完成:
```json
{
  "id": "aigwt_minimax_videos_Qm3Tz8VbN1sLk0pYc4dReA",
  "object": "video",
  "model": "MiniMax-H3",
  "status": "completed",
  "progress": 100,
  "created_at": 1791950400,
  "completed_at": 1791950507,
  "expires_at": 1792555200,
  "seconds": 5,
  "size": "768P",
  "usage": { "duration_seconds": 5 }
}
```

200 上下文增强:
```json
{
  "id": "aigwt_minimax_videos-context_Zp8Rk2Vt5Lq9Wx3Ny6Bh1A",
  "object": "video",
  "model": "MiniMax-H3",
  "status": "completed",
  "progress": 100,
  "created_at": 1791950400,
  "completed_at": 1791950421,
  "expires_at": 1792555200,
  "seconds": 5,
  "output_text": "integrated_multimodal_description: [Shot 1] Cinematic, a medium shot featuring a slow push-in on ...",
  "usage": { "completion_tokens": 1820 }
}
```

200 已失败:
```json
{
  "id": "aigwt_minimax_videos_Fj4Ns7Kd2Pw9Lm5Qx8Rt3Y",
  "object": "video",
  "model": "MiniMax-H3",
  "status": "failed",
  "progress": 0,
  "created_at": 1791950400,
  "completed_at": 1791950482,
  "expires_at": 1792555200,
  "seconds": 5,
  "size": "768P",
  "error": {
    "code": "upstream_error",
    "message": "upstream task failed"
  }
}
```

404:
```json
{
  "error": {
    "message": "video task not found",
    "type": "invalid_request_error",
    "code": "not_found"
  }
}
```

| `status` | 含义 | 你需要做的 |
| --- | --- | --- |
| `queued` | 已受理，尚未开始生成 | 继续轮询 |
| `in_progress` | 服务商正在生成 | 继续轮询 |
| `completed` | 已完成 | 视频任务调用 `/content` 下载；上下文增强任务读取 `output_text` |
| `failed` | 已失败 | 读取 `error`，不收费 |

| 字段 | 说明 |
| --- | --- |
| `id` | 网关的任务 ID，格式为 `aigwt_<服务商>_<任务类型>_<随机串>`，例如 `aigwt_minimax_videos_…`、`aigwt_minimax_videos-regenerations_…`、`aigwt_minimax_videos-context_…`。它不是服务商的任务 ID，向服务商报障时请用 `error.provider_request_id` |
| `object` | 固定为 `video`，上下文增强任务也一样 |
| `model` | 实际调用的上游模型名，例如 `MiniMax-H3` |
| `status` | `queued`、`in_progress`、`completed`、`failed` 之一 |
| `progress` | 完成前为 `0`，完成后为 `100`，不表示中间进度，请以 `status` 判断 |
| `created_at` / `completed_at` | 提交时间、结束时间（Unix 秒）。`completed_at` 只在任务结束后出现 |
| `expires_at` | 任务记录的过期时间，提交后 7 天。过期后查询返回 404 |
| `seconds` / `size` | 原样回显提交时的值，没有提交的不出现 |
| `output_text` | 仅上下文增强任务有，为增强后的提示词 |
| `error` | 仅 `failed` 时出现：`code`、`message`，以及有值时才出现的 `provider_request_id`（服务商的请求标识）和 `retryable` |
| `usage` | 任务完成后出现：视频生成和升清为 `duration_seconds`（计费秒数：视频生成含参考视频的秒数；升清只计源视频的秒数），上下文增强为 `completion_tokens`（输出 Token 数） |

- 失败任务的 `error.code` 是服务商给出的错误码；服务商没有给出可识别的错误码时为 `upstream_error`。`error.retryable` 为 `false` 表示原样重试不会成功，`true` 表示可以重试，没有这个字段表示网关无法判断。完整的取值见[异步任务](https://platform.lyrouter.com/ai_gateway/docs/async)。
- **轮询间隔**：提交后约 10 秒开始查询，之后逐步放宽到每 30 秒一次。
- **等待上限**：提交 24 小时后仍未完成的任务不会再有结果，也不收费，可以停止轮询。这时查询结果可能仍显示 `queued` 或 `in_progress`，任务的实际状态请在控制台工作空间的 **多媒体生成** 页面按任务 ID 搜索查看（「状态」显示「已过期」）。

## 下载视频

`GET /v1/videos/{id}/content`

视频生成或升清任务变为 `completed` 后调用，返回 302，`Location` 响应头是服务商提供的带签名的视频地址。让客户端跟随跳转即可下载：cURL 加 `-L`，Python `httpx` 设 `follow_redirects=True`，Node.js 的 `fetch` 默认跟随。上下文增强任务的结果是 `output_text` 中的文本，没有视频可下载，不要对它调用这个接口：目前对已完成的上下文增强任务调用会返回 HTTP 500，正文是 HTML 页面，不是 JSON 错误。

### 请求示例

**请求示例**

cURL:
```bash
# 下载到本地
curl -L https://api.lyrouter.com/v1/videos/$TASK_ID/content \
  -H "Authorization: Bearer $LYROUTER_API_KEY" \
  -o video.mp4

# 只取视频地址，不下载
curl -s -o /dev/null -w '%{redirect_url}\n' \
  https://api.lyrouter.com/v1/videos/$TASK_ID/content \
  -H "Authorization: Bearer $LYROUTER_API_KEY"
```

Python:
```python
import os
import httpx

task_id = "aigwt_minimax_videos_Qm3Tz8VbN1sLk0pYc4dReA"
with httpx.stream(
    "GET",
    f"https://api.lyrouter.com/v1/videos/{task_id}/content",
    headers={"Authorization": f"Bearer {os.environ['LYROUTER_API_KEY']}"},
    follow_redirects=True,
    timeout=600,
) as video:
    video.raise_for_status()
    with open("video.mp4", "wb") as f:
        for chunk in video.iter_bytes():
            f.write(chunk)
```

Node.js:
```js
import { writeFile } from "node:fs/promises";

const taskId = "aigwt_minimax_videos_Qm3Tz8VbN1sLk0pYc4dReA";
const video = await fetch(`https://api.lyrouter.com/v1/videos/${taskId}/content`, {
  headers: { Authorization: `Bearer ${process.env.LYROUTER_API_KEY}` },
});
if (!video.ok) throw new Error(`download failed: ${video.status}`);
await writeFile("video.mp4", Buffer.from(await video.arrayBuffer()));
```

### 响应

**响应示例**

302:
```text
状态码：302
Location: <服务商提供的带签名的视频地址>
```

409:
```json
{
  "error": {
    "message": "video is not ready yet",
    "type": "invalid_request_error",
    "code": "not_ready"
  }
}
```

404:
```json
{
  "error": {
    "message": "video task not found",
    "type": "invalid_request_error",
    "code": "not_found"
  }
}
```

- 视频地址在任务完成时确定，之后每次调用都跳转到同一个地址。地址的有效期由服务商决定，通常为 24 小时，过期后无法通过网关重新获取，请拿到后立即下载或转存到自己的存储。
- 任务还没完成（或已失败）时返回 409。这类 4xx 也会计入 API Key 的错误率，频繁调用可能触发[熔断](https://platform.lyrouter.com/ai_gateway/docs/rate-errors)，请先查询到 `completed` 再下载。

## 视频升清

`POST /v1/videos/regenerations`

把一条 **H3 生成的 768P 视频**重新生成为 2K，仅 `minimax/minimax-h3` 支持。它只适用于 H3 自己产出、符合 768P 规格的视频，不是通用的视频放大。提交后返回同样的任务对象，用 `GET /v1/videos/{id}` 查询，完成后用 `/content` 下载。只接受 JSON 请求体。

### 请求体

| 参数 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- |
| `model` | string | 是 | `minimax/minimax-h3` |
| `prompt` | string | 是 | 生成那条 768P 视频时**实际送入模型的最终提示词**。当时用过上下文增强的，填增强**之后**的提示词 |
| `source_video` | string | 是 | 要升清的 768P 视频的 URL |
| `size` | string | 否 | 目标分辨率，默认 `2K` |
| `first_frame` / `last_frame` / `reference_image` / `input_reference` / `reference_audio` | — | 否 | 生成源视频时用过的参考素材，按当时的输入原样附上，写法同「提交任务」 |
| `watermark` | boolean | 否 | 是否给视频加水印 |

- **不要传 `seconds`**：时长由源视频决定，传了返回 400 `seconds is not supported for video regeneration (the source video determines the duration)`。
- `generate_audio`、`output_format`、`omni_reference_task_type` 同样返回 400 `<参数名> is not supported for this model`。
- 源视频必须满足：含音轨、24 fps、宽和高都能被 32 整除、面积在 589,824～1,032,192 像素之间、总帧数 107～362（每档递增 17 帧，约 4～15 秒）。
- 服务商要求 `prompt` 和参考素材与生成源视频时完全一致。给的不是当时那一份时**不会报错**，只是拿不到预期的效果，网关也无法替你校验。

### 请求示例

**请求示例**

cURL:
```bash
curl https://api.lyrouter.com/v1/videos/regenerations \
  -H "Authorization: Bearer $LYROUTER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "minimax/minimax-h3",
    "prompt": "生成那条 768P 视频时实际送入模型的最终提示词",
    "source_video": "https://example.com/h3-768p.mp4",
    "size": "2K"
  }'
```

Python:
```python
import os
import httpx

resp = httpx.post(
    "https://api.lyrouter.com/v1/videos/regenerations",
    headers={"Authorization": f"Bearer {os.environ['LYROUTER_API_KEY']}"},
    json={
        "model": "minimax/minimax-h3",
        "prompt": "生成那条 768P 视频时实际送入模型的最终提示词",
        "source_video": "https://example.com/h3-768p.mp4",
        "size": "2K",
    },
    timeout=60,
)
resp.raise_for_status()
print(resp.json()["id"])
```

Node.js:
```js
const resp = await fetch("https://api.lyrouter.com/v1/videos/regenerations", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.LYROUTER_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    model: "minimax/minimax-h3",
    prompt: "生成那条 768P 视频时实际送入模型的最终提示词",
    source_video: "https://example.com/h3-768p.mp4",
    size: "2K",
  }),
});
if (!resp.ok) throw new Error(await resp.text());
console.log((await resp.json()).id);
```

### 响应

提交成功返回 HTTP 200 和任务对象，`id` 以 `aigwt_minimax_videos-regenerations_` 开头；完成后的 `usage.duration_seconds` 为计费秒数。下面的 400 是没有传 `source_video`。

**响应示例**

200:
```json
{
  "id": "aigwt_minimax_videos-regenerations_Hk2Lp9WqR4tYv7Xz1Bc3Nd",
  "object": "video",
  "model": "MiniMax-H3",
  "status": "queued",
  "progress": 0,
  "created_at": 1791950400,
  "expires_at": 1792555200,
  "size": "2K"
}
```

400:
```json
{
  "error": {
    "message": "source_video is required for video regeneration (the 768P video to upscale)",
    "type": "invalid_request_error",
    "code": "invalid_parameter"
  }
}
```

## 上下文增强

`POST /v1/videos/context`

把提示词和多模态上下文整理成一段结构化、语义更丰富的提示词，仅 `minimax/minimax-h3` 支持。**这个接口只返回文本，不生成视频。** 它同样是异步任务：提交后用 `GET /v1/videos/{id}` 查询，完成后增强后的提示词在任务对象的 `output_text` 中，没有 `/content` 可下载。典型用法是两步：先调本接口拿到 `output_text`，再把它作为 `prompt` 提交给 `POST /v1/videos`。只接受 JSON 请求体。

> [!warning]
> 目前这个接口不会拒绝 Seedance 模型：传 Seedance 的模型 ID 会生成一条普通视频（没有 `output_text`），并按视频计费。请只用 `minimax/minimax-h3` 调用本接口。

### 请求体

| 参数 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- |
| `model` | string | 是 | `minimax/minimax-h3` |
| `prompt` | string | 是 | 原始提示词 |
| `seconds` | integer | 是 | 计划生成的视频时长（秒），4～15。不传时返回 400，正文是纯文本 `binding: expr_path=duration, cause=missing required parameter`（响应头目前仍是 `Content-Type: application/json`） |
| `size` | string | 否 | 写法同「提交任务」。不传时网关按 `2K` 发给服务商 |
| `first_frame` / `last_frame` / `reference_image` / `input_reference` / `reference_audio` | — | 否 | 多模态上下文，写法和限制同「提交任务」 |

`generate_audio`、`output_format`、`omni_reference_task_type`、`source_video` 返回 400 `<参数名> is not supported for this model`。

### 请求示例

**请求示例**

cURL:
```bash
curl https://api.lyrouter.com/v1/videos/context \
  -H "Authorization: Bearer $LYROUTER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "minimax/minimax-h3",
    "prompt": "一只猫在雨里走过霓虹街道",
    "seconds": 5
  }'
```

Python:
```python
import os
import httpx

resp = httpx.post(
    "https://api.lyrouter.com/v1/videos/context",
    headers={"Authorization": f"Bearer {os.environ['LYROUTER_API_KEY']}"},
    json={
        "model": "minimax/minimax-h3",
        "prompt": "一只猫在雨里走过霓虹街道",
        "seconds": 5,
    },
    timeout=60,
)
resp.raise_for_status()
print(resp.json()["id"])
```

Node.js:
```js
const resp = await fetch("https://api.lyrouter.com/v1/videos/context", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.LYROUTER_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    model: "minimax/minimax-h3",
    prompt: "一只猫在雨里走过霓虹街道",
    seconds: 5,
  }),
});
if (!resp.ok) throw new Error(await resp.text());
console.log((await resp.json()).id);
```

### 响应

提交成功返回 HTTP 200 和任务对象，`id` 以 `aigwt_minimax_videos-context_` 开头。完成后的结果见上文「查询任务」中的「200 上下文增强」：`output_text` 是增强后的提示词，`usage.completion_tokens` 是输出 Token 数。下面的 400 是用 `minimax/minimax-h3-max`（已下线）调用本接口时服务商返回的报错，网关原样透传，没有 `error.code`。

**响应示例**

200:
```json
{
  "id": "aigwt_minimax_videos-context_Zp8Rk2Vt5Lq9Wx3Ny6Bh1A",
  "object": "video",
  "model": "MiniMax-H3",
  "status": "queued",
  "progress": 0,
  "created_at": 1791950400,
  "expires_at": 1792555200,
  "seconds": 5
}
```

400:
```json
{
  "type": "error",
  "error": {
    "type": "bad_request_error",
    "message": "invalid params, model MiniMax-H3-Max does not support resolution 2K, supported resolutions: 480P, 768P (2013)",
    "http_code": "400"
  },
  "request_id": "<服务商的请求标识>"
}
```

## 删除任务

`DELETE /v1/videos/{id}`

对已结束（`completed` 或 `failed`）的任务返回 HTTP 200 和任务对象，并带 `"deleted": true`；任务记录不会因此删除，在 `expires_at` 之前仍可查询和下载。对进行中的任务调用返回 409 `cancel_not_supported`：服务商不提供取消接口，请等待任务结束。OpenAI 官方 SDK 的 `videos.delete()` 读取的就是这个 `deleted` 字段。

## MiniMax 特有参数与限制

### `size`：短边必须正好落在档位上

| 写法 | 网关怎么处理 |
| --- | --- |
| 档位名（`480P`、`768P`、`2K`） | 原样送出 |
| `宽x高` | 取**短边**查档位表：短边必须**正好**是 `480` 或 `768`，否则网关直接返回 400 并列出合法写法；宽高比按就近匹配 |

- `1280x720` 的短边是 720，不在档位里，会被拒绝；768P 横屏可以写 `1366x768`，竖屏写 `768x1366`。
- `2K` 没有固定的短边，只能写档位名 `"2K"`。
- 用 `宽x高` 时，宽高比也要落在 21:9、16:9、4:3、1:1、3:4、9:16 之一（允许 2% 误差），否则返回 400 `size implies an aspect ratio this model does not support: <取值>`。
- 档位决定的是分辨率档，产物的实际像素由服务商决定，以下载到的视频为准。实测：文生视频和多模态参考用 `480P` 或 `854x480` 时产物为 1038×576（短边 576，不是 480）；`768P` 为 1344×768；首尾帧图生视频用 1:1 的图片 + `854x480` 时为 768×768；升清到 `2K` 为 2560×1440。

### 三种输入场景与宽高比

| 场景 | 怎么传 | 宽高比 |
| --- | --- | --- |
| 文生视频 | 只传 `prompt` | 必须是明确的比例，不能自适应：网关按 `size` 推出比例；`size` 写的是档位名时按 16:9 生成 |
| 图生视频 | `first_frame`（+ `last_frame`） | 由输入图片决定，`size` 只决定分辨率 |
| 多模态参考生视频 | `reference_image` / `input_reference` / `reference_audio` | `size` 写 `宽x高` 时按它推出的比例，写档位名时自适应。**传参考视频时不能自适应**：`size` 请写成 `宽x高`（如 `854x480`、`1366x768`），写档位名时任务会失败 |

### 输入素材限制

| 素材 | 限制 |
| --- | --- |
| 图片 | JPG、JPEG、PNG、WEBP、HEIC、HEIF；宽高 [256, 5760] px；宽高比 [0.4, 2.5]；≤ 30 MB；首帧 ≤ 1、尾帧 ≤ 1、参考图 ≤ 9 |
| 视频 | MP4、MOV（视频 H.264 / H.265，音频 AAC / MP3）；≤ 50 MB；≤ 3 个；单段 [2, 15] 秒且总时长 ≤ 15 秒；宽高 [256, 5760] px；宽高比 [0.4, 2.5]；帧率 [23.976, 60] |
| 音频 | WAV、MP3；≤ 15 MB；≤ 3 个；单段 [2, 15] 秒且总时长 ≤ 15 秒 |
| 请求体 | 总大小 ≤ 64 MB，大文件请用公网 URL |

参考素材的尺寸和宽高比由服务商校验：不符合时服务商以错误码 2013 拒绝，并在报错中指出是哪一项（如 `content[N].image_url`），不收费。H3-Max 不支持多张参考图，服务商会拒绝并建议改用 H3（H3-Max 已被服务商下线，目前不可用）。

### 计费

| 任务 | 计量 | 分档 |
| --- | --- | --- |
| 视频生成（`minimax-h3`） | 按秒 | 480P / 768P / 2K 三档 |
| 视频生成（`minimax-h3-max`，已被服务商下线，目前不可用，请改用 `minimax-h3`） | 按秒 | 480P / 768P 两档 |
| 视频升清（`/v1/videos/regenerations`） | 按秒 | 单独一档 |
| 上下文增强（`/v1/videos/context`） | 按 Token | 输入、输出两项分开计 |

- **计费秒数是输入 + 输出的总和**，即任务对象中的 `usage.duration_seconds`：传了参考视频时，参考视频的秒数同样计入（例如 4 秒参考视频 + 4 秒输出，`duration_seconds` 为 8）。参考音频不收费。视频升清不相加，`duration_seconds` 只计源视频的秒数（实测源视频 4 秒，计 4）。
- **参考图单独计一项**（两个模型的参考图单价不同）：`minimax-h3` 的参考图前 5 张免费，超出部分按张计费。张数不在 `usage` 中，费用可在日志中查看。
- 上下文增强按输入、输出 Token 两项计费，任务对象的 `usage` 只列出 `completion_tokens`（输出），输入 Token 的费用同样计入。
- 价格在提交时锁定；任务变为 `completed` 时按服务商实际返回的计量结算；`failed` 的任务（包括超时）不收费。在日志中查看任务费用的方法见[异步任务](https://platform.lyrouter.com/ai_gateway/docs/async)。

## 完整调用示例

先用上下文增强把提示词扩写，再用增强后的提示词生成 768P 视频，轮询到结束后下载。cURL 示例需要 `jq`，请保存为脚本文件后用 `bash` 运行（出错时用 `exit` 退出）。

**完整示例**

cURL:
```bash
BASE=https://api.lyrouter.com/v1
AUTH="Authorization: Bearer $LYROUTER_API_KEY"

# 提交任务（请求体从标准输入读取），输出任务 ID；没拿到任务 ID 时打印响应并返回失败
submit() {
  local resp id
  resp=$(curl -s "$BASE/$1" -H "$AUTH" -H "Content-Type: application/json" -d @-)
  id=$(echo "$resp" | jq -r '.id // empty')
  if [ -z "$id" ]; then echo "提交失败：$resp" >&2; return 1; fi
  echo "task: $id" >&2
  echo "$id"
}

# 轮询一个任务直到结束，输出最终的任务对象；查询出错或等待超过 2 小时时返回失败
wait_task() {
  local delay=10 give_up_at=$(( $(date +%s) + 2 * 3600 )) task status
  while [ "$(date +%s)" -le "$give_up_at" ]; do
    sleep $delay
    delay=$(( delay * 3 / 2 > 30 ? 30 : delay * 3 / 2 ))
    task=$(curl -s "$BASE/videos/$1" -H "$AUTH")
    status=$(echo "$task" | jq -r '.status')
    case "$status" in
      completed|failed) echo "$task"; return 0 ;;
      queued|in_progress|"") ;;  # 空值多为网络抖动：保留任务 ID 继续查
      *) echo "查询出错：$task" >&2; return 1 ;;
    esac
  done
  echo "$1 仍在处理，可稍后用同一个 ID 继续查询" >&2; return 1
}

# 1. 上下文增强，拿到增强后的提示词；失败时打印原因并退出
CTX_ID=$(echo '{"model":"minimax/minimax-h3","prompt":"一只猫在雨里走过霓虹街道","seconds":5}' \
  | submit videos/context) || exit 1
CTX=$(wait_task "$CTX_ID") || exit 1
PROMPT=$(echo "$CTX" | jq -r '.output_text // empty')
if [ -z "$PROMPT" ]; then echo "$CTX" | jq '.error'; exit 1; fi

# 2. 用增强后的提示词生成视频
VIDEO_ID=$(jq -n --arg p "$PROMPT" \
    '{model:"minimax/minimax-h3",prompt:$p,size:"768P",seconds:5}' \
  | submit videos) || exit 1
TASK=$(wait_task "$VIDEO_ID") || exit 1

# 3. 完成则下载，失败则打印原因
if [ "$(echo "$TASK" | jq -r '.status')" = "completed" ]; then
  curl -sL "$BASE/videos/$VIDEO_ID/content" -H "$AUTH" -o video.mp4
else
  echo "$TASK" | jq '.error'
fi
```

Python:
```python
import os
import time
import httpx

BASE = "https://api.lyrouter.com/v1"
HEADERS = {"Authorization": f"Bearer {os.environ['LYROUTER_API_KEY']}"}

def wait_task(client: httpx.Client, task: dict, max_wait: int = 2 * 3600) -> dict:
    """从 10 秒开始轮询，逐步放宽到 30 秒，直到任务结束。"""
    if not task.get("id"):
        raise RuntimeError(f"没有拿到任务 ID：{task}")
    print("task:", task["id"])
    delay, give_up_at = 10, time.time() + max_wait
    while task["status"] in ("queued", "in_progress"):
        if time.time() > give_up_at:
            raise TimeoutError(f"{task['id']} 仍在处理，可稍后用同一个 ID 继续查询")
        time.sleep(delay)
        delay = min(delay * 1.5, 30)
        try:
            resp = client.get(f"{BASE}/videos/{task['id']}")
            resp.raise_for_status()
        except httpx.TransportError:
            continue  # 网络抖动：保留任务 ID 继续查，不要重新提交
        task = resp.json()
    if task["status"] == "failed":
        err = task.get("error", {})
        raise RuntimeError(f"{err.get('code')}: {err.get('message')}")
    return task

with httpx.Client(headers=HEADERS, timeout=60) as client:
    # 1. 上下文增强，拿到增强后的提示词
    resp = client.post(f"{BASE}/videos/context", json={
        "model": "minimax/minimax-h3",
        "prompt": "一只猫在雨里走过霓虹街道",
        "seconds": 5,
    })
    resp.raise_for_status()
    prompt = wait_task(client, resp.json())["output_text"]

    # 2. 用增强后的提示词生成视频
    resp = client.post(f"{BASE}/videos", json={
        "model": "minimax/minimax-h3",
        "prompt": prompt,
        "size": "768P",
        "seconds": 5,
    })
    resp.raise_for_status()
    task = wait_task(client, resp.json())

    # 3. 下载：接口返回 302，跟随跳转即可
    with client.stream("GET", f"{BASE}/videos/{task['id']}/content",
                       follow_redirects=True, timeout=600) as video:
        video.raise_for_status()
        with open("video.mp4", "wb") as f:
            for chunk in video.iter_bytes():
                f.write(chunk)
    print("已保存 video.mp4，用量：", task.get("usage"))
```

Node.js:
```js
import { writeFile } from "node:fs/promises";

const BASE = "https://api.lyrouter.com/v1";
const headers = { Authorization: `Bearer ${process.env.LYROUTER_API_KEY}` };
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));

async function submit(path, body) {
  const resp = await fetch(`${BASE}${path}`, {
    method: "POST",
    headers: { ...headers, "Content-Type": "application/json" },
    body: JSON.stringify(body),
  });
  if (!resp.ok) throw new Error(await resp.text());
  return resp.json();
}

// 从 10 秒开始轮询，逐步放宽到 30 秒，直到任务结束
async function waitTask(task) {
  if (!task.id) throw new Error(`没有拿到任务 ID：${JSON.stringify(task)}`);
  console.log("task:", task.id);
  let delay = 10_000;
  while (task.status === "queued" || task.status === "in_progress") {
    await sleep(delay);
    delay = Math.min(delay * 1.5, 30_000);
    let resp;
    try {
      resp = await fetch(`${BASE}/videos/${task.id}`, { headers });
    } catch {
      continue; // 网络抖动：保留任务 ID 继续查，不要重新提交
    }
    if (!resp.ok) throw new Error(await resp.text());
    task = await resp.json();
  }
  if (task.status === "failed") {
    throw new Error(`${task.error?.code}: ${task.error?.message}`);
  }
  return task;
}

// 1. 上下文增强，拿到增强后的提示词
const ctx = await waitTask(await submit("/videos/context", {
  model: "minimax/minimax-h3",
  prompt: "一只猫在雨里走过霓虹街道",
  seconds: 5,
}));

// 2. 用增强后的提示词生成视频
const task = await waitTask(await submit("/videos", {
  model: "minimax/minimax-h3",
  prompt: ctx.output_text,
  size: "768P",
  seconds: 5,
}));

// 3. 下载：fetch 默认跟随 302 跳转
const video = await fetch(`${BASE}/videos/${task.id}/content`, { headers });
if (!video.ok) throw new Error(`download failed: ${video.status}`);
await writeFile("video.mp4", Buffer.from(await video.arrayBuffer()));
console.log("saved video.mp4", task.usage);
```

## 常见错误

错误响应为 `{"error":{"message":"...","type":"...","code":"..."}}`。

| 状态码 | `error.message` | `error.code` | 原因与处理 |
| --- | --- | --- | --- |
| 400 | `this model takes discrete resolution tiers, not arbitrary sizes: ...` | `invalid_parameter` | `size` 的短边不是 480 或 768。改写档位名（如 `768P`、`2K`）或短边正好为 480 / 768 的尺寸 |
| 400 | `size implies an aspect ratio this model does not support: <取值>` | `invalid_parameter` | `size` 推出的宽高比不在支持的比例中 |
| 400 | `seconds must be a positive whole number of seconds` | `invalid_parameter` | `seconds` 不是正整数 |
| 400 | `<参数名> is not supported for this model` | `invalid_parameter` | 传了 MiniMax 不支持的 `generate_audio`、`output_format`、`omni_reference_task_type`，或在 `/v1/videos` 上传了 `source_video`。从请求中删掉 |
| 400 | `seconds is not supported for video regeneration (the source video determines the duration)` | `invalid_parameter` | 视频升清不接受 `seconds`，删掉即可 |
| 400 | `source_video is required for video regeneration (the 768P video to upscale)` | `invalid_parameter` | 视频升清缺少源视频 |
| 400 | `This model does not support the videos_regenerations operation`（或 `videos_context`） | `unsupported_model_operation` | 所选模型不支持视频升清或上下文增强，改用 `minimax/minimax-h3`。目前并非所有不支持的模型都返回这条，见表下说明 |
| 400 | `unsupported parameters for this model: <参数名>` | `unsupported_parameters` | 带了 `X-Gateway-Strict-Parameters: true`，且请求中有会被丢弃的参数 |
| 403 | `Model is not available in the model plaza` | `model_not_available` | 模型没有在本工作空间开通，或使用了虚拟模型、`lyrouter/auto`。换用已开通的具体模型 ID |
| 404 | `video task not found` | `not_found` | 任务 ID 不存在、已超过 7 天、属于其他工作空间，或传入的是服务商的任务 ID |
| 409 | `video is not ready yet` | `not_ready` | 任务还没完成（或已失败）就调用了 `/content`。先查询到 `completed` |
| 409 | `cancelling an in-flight video task is not supported: ...` | `cancel_not_supported` | 对进行中的任务调用了 `DELETE`。请等待任务结束 |
| 429 | `upstream concurrency full` | `upstream_concurrency_full` | 服务商账户的并发任务已满，换 API Key 无效，稍后重新提交 |

- 目前用其他模型调用视频升清或上下文增强，不一定返回 `unsupported_model_operation`：Seedance 2.5 调视频升清返回 400 `source_video is not supported for this model`（`invalid_parameter`）；Seedance 模型调上下文增强会被受理，生成一条普通视频并照常计费；`minimax/minimax-h3-max`（已下线）调视频升清会被受理并建出任务，调上下文增强返回服务商透传的 400（`invalid params, model MiniMax-H3-Max does not support resolution 2K, …`，没有 `error.code`）。这两个接口请只用 `minimax/minimax-h3` 调用。
- 用表单提交时 `input_reference` 只能传 URL。目前上传文件**不会报错**，文件会被忽略，任务按没有参考视频的方式生成并照常计费；只有填了非 URL 的文本（如 `video.mp4`）时才返回 400 纯文本 `input_reference must be a URL; uploading a file is not supported by this gateway (pass a publicly reachable https:// URL instead)`。这类由网关返回的纯文本报错，响应头目前仍是 `Content-Type: application/json`，解析前请先判断正文是不是 JSON。
- 时长超出范围、图生视频与多模态参考混用、参考素材不合规等由服务商拒绝，状态码和报错原文由服务商决定；任务提交后才发现的问题以失败任务的形式返回，见上文「查询任务」。完整列表见[错误码与请求标识](https://platform.lyrouter.com/ai_gateway/docs/errors)。

## 常见问题

### 提示短边必须是 480 或 768，怎么办？

**答：** MiniMax 的分辨率是离散档位，不是任意尺寸。直接写档位名（如 `"768P"`、`"2K"`），或使用短边正好为 480 / 768 的尺寸（如 `1366x768`）。`2K` 只能写档位名。

### H3-Max 能做视频升清或上下文增强吗？

**答：** 不能。这两个接口只支持 `minimax/minimax-h3`。目前用 H3-Max 调用时网关不会直接拒绝：调视频升清会被受理并建出任务；调上下文增强返回服务商透传的 400（`invalid params, model MiniMax-H3-Max does not support resolution 2K, …`，没有 `error.code`）。H3-Max 也不支持 2K 和 4 秒时长。另外，H3-Max 已被服务商下线，目前连普通视频生成也不可用（调用返回 HTTP 200，但任务对象为空、没有任务 ID），请改用 `minimax/minimax-h3`。

### 升清后的视频效果不理想，怎么办？

**答：** 确认三件事：源视频是 H3 生成的 768P 产物；`prompt` 是生成源视频时实际送入模型的最终提示词（用过上下文增强的，填增强之后的那份）；参考素材与生成时一致。这些不一致时网关和服务商都不会报错，只是效果不达预期。

### 上下文增强完成了，为什么下载不到视频？

**答：** 上下文增强只产出文本，结果在任务对象的 `output_text` 中，没有视频可下载；目前对它调用 `/content` 会返回 HTTP 500。把 `output_text` 作为 `prompt` 提交给 `POST /v1/videos` 才会生成视频。

### 传了参考视频，为什么计费秒数比视频时长多？

**答：** MiniMax 的计费秒数是输入加输出：参考视频的秒数也计入 `usage.duration_seconds`。参考音频不收费，参考图前 5 张免费、超出部分按张计费。

### 传了参考视频，任务失败并提示 `aspect ratio "adaptive" is not supported`，怎么办？

**答：** 完整报错是 `h3submit: aspect ratio "adaptive" is not supported for task ref2va`。传参考视频（`input_reference`）时，MiniMax 要求明确的宽高比，而 `size` 写成档位名（如 `480P`）时网关发出的是自适应比例。把 `size` 改成 `宽x高`，短边为 480 或 768，例如 `854x480`、`1366x768`，再重新提交。这类失败不收费。

### 视频地址过期了，还能重新下载吗？

**答：** 不能。视频地址在任务完成时确定，再次调用 `/content` 拿到的仍是同一个地址，过期后需要重新提交任务。请在任务完成后立即下载或转存。

### 查询结果一直是 `in_progress`，怎么办？

**答：** 视频生成通常需要几分钟。提交 24 小时后仍未完成的任务不会再有结果，也不收费，可以停止轮询，并在控制台 **多媒体生成** 页面按任务 ID 查看任务状态。不要在等待期间重复提交同样的请求，每次提交都会生成新任务并单独计费。
