POST /v1/videos 提交一段视频,返回 202 Accepted 加一个 Job 信封。
视频是这套 API 上耗时最长的东西——以分钟计而不是秒——所以异步在这里不是不便,而是唯一可行的形态。
Permalink to 提交提交
curl https://hypit.ai/v1/videos \
-H "Authorization: Bearer $HYPIT_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "bytedance/seedance-2",
"prompt": "一只纸船顺着雨水沟漂走,镜头跟拍",
"seconds": 5,
"resolution": "720p",
"aspect_ratio": "16:9"
}'{
"id": "job_2f81c0a4d3b57e9016ab24cf",
"status": "queued",
"kind": "video",
"model": "bytedance/seedance-2",
"progress": 0,
"created_at": 1787824589,
"dispatch_deadline_at": 1787825189
}Permalink to 请求体请求体
| 字段 | 类型 | 说明 |
|---|---|---|
model | string | 必填 |
prompt | string | 必填,除非提供了参考图、首帧、参考视频/音频或 ref_video_url |
seconds | number | 时长。duration 是同义字段 |
resolution | string | 480p、720p、1080p、4k,以模型公布的为准 |
size | string | 如 "1920x1080",resolution 的替代写法 |
aspect_ratio | string | 如 "16:9"。ratio 是同义字段 |
fps | integer | 不能为负 |
seed | integer | |
negative_prompt | string | |
camera_fixed | boolean | |
watermark | boolean | |
input_reference | 参考素材 | 单张参考图,不是首帧 |
reference_image_urls | 参考素材数组 | 参考图列表,支持的模型上生效;与首尾帧语义独立 |
image_url | 参考素材 | 单张首帧图的兼容别名;多张参考图请用 reference_image_urls |
first_frame | 参考素材 | 首帧图。image 是同义字段 |
last_frame | 参考素材 | 尾帧,支持的模型上生效 |
reference_videos | string[] | 参考视频 URL 数组,支持多参模型的模型上生效 |
reference_audios | string[] | 参考音频 URL 数组,支持多参模型的模型上生效 |
generate_audio | boolean | 是否生成原生音频;仅支持该能力的模型生效 |
ref_video_url | string | 源视频,支持视频生视频的模型上生效;video_url 是兼容别名 |
extra | object | 厂商透传 |
input_reference / reference_image_urls 始终表示参考图;image_url、first_frame /
image 和 last_frame 始终表示帧。它们不会互相转换;
模型不支持对应语义时返回 400。reference_videos / reference_audios 只接受 URL,建议先通过
POST /v1/files 上传较大的素材,再把返回的 URL 放进数组。
请求体上限 24 MiB——因为图像可能以 base64 data: URL 的形式内嵌在里面。
Permalink to 参考素材参考素材
input_reference、reference_image_urls、image_url、first_frame、image 和 last_frame
各接受三种写法:
{ "input_reference": "https://example.com/reference.png" }{ "first_frame": "data:image/png;base64,iVBORw0KGgo…" }{ "input_reference": { "url": "https://example.com/frame.png" } }{ "input_reference": { "b64": "iVBORw0KGgo…", "mime_type": "image/png" } }裸 base64 字符串在解码后能被识别为图片时也接受——有几家 SDK 就是这么传原始字节的。其余情况返回
400 invalid_input_reference。
每个 http(s) URL 都走受保护的抓取器,拒绝内网与链路本地地址,并逐跳复核重定向。
表中没有列出的模型专属字段也会保留并透传(可以直接放在顶层,或放在 extra 对象里),例如
cfg_scale、camera_control、output_format。媒体 URL 仍会经过同样的安全校验;模型不支持的字段由对应适配器返回明确的 400。
Permalink to 计价形态计价形态
视频按每秒产出计价,再乘以分辨率比率。请从模型卡片读
pricing.credits.per_second 和 pricing.resolution_ratio;同一模型下,一秒 1080p 常常是一秒
480p 的好几倍。部分模型还有 flag_ratio,用来给请求字段表达不了的维度计价——比如默认会生成一条
音轨、或者带了参考视频——这也是为什么提交后的估算可能高于「per_second × seconds」的直觉值。
Permalink to 取成片取成片
状态查 GET /v1/jobs/{job_id},签名 MP4 链接查 GET /v1/jobs/{job_id}/assets。状态接口保留一个可读别名:
# 与 GET /v1/jobs/{job_id} 是同一个 handler
curl https://hypit.ai/v1/videos/job_2f81c0a4d3b57e9016ab24cf \
-H "Authorization: Bearer $HYPIT_API_KEY"
# 列出资产,再下载上面返回的 `items[0].url`(下载时不要带 API key)
curl https://hypit.ai/v1/jobs/job_2f81c0a4d3b57e9016ab24cf/assets \
-H "Authorization: Bearer $HYPIT_API_KEY"签名 URL 有效期 15 分钟,支持 HTTP Range 请求,可以直接交给 <video> 元素。需要新链接就再调一次
assets 路由;产物保留 30 天。
Permalink to 完整流程完整流程
import os, time, httpx
BASE = os.environ.get("HYPIT_BASE_URL", "https://hypit.ai/v1")
AUTH = {"Authorization": f"Bearer {os.environ['HYPIT_API_KEY']}"}
created = httpx.post(
f"{BASE}/videos",
headers={**AUTH, "Idempotency-Key": "demo-clip-0001"},
json={
"model": "bytedance/seedance-2",
"prompt": "一只纸船顺着雨水沟漂走",
"seconds": 5,
"resolution": "720p",
},
timeout=60,
)
created.raise_for_status()
job_id = created.json()["id"]
print("job", job_id)
delay = 5.0
while True:
job = httpx.get(f"{BASE}/jobs/{job_id}", headers=AUTH, timeout=30).json()
print(job["status"], job["progress"])
if job["status"] in {"succeeded", "failed", "queue_expired"}:
break
time.sleep(delay)
delay = min(delay * 1.3, 15.0)
if job["status"] != "succeeded":
raise RuntimeError(f"{job['status']}: {job.get('error_code')} {job.get('error')}")
asset = httpx.get(f"{BASE}/jobs/{job_id}/assets", headers=AUTH, timeout=30).json()["items"][0]
with httpx.stream("GET", asset["url"], follow_redirects=True, timeout=None) as src:
src.raise_for_status()
with open("clip.mp4", "wb") as f:
for chunk in src.iter_bytes():
f.write(chunk)import { writeFile } from "node:fs/promises";
const BASE = process.env.HYPIT_BASE_URL ?? "https://hypit.ai/v1";
const AUTH = { Authorization: `Bearer ${process.env.HYPIT_API_KEY}` };
const TERMINAL = new Set(["succeeded", "failed", "queue_expired"]);
const created = await fetch(`${BASE}/videos`, {
method: "POST",
headers: { ...AUTH, "Content-Type": "application/json", "Idempotency-Key": "demo-clip-0001" },
body: JSON.stringify({
model: "bytedance/seedance-2",
prompt: "一只纸船顺着雨水沟漂走",
seconds: 5,
resolution: "720p",
}),
});
if (!created.ok) throw new Error(`${created.status} ${await created.text()}`);
const { id } = await created.json();
let job, delay = 5_000;
for (;;) {
job = await (await fetch(`${BASE}/jobs/${id}`, { headers: AUTH })).json();
console.log(job.status, job.progress);
if (TERMINAL.has(job.status)) break;
await new Promise((r) => setTimeout(r, delay));
delay = Math.min(delay * 1.3, 15_000);
}
if (job.status !== "succeeded") throw new Error(`${job.status}: ${job.error_code} ${job.error}`);
const { items } = await (await fetch(`${BASE}/jobs/${id}/assets`, { headers: AUTH })).json();
await writeFile("clip.mp4", Buffer.from(await (await fetch(items[0].url)).arrayBuffer()));Permalink to 这条路由特有的错误这条路由特有的错误
| 状态 | code | 含义 |
|---|---|---|
400 | missing_model | model 必填 |
400 | missing_prompt | prompt、参考图、首帧、ref_video_url 一个都没提供 |
400 | invalid_seconds | seconds 不能为负 |
400 | invalid_fps | fps 不能为负 |
400 | invalid_input_reference | 参考素材不是 URL、data: URL 或 base64 图片 |
400 | invalid_json | 请求体不是合法 JSON |
400 | invalid_media_url / unsupported_media_url_scheme | URL 未通过受保护抓取策略 |
413 | body_too_large | 请求体超过 24 MiB |
404 | model_not_found | 没有启用的上游为这个模型提供视频能力 |