创建视频任务
提交异步视频任务;任务查询路径和响应格式因模型而异。
/v1/video/generationsHeaders请求头
左右滑动查看完整参数表
| 参数名 | 类型 | 必填 | 说明 | 默认值 / 示例 |
|---|---|---|---|---|
Authorization | string | 必填 | 平台 API Key,使用 Bearer 认证。 | |
Content-Type | string | 必填 | 请求体编码格式。 |
Request Body请求体
application/json左右滑动查看完整参数表
| 参数名 | 类型 | 必填 | 说明 | 默认值 / 示例 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
model | string | 必填 | 模型 ID;请替换为账户可用且支持当前接口的模型。 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
prompt | string | 可选 | 使用 prompt 格式的模型在此填写视频提示词。MiniMax-H3 使用顶层 content 数组中的 text 项,无需重复提供顶层 prompt。 | — | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
image | string | 可选 | 输入图片 URL 或 Base64;仅图生视频模型适用。 | — | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
duration | number | 可选 | 对 MiniMax-H3 必填 视频时长(秒),按具体模型填写整数。原生规格:Wan 3.0 为 2–30 秒,Seedance 2.5 文生为 4–30 秒,HappyHorse 1.1 文生为 3–15 秒,MiniMax-H3 为 4–15 秒,Veo 3.1 为 4/6/8 秒。 | — | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
seconds | string | 可选 | 兼容视频协议的时长字符串。若同时填写 seconds 与 duration,两者应使用相同秒数。 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
resolution | string | 可选 | 对 MiniMax-H3 必填 模型专用分辨率字符串。Seedance 2.5 使用 480p/720p/1080p,Wan 3.0 与 HappyHorse 1.1 使用 480P/720P/1080P,MiniMax-H3 使用 768P/2K,Veo 3.1 的参考值为 720p。字段支持情况因模型和通道而异,最终尺寸以输出文件为准。 | — | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
ratio | string | 可选 | MiniMax-H3 文生视频(content 仅含 text)必填顶层 ratio,取 21:9、16:9、4:3、1:1、3:4、9:16,不能使用 adaptive。官方图生视频按输入图片自适应;其他模型的参数位置见 metadata。 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
size | string | 可选 | 宽x高形式的兼容尺寸,例如 1280x720;仅适用于支持此字段的模型。 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
content | array | 可选 | 对 MiniMax-H3 必填 MiniMax-H3 原生内容数组。文生视频在顶层提供 type=text 的文本项,下列子字段描述文本项格式。 | — | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 参数名 | 类型 | 必填 | 说明 | 默认值 / 示例 |
|---|---|---|---|---|
type | string | 必填 | 内容项类型;纯文本项填写 text。 | |
text | string | 必填 | 该文本项的视频提示词。MiniMax-H3 必须包含一个非空文本项,最长 7000 字符,无需重复填写顶层 prompt。 | — |
width输出宽度;需模型支持。
height输出高度;需模型支持。
fps输出帧率;需模型支持。
seed随机种子;需模型支持。
n生成数量;需模型支持。
response_format响应格式;由具体视频接口决定。
user应用内用户标识;需接口支持。
metadata模型专用扩展参数。Seedance/Veo 使用扁平字段,Wan/HappyHorse 使用 parameters 嵌套字段;各模型的结构不可互换。MiniMax-H3 的 ratio/content 放在顶层。
metadata 的子字段 8
| 参数名 | 类型 | 必填 | 说明 | 默认值 / 示例 | ||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
resolution | string | 可选 | 扁平分辨率字段,例如 Seedance 的 480p、Veo 的 720p;支持范围按模型选择。 | — | ||||||||||||||||||||||||||||||
ratio | string | 可选 | 扁平宽高比字段,例如 Seedance 的 16:9。MiniMax-H3 使用顶层 ratio。 | — | ||||||||||||||||||||||||||||||
duration | integer | 可选 | 扁平时长字段。Seedance 文生视频最低 4 秒,与顶层 duration/seconds 保持一致。 | — | ||||||||||||||||||||||||||||||
generate_audio | boolean | 可选 | Seedance 同步音频开关,false 表示请求不生成同步音频。此字段不用于独立语音合成。 | |||||||||||||||||||||||||||||||
aspectRatio | string | 可选 | Veo 原生宽高比,例如 16:9。 | — | ||||||||||||||||||||||||||||||
durationSeconds | integer | 可选 | Veo 原生时长,例如 4,单位为秒。 | — | ||||||||||||||||||||||||||||||
numberOfVideos | integer | 可选 | Veo 原生视频数量,例如 1。 | — | ||||||||||||||||||||||||||||||
parameters | object | 可选 | Wan 与 HappyHorse 的嵌套参数对象;Wan 同时需要顶层 resolution。 | — | ||||||||||||||||||||||||||||||
| ||||||||||||||||||||||||||||||||||
| 参数名 | 类型 | 必填 | 说明 | 默认值 / 示例 |
|---|---|---|---|---|
resolution | string | 可选 | Wan/HappyHorse 的分辨率选项,例如 480P、720P、1080P。是否采用请求值取决于模型和通道,输出尺寸以实际文件为准。 | — |
ratio | string | 可选 | Wan/HappyHorse 的宽高比,例如 16:9;可选值按模型选择。 | |
duration | integer | 可选 | HappyHorse 的视频时长,整数 3–15 秒,与顶层 duration/seconds 保持一致。 | — |
prompt_extend | boolean | 可选 | Wan 提示词扩写选项,false 表示请求关闭扩写;是否生效取决于模型和通道支持。 | |
audio | boolean | 可选 | Wan 音轨选项,false 表示请求关闭音频;是否生效取决于通道支持,最终音轨以输出文件为准。 |
使用说明
- 各模型的时长、分辨率与音轨选项不同,最终规格以输出文件为准,费用以账户账单为准。
- Seedance/Veo 的扁平 metadata 与 Wan/HappyHorse 的 metadata.parameters 嵌套结构不可互换。
- MiniMax-H3 使用 model、content、resolution、duration;文生视频还需顶层 ratio。提示词只填入 content 的 text 项,无需额外提供 prompt、seconds 或 metadata。
- Doubao-Seedance-2.5 文生视频最低 4 秒。
- 创建受理与生成完成是两个阶段:保存返回的任务 ID,再以低频 GET /v1/video/generations/{task_id} 查询同一任务;任务完成后读取结果地址。
- 请求超时不等于服务端未创建任务;先检查已有任务 ID、提交记录或任务列表,避免重复提交。
创建、轮询与获取结果
创建请求只提交一次,并立即保存响应中的任务 ID。以下示例先等待 3 秒,再按 5、10、30 秒的间隔查询,之后保持 30 秒间隔;轮询预算为 600 秒。这些数值是客户端示例策略,不是平台频率限制或生成时长承诺。超时后保留任务 ID,后续继续查询同一任务。HTTP 错误、失败、取消及未知状态会停止本轮轮询。
| 阶段 | 可能的响应位置 | 处理方式 |
|---|---|---|
| 排队 | status=queued;data.status=NOT_START;output.task_status=PENDING | 继续查询已有任务 |
| 处理中 | data.status=IN_PROGRESS;嵌套 status=running | 继续查询已有任务 |
| 成功 | data.status=SUCCESS;嵌套 status=succeeded / task_status=SUCCEEDED | 读取结果地址并下载 |
| 失败或取消 | FAILURE / failed / cancelled | 停止轮询,读取 fail_reason / error |
| 结果地址 | result_url、content.video_url、output.video_url、task.content.url | 按返回的 HTTPS URL 下载 |
先把所选模型的完整 JSON 请求体保存为 video-request.json。模型的时长、分辨率、content 与 metadata 结构各不相同,按本页对应模型填写。查询成功的 code=success 不等于视频任务成功。
展开 Python:一次创建、有界轮询与保存视频
# 安装依赖:python -m pip install requests
import json
import os
import tempfile
import time
from pathlib import Path
from urllib.parse import quote, urlsplit
import requests
BASE_URL = "https://ai.kedang.net"
def normalize_task(payload):
"""读取任务状态;HTTP 200 或 code=success 仅表示查询成功。"""
if not isinstance(payload, dict):
raise ValueError("任务响应必须为 JSON 对象")
if payload.get("error"):
return {"state": "failed", "url": "", "reason": str(payload["error"])}
code = payload.get("code")
if code is not None and str(code).lower() not in ("0", "200", "success", "ok"):
raise RuntimeError(payload.get("message") or str(code))
nodes, layer = [payload], [payload]
for _ in range(5):
children = []
for node in layer:
for key in ("data", "output", "task", "content"):
if isinstance(node.get(key), dict):
children.append(node[key])
nodes.extend(children)
layer = children
# 外层任务状态优先;未提供时再读取嵌套状态。
raw = next((str(node.get("status") or node.get("task_status")).lower()
for node in nodes if node.get("status") or node.get("task_status")), "")
states = {
"queued": "queued", "pending": "queued", "not_start": "queued", "submitted": "queued",
"in_progress": "running", "running": "running", "processing": "running",
"success": "succeeded", "succeeded": "succeeded", "completed": "succeeded",
"failure": "failed", "failed": "failed", "fail": "failed", "error": "failed", "expired": "failed",
"cancelled": "cancelled", "canceled": "cancelled",
}
url = next((node[key] for node in nodes for key in ("result_url", "video_url", "url")
if isinstance(node.get(key), str) and node[key]), "")
reason = next((str(node[key]) for node in nodes for key in ("fail_reason", "error", "message")
if node.get(key)), "")
return {"state": states.get(raw, "unknown"), "url": url, "reason": reason}
def poll_task(task_id, fetch, sleep, monotonic, total_timeout=600):
"""3 / 5 / 10 / 30 秒间隔与 600 秒预算均为客户端示例策略。"""
if not isinstance(task_id, str) or not task_id:
raise ValueError("任务 ID 不能为空")
if total_timeout <= 0:
raise ValueError("轮询预算必须大于零")
encoded_id = quote(task_id, safe="")
deadline = monotonic() + total_timeout
delays, attempt = (3, 5, 10, 30), 0
while True:
remaining = deadline - monotonic()
if remaining <= 0:
raise TimeoutError("轮询超时,保留此 ID 后续查询:" + task_id)
sleep(min(delays[min(attempt, len(delays) - 1)], remaining))
remaining = deadline - monotonic()
if remaining <= 0:
raise TimeoutError("轮询超时,保留此 ID 后续查询:" + task_id)
payload = fetch(encoded_id, min(30, remaining))
if monotonic() >= deadline:
raise TimeoutError("轮询超时,保留此 ID 后续查询:" + task_id)
task = normalize_task(payload)
if task["state"] == "succeeded":
return payload
if task["state"] in ("failed", "cancelled"):
raise RuntimeError("任务 " + task_id + ":" + task["state"] + " " + task["reason"])
if task["state"] not in ("queued", "running"):
raise RuntimeError("未知任务状态,请按任务 ID 检查响应:" + task_id)
attempt += 1
def download_video(url, target, headers=None):
parsed = urlsplit(url)
if parsed.scheme != "https" or not parsed.netloc:
raise ValueError("下载地址必须为有效 HTTPS URL")
target = Path(target)
temporary = None
try:
with requests.get(url, headers=headers or {}, stream=True, timeout=(10, 120)) as response:
response.raise_for_status()
mime = response.headers.get("Content-Type", "").split(";", 1)[0].lower()
if mime and not (mime.startswith("video/") or mime == "application/octet-stream"):
raise ValueError("下载响应不是视频或二进制文件")
with tempfile.NamedTemporaryFile(dir=target.parent, delete=False) as file:
temporary = Path(file.name)
size = 0
for chunk in response.iter_content(chunk_size=1024 * 1024):
if chunk:
file.write(chunk)
size += len(chunk)
if size == 0:
raise ValueError("下载内容为空")
temporary.replace(target)
finally:
if temporary is not None:
temporary.unlink(missing_ok=True)
return target
def main():
headers = {"Authorization": "Bearer " + os.environ["KEDANG_API_KEY"]}
# 将所选模型的完整创建请求保存到此 JSON 文件,包含它要求的规格与扩展参数。
body = json.loads(Path("video-request.json").read_text(encoding="utf-8"))
if not body.get("model") or body["model"] == "MODEL_ID":
raise ValueError("请在 video-request.json 中填写账户可用的模型与完整请求参数")
# 创建只提交一次。网络超时时先核对任务记录,避免重复创建。
created_response = requests.post(BASE_URL + "/v1/video/generations", headers=headers,
json=body, timeout=(10, 120))
created_response.raise_for_status()
created = created_response.json()
if created.get("error"):
raise RuntimeError(created["error"])
data = created.get("data") if isinstance(created.get("data"), dict) else {}
task_id = created.get("task_id") or created.get("id") or data.get("task_id")
if not isinstance(task_id, str) or not task_id:
raise RuntimeError("响应没有任务 ID;保留提交记录,不自动重新创建。")
Path("video-task-id.txt").write_text(task_id, encoding="utf-8")
print("任务 ID:", task_id)
def fetch(encoded_id, timeout):
response = requests.get(BASE_URL + "/v1/video/generations/" + encoded_id,
headers=headers, timeout=timeout)
response.raise_for_status()
return response.json()
result = poll_task(task_id, fetch, time.sleep, time.monotonic, total_timeout=600)
url = normalize_task(result)["url"]
if not url:
raise RuntimeError("任务已成功但缺少结果地址;使用保存的任务 ID 查询。")
# 返回的媒体 URL 可能在独立域名,下载时不发送平台 Authorization。
print(download_video(url, "video.mp4"))
if __name__ == "__main__":
main()