# 万相 3.0 视频生成 API

> 任务式异步接口：文生视频、图生视频、参考生视频；与阿里百炼（DashScope）原生格式完全一致，把 Base URL 指向网关、只换 API Key 即可调用

万相 3.0（`wan3.0-video` 与 `wan3.0-video-prime`，**两个模型调用方式完全相同**，仅需切换请求体里的 `model` 字段）视频生成采用**异步任务**模式：先提交任务拿到 `task_id`，再轮询查询接口获取成片。接口路径、请求体、响应结构与阿里百炼（DashScope）原生完全一致——**你只需把 Base URL 从百炼域名换成 `https://api.looprouter.com`、把 API Key 换成 LoopRouter 的 `sk-` 令牌，已有的百炼官方示例、SDK 或自研代码即可直接运行**，无需业务空间 ID（`WorkspaceId`）。

<Tip>
文生、图生、参考生视频共用同一个接口，仅靠 `input.media` 数组的组合区分模式。`wan3.0-video` 与 `wan3.0-video-prime` 的请求体、参数、响应结构完全一致，切换模型只改 `model` 字段。模型清单、定价与常见问题见 [能力总览](/api-capabilities/wan3/overview)。
</Tip>

<Warning>
`ratio` 仅支持 `adaptive` / `16:9` / `4:3` / `1:1` / `3:4` / `9:16`，两个模型均**不支持 `21:9`**，传入会返回参数错误。
</Warning>

<Warning>
**接入前请注意以下差异点，可以避开绝大多数排障时间：**

1. Base URL 为 `https://api.looprouter.com`，完整路径是 `/api/v1/services/aigc/video-generation/video-synthesis`，**不需要** `{WorkspaceId}` 子域。
2. 鉴权用 LoopRouter 的 `sk-` 令牌（`Authorization: Bearer sk-...`），不是百炼 API Key。
3. `X-DashScope-Async` 请求头可选，值为 `enabled`。
4. `resolution` 支持 `480P` / `720P` / `1080P`（默认 `1080P`）；传入其他档位返回错误且不扣费。
5. `duration` 传 **2–30** 的明确整数；网关**不支持**智能时长 `-1`。有视频输入时「输入视频总时长 + 输出时长 ≤ 30 秒」。
6. 成功状态是 `SUCCEEDED`（全大写）；成片 `output.video_url` 为临时链接，请尽快转存。
</Warning>

## 代码示例

<CodeGroup>

```bash 创建任务（cURL）
curl --location "https://api.looprouter.com/api/v1/services/aigc/video-generation/video-synthesis" \
  -H "X-DashScope-Async: enabled" \
  -H "Authorization: Bearer $LOOPROUTER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "wan3.0-video",
    "input": {
      "prompt": "一只小猫在月光下的屋顶上奔跑，城市的霓虹灯在远处闪烁，电影级画质，流畅运镜。"
    },
    "parameters": {
      "resolution": "1080P",
      "ratio": "adaptive",
      "duration": 5,
      "prompt_extend": true
    }
  }'

# 返回：{"output":{"task_status":"PENDING","task_id":"..."},"request_id":"..."}
# 只返回任务 ID，视频需轮询查询接口获取
```

