Permalink to 视频视频

POST /v1/videos 提交一段视频,返回 202 Accepted 加一个 Job 信封。 视频是这套 API 上耗时最长的东西——以分钟计而不是秒——所以异步在这里不是不便,而是唯一可行的形态。

Permalink to 提交提交

bash
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"
  }'
json
{
  "id": "job_2f81c0a4d3b57e9016ab24cf",
  "status": "queued",
  "kind": "video",
  "model": "bytedance/seedance-2",
  "progress": 0,
  "created_at": 1787824589,
  "dispatch_deadline_at": 1787825189
}

Permalink to 请求体请求体

字段类型说明
modelstring必填
promptstring必填,除非提供了参考图、首帧、参考视频/音频或 ref_video_url
secondsnumber时长。duration 是同义字段
resolutionstring480p720p1080p4k,以模型公布的为准
sizestring"1920x1080"resolution 的替代写法
aspect_ratiostring"16:9"ratio 是同义字段
fpsinteger不能为负
seedinteger
negative_promptstring
camera_fixedboolean
watermarkboolean
input_reference参考素材单张参考图,不是首帧
reference_image_urls参考素材数组参考图列表,支持的模型上生效;与首尾帧语义独立
image_url参考素材单张首帧图的兼容别名;多张参考图请用 reference_image_urls
first_frame参考素材首帧图。image 是同义字段
last_frame参考素材尾帧,支持的模型上生效
reference_videosstring[]参考视频 URL 数组,支持多参模型的模型上生效
reference_audiosstring[]参考音频 URL 数组,支持多参模型的模型上生效
generate_audioboolean是否生成原生音频;仅支持该能力的模型生效
ref_video_urlstring源视频,支持视频生视频的模型上生效;video_url 是兼容别名
extraobject厂商透传

input_reference / reference_image_urls 始终表示参考图;image_urlfirst_frame / imagelast_frame 始终表示帧。它们不会互相转换; 模型不支持对应语义时返回 400。reference_videos / reference_audios 只接受 URL,建议先通过 POST /v1/files 上传较大的素材,再把返回的 URL 放进数组。

请求体上限 24 MiB——因为图像可能以 base64 data: URL 的形式内嵌在里面。

Permalink to 参考素材参考素材

input_referencereference_image_urlsimage_urlfirst_frameimagelast_frame 各接受三种写法:

json
{ "input_reference": "https://example.com/reference.png" }
json
{ "first_frame": "data:image/png;base64,iVBORw0KGgo…" }
json
{ "input_reference": { "url": "https://example.com/frame.png" } }
json
{ "input_reference": { "b64": "iVBORw0KGgo…", "mime_type": "image/png" } }

裸 base64 字符串在解码后能被识别为图片时也接受——有几家 SDK 就是这么传原始字节的。其余情况返回 400 invalid_input_reference

每个 http(s) URL 都走受保护的抓取器,拒绝内网与链路本地地址,并逐跳复核重定向。

表中没有列出的模型专属字段也会保留并透传(可以直接放在顶层,或放在 extra 对象里),例如 cfg_scalecamera_controloutput_format。媒体 URL 仍会经过同样的安全校验;模型不支持的字段由对应适配器返回明确的 400。

Permalink to 计价形态计价形态

视频按每秒产出计价,再乘以分辨率比率。请从模型卡片pricing.credits.per_secondpricing.resolution_ratio;同一模型下,一秒 1080p 常常是一秒 480p 的好几倍。部分模型还有 flag_ratio,用来给请求字段表达不了的维度计价——比如默认会生成一条 音轨、或者带了参考视频——这也是为什么提交后的估算可能高于「per_second × seconds」的直觉值。

Permalink to 取成片取成片

状态查 GET /v1/jobs/{job_id},签名 MP4 链接查 GET /v1/jobs/{job_id}/assets。状态接口保留一个可读别名:

bash
# 与 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 完整流程完整流程

python
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)
js
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含义
400missing_modelmodel 必填
400missing_promptprompt、参考图、首帧、ref_video_url 一个都没提供
400invalid_secondsseconds 不能为负
400invalid_fpsfps 不能为负
400invalid_input_reference参考素材不是 URL、data: URL 或 base64 图片
400invalid_json请求体不是合法 JSON
400invalid_media_url / unsupported_media_url_schemeURL 未通过受保护抓取策略
413body_too_large请求体超过 24 MiB
404model_not_found没有启用的上游为这个模型提供视频能力