```python 完整流程（Python）
import time
import requests

BASE = "https://api.looprouter.com"
HEADERS = {
    "Authorization": "Bearer sk-你的APIKey",
    "Content-Type": "application/json",
    "X-DashScope-Async": "enabled",  # 可选
}

# 1. 创建任务
resp = requests.post(
    f"{BASE}/api/v1/services/aigc/video-generation/video-synthesis",
    headers=HEADERS,
    json={
        "model": "wan3.0-video",
        "input": {
            "prompt": "一只小猫在月光下的屋顶上奔跑，城市的霓虹灯在远处闪烁，电影级画质，流畅运镜。"
        },
        "parameters": {
            "resolution": "1080P",
            "ratio": "adaptive",
            "duration": 5,          # 2~30 秒的明确整数，不支持 -1
            "prompt_extend": True,   # 开启 prompt 智能改写，短提示词效果提升明显
            # "audio": True,         # 默认输出含声音
            # "seed": 42,            # 固定随机种子，结果更可复现
        },
    },
)
resp.raise_for_status()
task_id = resp.json()["output"]["task_id"]
print("任务已创建:", task_id)

# 2. 轮询任务状态（建议 15 秒一次；长时长任务可适当延长）
while True:
    task = requests.get(
        f"{BASE}/api/v1/tasks/{task_id}",
        headers={"Authorization": HEADERS["Authorization"]},
    ).json()
    status = task["output"]["task_status"]
    print("当前状态:", status)
    if status in ("SUCCEEDED", "FAILED", "CANCELED", "UNKNOWN"):
        break
    time.sleep(15)

# 3. 下载成片（下载时不要携带 Authorization 头）
if status == "SUCCEEDED":
    video_url = task["output"]["video_url"]
    with requests.get(video_url, stream=True) as r:
        r.raise_for_status()
        with open("output.mp4", "wb") as f:
            for chunk in r.iter_content(chunk_size=1 << 20):
                f.write(chunk)
    print("已保存 output.mp4")
else:
    print("任务未成功:", task["output"].get("message"))
```

```python 图生 / 首尾帧 / 参考生视频（Python）
# 模式二：首帧图生视频 —— 用 first_frame 严格指定视频第一帧
first_frame_body = {
    "model": "wan3.0-video",
    "input": {
        "prompt": "镜头缓缓推进，人物转身微笑，背景光线由冷转暖。",
        "media": [
            {"type": "first_frame", "url": "https://example.com/first.png"}
        ],
    },
    "parameters": {"resolution": "1080P", "ratio": "adaptive", "duration": 5},
}

# 模式三：首尾帧图生视频 —— 同时约束首帧与尾帧
first_last_frame_body = {
    "model": "wan3.0-video",
    "input": {
        "prompt": "一个年轻女孩从微笑逐渐变为大笑，镜头缓缓推进。",
        "media": [
            {"type": "first_frame", "url": "https://example.com/first.png"},
            {"type": "last_frame", "url": "https://example.com/last.jpg"},
        ],
    },
    "parameters": {"resolution": "1080P", "ratio": "adaptive", "duration": 5},
}

# 模式四：全能参考生视频 —— 参考图/参考视频/参考音频自由组合，
# prompt 中可用「图1」「视频1」按 media 数组顺序指代（图与视频分别计数）
reference_body = {
    "model": "wan3.0-video",
    "input": {
        "prompt": "视频1抱着图1，在图2的椅子上弹奏一支舒缓的乡村民谣。",
        "media": [
            {"type": "reference_video", "url": "https://example.com/role.mp4"},
            {"type": "reference_image", "url": "https://example.com/object1.png"},
            {"type": "reference_image", "url": "https://example.com/chair2.png"},
            # {"type": "reference_audio", "url": "https://example.com/voice.mp3"},
        ],
    },
    "parameters": {"resolution": "480P", "ratio": "adaptive", "duration": 5, "prompt_extend": True},
}
```

```javascript Node.js
const BASE = "https://api.looprouter.com";
const KEY = process.env.LOOPROUTER_API_KEY;

async function main() {
  // 创建任务
  const createRes = await fetch(
    `${BASE}/api/v1/services/aigc/video-generation/video-synthesis`,
    {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        "X-DashScope-Async": "enabled",
        Authorization: `Bearer ${KEY}`,
      },
      body: JSON.stringify({
        model: "wan3.0-video",
        input: {
          prompt: "一只小猫在月光下的屋顶上奔跑，城市的霓虹灯在远处闪烁。",
        },
        parameters: { resolution: "1080P", ratio: "adaptive", duration: 5 },
      }),
    }
  );
  const taskId = (await createRes.json()).output.task_id;
  console.log("任务已创建:", taskId);

  // 轮询
  for (;;) {
    await new Promise((r) => setTimeout(r, 15000));
    const task = await (
      await fetch(`${BASE}/api/v1/tasks/${taskId}`, {
        headers: { Authorization: `Bearer ${KEY}` },
      })
    ).json();
    const status = task.output.task_status;
    console.log("当前状态:", status);
    if (["SUCCEEDED", "FAILED", "CANCELED", "UNKNOWN"].includes(status)) {
      if (status === "SUCCEEDED") console.log("视频地址:", task.output.video_url);
      else console.error("任务未成功:", task.output.message);
      break;
    }
  }
}

main();
```

```bash 查询任务（cURL）
curl -X GET "https://api.looprouter.com/api/v1/tasks/{task_id}" \
  -H "Authorization: Bearer $LOOPROUTER_API_KEY"
```

</CodeGroup>

## 请求参数速查

`POST /api/v1/services/aigc/video-generation/video-synthesis`，请求头 `Authorization: Bearer sk-...`、`Content-Type: application/json`；`X-DashScope-Async` 可选，值为 `enabled`。

| 参数 | 类型 | 必填 | 默认值 | 说明 |
| --- | --- | --- | --- | --- |
| `model` | string | 是 | — | `wan3.0-video` 或 `wan3.0-video-prime`（两者调用方式相同），可用模型以控制台「模型广场」为准 |
| `input` | object | 是 | — | 输入信息；`prompt` 与 `media` 至少填其一 |
| `input.prompt` | string | 条件必填 | — | 文本提示词，支持中英文，≤20000 字符；参考模式下可用「图1」「视频1」指代 `media` 素材 |
| `input.media` | array | 条件必填 | — | 媒体素材数组，见下方 [media 数组](#media-数组) |
| `parameters` | object | 否 | — | 视频处理参数 |
| `parameters.resolution` | string | 否 | `1080P` | `480P` / `720P` / `1080P`；传入其他档位返回错误且不扣费 |
| `parameters.ratio` | string | 否 | `adaptive` | `adaptive`、`16:9`、`4:3`、`1:1`、`3:4`、`9:16`；**不支持 `21:9`** |
| `parameters.duration` | int | 否 | `5` | 输出时长（秒），范围 **2–30**；有视频输入时「输入 + 输出 ≤ 30」。**不支持 `-1`** |
| `parameters.audio` | bool | 否 | `true` | 输出视频是否含音轨；开关声音价格相同 |
| `parameters.prompt_extend` | bool | 否 | `true` | 是否开启 prompt 智能改写，短提示词效果提升明显但略增耗时 |
| `parameters.seed` | int | 否 | — | 随机种子，用于复现结果，取值 [0, 2147483647] |
| `parameters.watermark` | bool | 否 | `false` | 是否添加水印标识 |

<Warning>
网关**不支持**智能时长 `duration: -1`（提交时真实时长未知，无法按秒预扣，会返回参数错误）。请传 2–30 秒的明确整数。
</Warning>

### media 数组

`input.media` 的每个元素为一个媒体对象，含 `type` 与 `url` 两个字段：

| type | 数量上限 | 说明 |
| --- | --- | --- |
| `first_frame` | 1 | 首帧图像，严格作为视频第一帧 |
| `last_frame` | 1 | 尾帧图像，严格作为视频最后一帧 |
| `reference_image` | 10 | 参考图像。数组中第 1 个对应「图1」，第 2 个对应「图2」，以此类推 |
| `reference_video` | 5 | 参考视频，合计时长 ≤15 秒。第 1 个对应「视频1」，依次类推（图与视频分别计数） |
| `reference_audio` | 5 | 参考音频，合计时长 ≤15 秒 |
| `file` | 1 | 文件（docx/pdf/pptx/xlsx 等，≤50 页、≤100MB）；不可与 `link` 同传 |
| `link` | 1 | 公开网页链接（无需登录的新闻/博客等）；不可与 `file` 同传 |

`url` 支持三种形式：**公网 URL**（HTTP/HTTPS）、**OSS 临时 URL**（`oss://...`）、**Base64 编码**（`data:{MIME_type};base64,{data}`）。

图像要求：JPEG/JPG/PNG（不支持透明通道）/BMP/WEBP，单边 [240, 8000]px，长宽比 ≤8:1，小于 20MB。视频要求：mp4/mov，单段 [1, 15] 秒，单边 [240, 4096]px，单文件 ≤100MB。音频要求：wav/mp3，单段 [1, 15] 秒，≤15MB。

<Warning>
`first_frame` / `last_frame` 与 `reference_image` / `reference_video` / `reference_audio` / `file` / `link` **互斥**，不能在同一请求中混用，否则返回 `The two modes are mutually exclusive` 错误。
</Warning>

### 生成模式

| 模式 | media 组合 |
| --- | --- |
| 文生视频 | 仅 `input.prompt`，不传 `media` |
| 首帧图生视频 | `prompt` + 1 个 `first_frame` |
| 首尾帧图生视频 | `prompt` + 1 个 `first_frame` + 1 个 `last_frame` |
| 参考生视频 | `prompt` + 参考图 / 参考视频 / 参考音频（可组合），或 `file` / `link` |

## 查询任务

`GET /api/v1/tasks/{task_id}`

任务状态机：`PENDING → RUNNING → SUCCEEDED / FAILED / CANCELED`；`UNKNOWN` 表示任务不存在或已过查询有效期。

成功响应示例（与百炼原生结构一致）：

```json
{
  "request_id": "78c9b768-0285-996c-b682-xxxxxx",
  "output": {
    "task_id": "17ed7e50-00cf-4509-aea1-xxxxxx",
    "task_status": "SUCCEEDED",
    "submit_time": "2026-08-06 10:01:35.452",
    "scheduled_time": "2026-08-06 10:01:35.507",
    "end_time": "2026-08-06 10:13:33.838",
    "orig_prompt": "一只小猫在月光下的屋顶上奔跑……",
    "video_url": "https://.../output.mp4"
  },
  "usage": {
    "video_count": 1,
    "duration": 5.0,
    "input_video_duration": 0.0,
    "output_video_duration": 5.0,
    "fps": 30,
    "SR": 1080,
    "ratio": "16:9"
  }
}
```

<Warning>
成片地址在 `output.video_url` 字段，为**临时链接**，请及时转存；下载时请求中**不要**携带 `Authorization` 头。
</Warning>

<Info>
`usage` 为成功任务的用量统计：`output_video_duration` 是实际输出时长（结算依据，向上取整到整秒计费），`SR` 为输出分辨率，`fps` 固定 30。
</Info>

## 错误响应

错误采用与百炼一致的结构 `{code, message, request_id}`：

```json
{
  "code": "InvalidApiKey",
  "message": "No API-key provided. (request id: ...)",
  "request_id": "7438d53d-6eb8-4596-8835-xxxxxx"
}
```

任务执行阶段的失败会在查询响应的 `output` 中回填 `code` / `message`，并把 `task_status` 置为 `FAILED`：

```json
{
  "request_id": "e5e57877-c0fc-47ed-8fad-xxxxxx",
  "output": {
    "task_id": "eff1443c-ccab-4676-aad3-xxxxxx",
    "task_status": "FAILED",
    "code": "InvalidParameter",
    "message": "The two modes are mutually exclusive. Do not pass reference_xx and first_frame/last_frame at the same time."
  }
}
```

| HTTP 状态码 | code | 常见原因 |
| --- | --- | --- |
| 400 | `InvalidParameter` | 缺少 `model`/`input`；`resolution` 非 480P/720P/1080P；`duration` 超出 2–30 或传了 `-1`；首帧与参考类混用 |
| 401 | `InvalidApiKey` | API Key 缺失或无效 |
| 403 | `AccessDenied` | 令牌无权访问该模型；内容审核未通过 |
| 429 | `Throttling` | 当前分组上游负载已饱和，请稍后再试 |
| 5xx | `InternalError` | 服务端异常，建议指数退避后重试 |

<Info>
429 与 5xx 建议采用指数退避重试（如 2s → 4s → 8s）。参数校验失败、鉴权失败与内容审核拒绝**均不扣费**；已预扣的失败任务会自动退款。
</Info>

## 相关文档

<CardGroup cols={3}>
  <Card title="能力总览与定价" icon="circle-info" href="/api-capabilities/wan3/overview">
    模型清单、技术规格、计费规则
  </Card>
  <Card title="快速开始" icon="rocket" href="/getting-started">
    三步跑通第一个视频
  </Card>
  <Card title="AI 集成指南" icon="robot" href="/ai-usage">
    llms.txt 与推荐提示词
  </Card>
</CardGroup>
