# 通用 API · VidBudget API

POST https://vidbudget.com/v1/videos/generations

视频保留 7 天，请及时下载。

## 一套 API，接入全部创作流程

Base URL: https://vidbudget.com。REST / JSON，异步任务。供应商由服务端适配，应用只调用 VidBudget 模型 ID。

流程：上传本地素材 → 查询上传任务取得 URL → 提交生成 → 查询生成任务 → 下载结果。已有可公开下载的 HTTPS 素材可直接提交。

GET /v1/models 的 available、availability、capabilities 是当前可用规格；后台渠道变化无需修改你的上传或查询代码。

| 方法 | 路径 | 作用 |
| --- | --- | --- |
| GET | /v1/models | 模型、价格、可用状态、素材上限 |
| GET | /v1/balance | 余额与积分 |
| POST | /v1/uploads | 上传一份图片、视频或音频 |
| POST | /v1/uploads/presign | 获取大文件直传签名 |
| POST | /v1/uploads/{id}/complete | 确认直传并创建处理任务 |
| POST | /v1/videos/generations | 创建视频生成任务 |
| GET | /v1/tasks/{id} | 查询上传或生成任务 |
| GET | /v1/tasks | 最近 100 个任务 |
| GET | /v1/videos/jobs/{id} | 生成任务查询兼容路径 |

## 认证、幂等与余额

在 /account/keys 创建自己的 VidBudget API Key。供应商密钥由平台管理，不需要提供给调用者。

认证：Authorization: Bearer YOUR_VIDBUDGET_API_KEY。JSON 请求使用 Content-Type: application/json；文件上传由客户端自动生成 multipart boundary。

每次新的生成使用唯一 Idempotency-Key。同一任务断网重发时，保持幂等键及请求参数完全相同；服务返回原任务，不重复扣费。不要在超时后换新键重购。

普通账户提交时预扣，成功结算，最终失败退回预扣。无可用通道不扣费；请先确认余额。

GET /v1/balance 返回 billing_mode：wallet 为普通余额计费，admin 为服务器确认的管理员调用。管理员通过创作台或本人 API Key 生成时不扣账户余额，上游费用仍由平台承担，每次调用记录审计。请求参数不能指定或覆盖计费身份。

## 模型和参数范围

图片、视频、音频数量分别计算。请从 capabilities 读取当前 durations、ratios、resolutions、images、videos、audios。

有参考视频时还可能受参考视频时长与输出时长合计限制，见 capabilities.video_and_output_seconds。

上传组件允许一次批量选择 30 张图片；单次生成可接受的数量取决于所选模型的当前规格。

| 模型 ID | 名称 | 可接单 | 参考图 / 视频 / 音频 |
| --- | --- | --- | --- |
| seedance-2.5 | Seedance 2.5 | 维护中 | 30 / 10 / 10 |
| seedance-2.5-30s | Seedance 2.5 Special | 是 | 30 / 10 / 10 |
| seedance-2.0 | Seedance 2.0 | 维护中 | 9 / 3 / 0 |
| seedance-2.0-fast | Seedance 2.0 Fast | 维护中 | 9 / 3 / 3 |

## 本地文件与批量上传

POST /v1/uploads，multipart/form-data，字段名 file。每个请求一个文件，普通上传最多 90000000 字节。支持 PNG/JPEG/WebP/GIF、MP4/WebM、MP3/WAV/M4A，服务端验证实际文件。

返回 HTTP 202 和上传任务 id；查询 GET /v1/tasks/{id}，只有 succeeded 时才能取 data[0].url。不要把上传任务 ID 直接填入生成素材数组。

批量上传建议 6 路并发，按原文件顺序收集结果。每个 API Key 每小时当前上限 150 次。

超过普通上限时调用 presign（content_type 和 size），PUT 原始字节到 upload_url，最后调用 complete；最大 1000000000 字节，签名有效 15 分钟。签名 PUT 只带返回的 headers，不带 VidBudget Key。

### cURL · 上传音频

```
curl https://vidbudget.com/v1/uploads \
  -H "Authorization: Bearer $VIDBUDGET_API_KEY" \
  -F "file=@reference.wav"
```

### JSON · 大文件签名

```
POST /v1/uploads/presign
{"content_type":"video/mp4","size":100000000}

PUT <upload_url>
POST /v1/uploads/<upload_id>/complete
GET /v1/tasks/<returned_task_id>
```

## 全参数生成请求

提示词用 @Image1、@Video1、@Audio1 引用每种素材；编号对应各数组的添加顺序。服务端负责转换供应商要求的名称。

duration 为整数秒，resolution 如 720P，aspect_ratio 如 16:9。素材 URL 必须为 HTTPS，不能使用本地路径或 data: URI。

规范字段为 images、video_references、audio_references。也接受 videos / audios 别名，同一素材类型只传一个字段。

audio / generate_audio 是成片音轨开关，不是参考音频。按条特价模型不接受此开关；传音频素材请使用 audio_references。

### JSON · 图片、视频、音频共同参考

```
{
  "model": "seedance-2.5-30s",
  "prompt": "以 @Image1 的主体为准，参考 @Video1 的镜头运动和 @Audio1 的节奏。",
  "duration": 5,
  "resolution": "720P",
  "aspect_ratio": "16:9",
  "images": ["https://media.vidbudget.com/inputs/IMAGE.png"],
  "video_references": ["https://media.vidbudget.com/inputs/VIDEO.mp4"],
  "audio_references": ["https://media.vidbudget.com/inputs/AUDIO.wav"]
}
```

## Python · 上传、生成、查询、下载

安装 requests。将自己的密钥设为 VIDBUDGET_API_KEY，准备 references/ 中的图片、camera.mp4 和 rhythm.wav 后运行。示例会产生真实生成费用。

示例遇到 HTTP 错误会停止；保留已返回的任务 ID，网络恢复后查询原任务，不要把整个脚本当作自动重试。

### Python

```
import os, time, uuid, mimetypes
from pathlib import Path
from concurrent.futures import ThreadPoolExecutor
import requests

BASE = "https://vidbudget.com"
HEADERS = {"Authorization": "Bearer " + os.environ["VIDBUDGET_API_KEY"]}

def wait(job):
    deadline = time.monotonic() + 7200
    while job["status"] not in ("succeeded", "failed"):
        if time.monotonic() > deadline:
            raise TimeoutError("Continue querying the original task: " + job["id"])
        time.sleep(3)
        response = requests.get(BASE + "/v1/tasks/" + job["id"], headers=HEADERS, timeout=85)
        response.raise_for_status()
        job = response.json()
    if job["status"] == "failed":
        raise RuntimeError(job["error"])
    return job["data"][0]["url"]

def upload(path):
    path = Path(path)
    mime = mimetypes.guess_type(path.name)[0] or "application/octet-stream"
    if path.suffix.lower() == ".m4a": mime = "audio/mp4"
    with path.open("rb") as file:
        response = requests.post(BASE + "/v1/uploads", headers=HEADERS,
            files={"file": (path.name, file, mime)}, timeout=85)
    response.raise_for_status()
    return wait(response.json())

# executor.map preserves the original file order. Up to 30 images for the full-parameter tier.
files = sorted(Path("references").glob("*.png"))
with ThreadPoolExecutor(max_workers=6) as pool:
    images = list(pool.map(upload, files))
video = upload("camera.mp4")
audio = upload("rhythm.wav")
request_id = str(uuid.uuid4())  # Persist this ID and body before submitting.
body = {"model": "seedance-2.5-30s", "prompt": "参考 @Image1、@Video1 和 @Audio1 生成镜头",
        "duration": 5, "resolution": "720P", "aspect_ratio": "16:9",
        "images": images, "video_references": [video], "audio_references": [audio]}
response = requests.post(BASE + "/v1/videos/generations",
    headers={**HEADERS, "Idempotency-Key": request_id}, json=body, timeout=85)
response.raise_for_status()
job = response.json()
print("Save task ID:", job["id"])
url = wait(job)
with requests.get(url, stream=True, timeout=85) as result:
    result.raise_for_status()
    with open("video.mp4", "wb") as file:
        for chunk in result.iter_content(1024 * 1024): file.write(chunk)

```

## 任务状态和结果

所有任务共有 id、kind、model、status、created_at、completed_at、elapsed_seconds、retry_count、error、data。初始状态 queued，处理中 processing，完成 succeeded，最终失败 failed。

succeeded 才能读取 data[0].url；视频结果来自 media.vidbudget.com。elapsed_seconds 包含排队、素材处理、生成和结果转存。

浏览器关闭不取消后台任务；GET /v1/tasks 获取自己的任务记录。仅能读取当前用户的任务。素材和成片请及时下载，不作为永久存储。

当前使用轮询，不提供生成 Webhook 或流式视频接口。

## 错误与处理

HTTP 错误格式：{"error":{"code":"..."}}。最终任务失败时 error 为错误码字符串。

客户端每 3–5 秒查询即可。提交响应不确定时，复用原 Idempotency-Key 与完全相同的参数。

| HTTP | 含义 |
| --- | --- |
| 400/422 | 参数或类型不符合要求 |
| 401/403 | 密钥或登录校验失败 |
| 402 | 余额不足 |
| 404 | 任务不存在或不属于当前用户 |
| 409 | 模型关闭或幂等键参数冲突 |
| 413 | 文件过大，改用预签名直传 |
| 415 | 文件 MIME 不支持 |
| 429 | 上传额度达到每小时上限 |
| 503 | 当前参数没有可用渠道 |
| 504 | 请求超时，查询或复用原请求，避免重购 |


---

# Seedance 2.5 · VidBudget API

POST https://vidbudget.com/v1/videos/generations

视频保留 7 天，请及时下载。

## 快速开始：提交 → 查询 → 下载

这是异步视频接口。提交成功返回 HTTP 202 和任务 ID，视频生成及素材处理在后台完成；不要等待提交请求直接返回视频。

保存任务 ID，每 5–10 秒查询一次。请求中断后继续查询原任务；需要重发提交时使用同一个幂等键和完全相同的参数。

### cURL · 提交

```
curl https://vidbudget.com/v1/videos/generations \
  -H "Authorization: Bearer $VIDBUDGET_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: YOUR_UNIQUE_REQUEST_ID" \
  -d '{
  "model": "seedance-2.5",
  "prompt": "湖面泛起微光，镜头缓缓向前推进",
  "duration": 5,
  "resolution": "720P",
  "aspect_ratio": "16:9"
}'
```

### cURL · 查询

```
curl https://vidbudget.com/v1/videos/jobs/TASK_ID -H "Authorization: Bearer $VIDBUDGET_API_KEY"
```

### Python · 完整轮询

```
import os, time, uuid, requests

BASE = "https://vidbudget.com"
headers = {"Authorization": "Bearer " + os.environ["VIDBUDGET_API_KEY"]}
response = requests.post(BASE + "/v1/videos/generations", headers={**headers,
    "Idempotency-Key": str(uuid.uuid4())}, json={'model': 'seedance-2.5', 'prompt': '湖面泛起微光，镜头缓缓向前推进', 'duration': 5, 'resolution': '720P', 'aspect_ratio': '16:9'}, timeout=85)
response.raise_for_status()
job = response.json()
print("保存任务 ID，断线后继续查询：", job["id"])
deadline = time.monotonic() + 7200
while job["status"] not in ("succeeded", "failed") and time.monotonic() < deadline:
    time.sleep(5)
    response = requests.get(BASE + "/v1/videos/jobs/" + job["id"], headers=headers, timeout=85)
    response.raise_for_status()
    job = response.json()
if job["status"] == "succeeded":
    url = job["data"][0]["url"]
    if not url.startswith("https://media.vidbudget.com/videos/"):
        raise RuntimeError("Unexpected media URL")
    with requests.get(url, stream=True, timeout=85) as media:
        media.raise_for_status()
        with open("video.mp4", "wb") as output:
            for chunk in media.iter_content(1024 * 1024):
                output.write(chunk)
elif job["status"] == "failed":
    print("失败，预扣已退回：", job["error"])
else:
    print("尚未完成，请继续查询原任务：", job["id"])

```

### Node.js · 完整轮询

```
// Node.js 18+; save the task ID before polling.
const base = "https://vidbudget.com";
const headers = {Authorization: `Bearer ${process.env.VIDBUDGET_API_KEY}`};
async function request(path, options = {}) {
  const response = await fetch(base + path, {...options, signal: AbortSignal.timeout(85000)});
  const data = await response.json();
  if (!response.ok) throw new Error(JSON.stringify(data.error));
  return data;
}
let job = await request('/v1/videos/generations', {method: 'POST', headers: {...headers,
  'Content-Type': 'application/json', 'Idempotency-Key': crypto.randomUUID()},
  body: JSON.stringify({"model": "seedance-2.5", "prompt": "湖面泛起微光，镜头缓缓向前推进", "duration": 5, "resolution": "720P", "aspect_ratio": "16:9"})});
console.log('Task ID:', job.id);
const deadline = Date.now() + 7200000;
while (!['succeeded', 'failed'].includes(job.status) && Date.now() < deadline) {
  await new Promise(resolve => setTimeout(resolve, 5000));
  job = await request('/v1/videos/jobs/' + job.id, {headers});
}
console.log(job.status === 'succeeded' ? job.data[0].url : job);

```

## 认证与请求头

在控制台创建 API 密钥。密钥仅创建时显示一次，不放入浏览器公开代码、URL 或日志。

### HTTP

```
Authorization: Bearer YOUR_API_KEY
Content-Type: application/json
Idempotency-Key: YOUR_UNIQUE_REQUEST_ID
```

## 规格与适用范围

参考图片用来描述人物、场景、商品和风格，不是指定首帧或尾帧。请确保拥有输入素材及人物肖像的使用权。

模型规格和可接单的通道组合不同；无可用配置时返回 503，不能仅凭模型出现在列表中认定可以生成。

| 项目 | 支持范围 |
| --- | --- |
| 时长 | 4 / 5 / 6 / 7 / 8 / 9 / 10 / 11 / 12 / 13 / 14 / 15 / 16 / 17 / 18 / 19 / 20 / 21 / 22 / 23 / 24 / 25 / 26 / 27 / 28 / 29 / 30 秒 |
| 分辨率 | 480P / 720P / 1080P |
| 画幅 | 16:9 / 9:16 / 1:1 / 4:3 / 3:4 |
| 参考图片 | 30 张以内 |
| 参考视频 | 10 段以内 |
| 参考音频 | 10 段以内 |
| 提示词 | 最多 11000 字符 / 33000 UTF-8 字节 |
| 计费 | 按生成秒数 |
| 视频保留 | 7 天，请及时下载 |

## 请求参数

Content-Type 为 application/json。不要把未支持的参数当作生效设置：当前不提供 callback_url、n 多条生成、指定首尾帧或流式输出。

| 参数 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- |
| model | string | 是 | seedance-2.5 |
| prompt | string | 是 | 视频描述；素材按 @Image1 / @Video1 / @Audio1 引用，序号对应各数组顺序。 |
| duration | integer | 否 | 默认 5 秒；范围见规格。Clip 竖屏必须显式指定 15 秒。 |
| resolution | string | 否 | 默认 720P；可选 480P, 720P, 1080P |
| aspect_ratio | string | 否 | 默认 16:9；别名 size、ratio；不能互相冲突。 |
| images | array<string> | 否 | HTTPS 图片 URL 数组；别名 reference_images、image_url、image，同一类型只传一个字段。 |
| video_references | array<string> | 否 | HTTPS 视频 URL 数组；本模型不支持时不要传。别名 video_reference、video_url。 |
| audio_references | array<string> | 否 | HTTPS 音频 URL 数组；本模型不支持时不要传。别名 audio_reference、audio_url。 |
| audio | boolean | 否 | 按条产品禁止传此参数；其他产品默认 true。false 不保证能关闭成片音轨，取决于模型本身。别名 generate_audio。 |
| seconds | integer | 否 | duration 的别名；同时传时必须相同。 |
| image_role | string | 否 | 只接受 reference / reference_images / ref；不支持首尾帧。 |

## 价格与扣费

下面读取当前价格配置，与实际扣费函数使用同一来源。提交预扣与任务入库在同一事务完成；成功结算，最终失败退回预扣金额。

| 分辨率 | 单价 USD | 示例费用 |
| --- | --- | --- |
| 480P | 0.1 / 秒 | 5 秒：$0.50 |
| 720P | 0.158 / 秒 | 5 秒：$0.79 |
| 1080P | 0.33 / 秒 | 5 秒：$1.65 |

## 参考素材与示例

先上传素材并查询上传任务，拿到 media.vidbudget.com 链接后填入相应数组；也可使用可公开下载的 HTTPS 素材 URL。不接受 base64 或 data: URI。

图片可用于角色与风格参考，视频可用于动作和运镜参考，音频可用于声音节奏参考。是否支持各类型及数量以本模型规格为准。

素材下载与验证在后台进行；无法读取、格式不合法或违反内容规则可能导致任务失败。失败时退回预扣余额。

真人照片可能受到生成服务的内容限制；不能保证每次接受或每次输出符合主观预期。

视频保留 7 天，请及时下载。上传素材也保留 7 天，不要把临时链接用作永久存储。

### JSON · 图片参考

```
{
  "model": "seedance-2.5",
  "prompt": "湖面泛起微光，镜头缓缓向前推进",
  "duration": 5,
  "resolution": "720P",
  "aspect_ratio": "16:9",
  "images": [
    "https://media.vidbudget.com/inputs/YOUR_IMAGE_UUID.png"
  ]
}
```

## 响应结构

提交响应同样使用此结构，但 status 为 queued，completed_at 为 null、data 为空数组。error 在失败时是错误码字符串，成功或进行中为 null。

### JSON · 查询完成

```
{
  "id": "TASK_UUID",
  "object": "task",
  "kind": "video",
  "model": "seedance-2.5",
  "status": "succeeded",
  "created_at": 1793354400,
  "completed_at": 1793354700,
  "elapsed_seconds": 300,
  "retry_count": 0,
  "error": null,
  "data": [
    {
      "url": "https://media.vidbudget.com/videos/VIDEO_UUID.mp4"
    }
  ]
}
```

## 任务状态与重试

retry_count 只记录额外提交次数，初次为 0，最多额外重试 2 次；查询次数不计入。completed_at 仅在最终成功或失败时写入，耗时包含排队、重试和媒体转存。

同通道自动重试仅针对可确认的可重试失败；提交结果未知时不会盲目重复购买。最终失败只退款一次，每笔余额变化有流水。

| 状态 | 含义 |
| --- | --- |
| queued | 已入库等待后台处理 |
| processing | 素材处理、生成、轮询或转存进行中 |
| succeeded | 已完成，读取 data[0].url |
| failed | 最终失败，读取 error；预扣自动退回 |

## 生成耗时与超时

视频生成可能需要数分钟或更久，时长、分辨率和排队都会影响完成时间。新站尚无真实生成耗时统计，不把原站实测值当作本服务承诺。

HTTP 请求时限为 85 秒，超时返回 JSON 504；生成总任务时限当前为 7200 秒。浏览器关闭不会取消后台任务。

503 表示当前没有匹配的已配置通道；先修复配置或等待服务可用，不要持续重复提交。

## 错误与处理方式

HTTP 错误结构为 {"error":{"code":"..."}}，message 不保证存在。任务失败则通过任务对象的 error 字符串读取。

| HTTP / 任务 | 错误码 | 处理 |
| --- | --- | --- |
| 400 | invalid_request | 检查参数、类型、素材字段和模型约束 |
| 401 | invalid_api_key / login_required | 检查密钥或登录状态 |
| 402 | insufficient_balance | 充值后再提交 |
| 404 | task_not_found | 确认任务属于当前用户，ID 正确 |
| 409 | conflict | 模型关闭或幂等键参数冲突 |
| 413 | payload_too_large | 大文件使用预签名直传 |
| 415 | unsupported_media_type | 仅上传图片/视频/音频 |
| 429 | upload_rate_limit | 等待窗口恢复 |
| 503 | channel_not_configured_or_unavailable | 没有可用通道，稍后重试 |
| 504 | request_timeout | 查询原任务，避免重复购买 |
| 任务 | media_processing_failed | 检查素材能否读取及实际文件格式 |
| 任务 | generation_timeout | 任务达到总时限，预扣退回 |
| 任务 | generation_failed 等安全错误码 | 检查内容与参数，再决定是否新建任务 |

## 素材上传与大文件直传

每 API key 每小时默认限制当前为 150 次。普通上传上限为 90000000 字节；超过此值使用预签名 PUT。直传上限 1000000000 字节，签名有效期 15 分钟。

支持图片、视频、音频 MIME，并在后台解码验证实际内容。推荐 PNG/JPEG/WebP，MP4，MP3/WAV/M4A。批量上传时每个文件单独请求，建议最多 6 路并发，保留添加顺序作为素材编号。

PUT 到 upload_url 时只带返回的上传 headers，不发送 VidBudget API key。上传完成后调用 complete，再查询任务取得最终素材 URL。

### cURL · 普通上传

```
curl https://vidbudget.com/v1/uploads -H "Authorization: Bearer $VIDBUDGET_API_KEY" -F "file=@reference.png"
```

### HTTP · 大文件流程

```
POST /v1/uploads/presign
{"content_type":"video/mp4","size":100000000}

PUT <upload_url>  （原始文件字节，使用返回的 headers）
POST /v1/uploads/<upload_id>/complete
GET /v1/tasks/<task_id>
```

## Webhook 与兼容性

当前 VidBudget 未实现生成结果 Webhook，不要传 callback_url 并期待通知。请使用 GET /v1/videos/jobs/{id} 或 GET /v1/tasks/{id} 轮询。

原版同步上传、其他支付方式、其他模型及旧站特有响应字段不适用于本接口。账户接口：GET /v1/balance、GET /v1/tasks、GET /v1/models。


---

# Seedance 2.5 Special · VidBudget API

POST https://vidbudget.com/v1/videos/generations

视频保留 7 天，请及时下载。

## 快速开始：提交 → 查询 → 下载

这是异步视频接口。提交成功返回 HTTP 202 和任务 ID，视频生成及素材处理在后台完成；不要等待提交请求直接返回视频。

保存任务 ID，每 5–10 秒查询一次。请求中断后继续查询原任务；需要重发提交时使用同一个幂等键和完全相同的参数。

### cURL · 提交

```
curl https://vidbudget.com/v1/videos/generations \
  -H "Authorization: Bearer $VIDBUDGET_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: YOUR_UNIQUE_REQUEST_ID" \
  -d '{
  "model": "seedance-2.5-30s",
  "prompt": "湖面泛起微光，镜头缓缓向前推进",
  "duration": 30,
  "resolution": "720P",
  "aspect_ratio": "16:9"
}'
```

### cURL · 查询

```
curl https://vidbudget.com/v1/videos/jobs/TASK_ID -H "Authorization: Bearer $VIDBUDGET_API_KEY"
```

### Python · 完整轮询

```
import os, time, uuid, requests

BASE = "https://vidbudget.com"
headers = {"Authorization": "Bearer " + os.environ["VIDBUDGET_API_KEY"]}
response = requests.post(BASE + "/v1/videos/generations", headers={**headers,
    "Idempotency-Key": str(uuid.uuid4())}, json={'model': 'seedance-2.5-30s', 'prompt': '湖面泛起微光，镜头缓缓向前推进', 'duration': 30, 'resolution': '720P', 'aspect_ratio': '16:9'}, timeout=85)
response.raise_for_status()
job = response.json()
print("保存任务 ID，断线后继续查询：", job["id"])
deadline = time.monotonic() + 7200
while job["status"] not in ("succeeded", "failed") and time.monotonic() < deadline:
    time.sleep(5)
    response = requests.get(BASE + "/v1/videos/jobs/" + job["id"], headers=headers, timeout=85)
    response.raise_for_status()
    job = response.json()
if job["status"] == "succeeded":
    url = job["data"][0]["url"]
    if not url.startswith("https://media.vidbudget.com/videos/"):
        raise RuntimeError("Unexpected media URL")
    with requests.get(url, stream=True, timeout=85) as media:
        media.raise_for_status()
        with open("video.mp4", "wb") as output:
            for chunk in media.iter_content(1024 * 1024):
                output.write(chunk)
elif job["status"] == "failed":
    print("失败，预扣已退回：", job["error"])
else:
    print("尚未完成，请继续查询原任务：", job["id"])

```

### Node.js · 完整轮询

```
// Node.js 18+; save the task ID before polling.
const base = "https://vidbudget.com";
const headers = {Authorization: `Bearer ${process.env.VIDBUDGET_API_KEY}`};
async function request(path, options = {}) {
  const response = await fetch(base + path, {...options, signal: AbortSignal.timeout(85000)});
  const data = await response.json();
  if (!response.ok) throw new Error(JSON.stringify(data.error));
  return data;
}
let job = await request('/v1/videos/generations', {method: 'POST', headers: {...headers,
  'Content-Type': 'application/json', 'Idempotency-Key': crypto.randomUUID()},
  body: JSON.stringify({"model": "seedance-2.5-30s", "prompt": "湖面泛起微光，镜头缓缓向前推进", "duration": 30, "resolution": "720P", "aspect_ratio": "16:9"})});
console.log('Task ID:', job.id);
const deadline = Date.now() + 7200000;
while (!['succeeded', 'failed'].includes(job.status) && Date.now() < deadline) {
  await new Promise(resolve => setTimeout(resolve, 5000));
  job = await request('/v1/videos/jobs/' + job.id, {headers});
}
console.log(job.status === 'succeeded' ? job.data[0].url : job);

```

## 认证与请求头

在控制台创建 API 密钥。密钥仅创建时显示一次，不放入浏览器公开代码、URL 或日志。

### HTTP

```
Authorization: Bearer YOUR_API_KEY
Content-Type: application/json
Idempotency-Key: YOUR_UNIQUE_REQUEST_ID
```

## 规格与适用范围

参考图片用来描述人物、场景、商品和风格，不是指定首帧或尾帧。请确保拥有输入素材及人物肖像的使用权。

模型规格和可接单的通道组合不同；无可用配置时返回 503，不能仅凭模型出现在列表中认定可以生成。

| 项目 | 支持范围 |
| --- | --- |
| 时长 | 4 / 5 / 6 / 7 / 8 / 9 / 10 / 11 / 12 / 13 / 14 / 15 / 16 / 17 / 18 / 19 / 20 / 21 / 22 / 23 / 24 / 25 / 26 / 27 / 28 / 29 / 30 秒 |
| 分辨率 | 720P |
| 画幅 | 16:9 / 9:16 / 1:1 / 4:3 / 3:4 / 21:9 |
| 参考图片 | 30 张以内 |
| 参考视频 | 10 段以内 |
| 参考音频 | 10 段以内 |
| 提示词 | 非空；请求 JSON 上限 2MB |
| 计费 | 按条 |
| 视频保留 | 7 天，请及时下载 |

## 请求参数

Content-Type 为 application/json。不要把未支持的参数当作生效设置：当前不提供 callback_url、n 多条生成、指定首尾帧或流式输出。

| 参数 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- |
| model | string | 是 | seedance-2.5-30s |
| prompt | string | 是 | 视频描述；素材按 @Image1 / @Video1 / @Audio1 引用，序号对应各数组顺序。 |
| duration | integer | 否 | 默认 30 秒；范围见规格。Clip 竖屏必须显式指定 15 秒。 |
| resolution | string | 否 | 默认 720P；可选 720P |
| aspect_ratio | string | 否 | 默认 16:9；别名 size、ratio；不能互相冲突。 |
| images | array<string> | 否 | HTTPS 图片 URL 数组；别名 reference_images、image_url、image，同一类型只传一个字段。 |
| video_references | array<string> | 否 | HTTPS 视频 URL 数组；本模型不支持时不要传。别名 video_reference、video_url。 |
| audio_references | array<string> | 否 | HTTPS 音频 URL 数组；本模型不支持时不要传。别名 audio_reference、audio_url。 |
| audio | boolean | 否 | 按条产品禁止传此参数；其他产品默认 true。false 不保证能关闭成片音轨，取决于模型本身。别名 generate_audio。 |
| seconds | integer | 否 | duration 的别名；同时传时必须相同。 |
| image_role | string | 否 | 只接受 reference / reference_images / ref；不支持首尾帧。 |

## 价格与扣费

下面读取当前价格配置，与实际扣费函数使用同一来源。提交预扣与任务入库在同一事务完成；成功结算，最终失败退回预扣金额。

特价版带参考音频为 $3.00 / 条；不传参考音频使用表中价格。素材要求以当前可用规格为准。

| 分辨率 | 单价 USD | 示例费用 |
| --- | --- | --- |
| 720P | 2 / 条 | 30 秒：$2.00 |

## 参考素材与示例

先上传素材并查询上传任务，拿到 media.vidbudget.com 链接后填入相应数组；也可使用可公开下载的 HTTPS 素材 URL。不接受 base64 或 data: URI。

图片可用于角色与风格参考，视频可用于动作和运镜参考，音频可用于声音节奏参考。是否支持各类型及数量以本模型规格为准。

素材下载与验证在后台进行；无法读取、格式不合法或违反内容规则可能导致任务失败。失败时退回预扣余额。

真人照片可能受到生成服务的内容限制；不能保证每次接受或每次输出符合主观预期。

视频保留 7 天，请及时下载。上传素材也保留 7 天，不要把临时链接用作永久存储。

### JSON · 图片参考

```
{
  "model": "seedance-2.5-30s",
  "prompt": "湖面泛起微光，镜头缓缓向前推进",
  "duration": 30,
  "resolution": "720P",
  "aspect_ratio": "16:9",
  "images": [
    "https://media.vidbudget.com/inputs/YOUR_IMAGE_UUID.png"
  ]
}
```

## 响应结构

提交响应同样使用此结构，但 status 为 queued，completed_at 为 null、data 为空数组。error 在失败时是错误码字符串，成功或进行中为 null。

### JSON · 查询完成

```
{
  "id": "TASK_UUID",
  "object": "task",
  "kind": "video",
  "model": "seedance-2.5-30s",
  "status": "succeeded",
  "created_at": 1793354400,
  "completed_at": 1793354700,
  "elapsed_seconds": 300,
  "retry_count": 0,
  "error": null,
  "data": [
    {
      "url": "https://media.vidbudget.com/videos/VIDEO_UUID.mp4"
    }
  ]
}
```

## 任务状态与重试

retry_count 只记录额外提交次数，初次为 0，最多额外重试 2 次；查询次数不计入。completed_at 仅在最终成功或失败时写入，耗时包含排队、重试和媒体转存。

同通道自动重试仅针对可确认的可重试失败；提交结果未知时不会盲目重复购买。最终失败只退款一次，每笔余额变化有流水。

| 状态 | 含义 |
| --- | --- |
| queued | 已入库等待后台处理 |
| processing | 素材处理、生成、轮询或转存进行中 |
| succeeded | 已完成，读取 data[0].url |
| failed | 最终失败，读取 error；预扣自动退回 |

## 生成耗时与超时

视频生成可能需要数分钟或更久，时长、分辨率和排队都会影响完成时间。新站尚无真实生成耗时统计，不把原站实测值当作本服务承诺。

HTTP 请求时限为 85 秒，超时返回 JSON 504；生成总任务时限当前为 7200 秒。浏览器关闭不会取消后台任务。

503 表示当前没有匹配的已配置通道；先修复配置或等待服务可用，不要持续重复提交。

## 错误与处理方式

HTTP 错误结构为 {"error":{"code":"..."}}，message 不保证存在。任务失败则通过任务对象的 error 字符串读取。

| HTTP / 任务 | 错误码 | 处理 |
| --- | --- | --- |
| 400 | invalid_request | 检查参数、类型、素材字段和模型约束 |
| 401 | invalid_api_key / login_required | 检查密钥或登录状态 |
| 402 | insufficient_balance | 充值后再提交 |
| 404 | task_not_found | 确认任务属于当前用户，ID 正确 |
| 409 | conflict | 模型关闭或幂等键参数冲突 |
| 413 | payload_too_large | 大文件使用预签名直传 |
| 415 | unsupported_media_type | 仅上传图片/视频/音频 |
| 429 | upload_rate_limit | 等待窗口恢复 |
| 503 | channel_not_configured_or_unavailable | 没有可用通道，稍后重试 |
| 504 | request_timeout | 查询原任务，避免重复购买 |
| 任务 | media_processing_failed | 检查素材能否读取及实际文件格式 |
| 任务 | generation_timeout | 任务达到总时限，预扣退回 |
| 任务 | generation_failed 等安全错误码 | 检查内容与参数，再决定是否新建任务 |

## 素材上传与大文件直传

每 API key 每小时默认限制当前为 150 次。普通上传上限为 90000000 字节；超过此值使用预签名 PUT。直传上限 1000000000 字节，签名有效期 15 分钟。

支持图片、视频、音频 MIME，并在后台解码验证实际内容。推荐 PNG/JPEG/WebP，MP4，MP3/WAV/M4A。批量上传时每个文件单独请求，建议最多 6 路并发，保留添加顺序作为素材编号。

PUT 到 upload_url 时只带返回的上传 headers，不发送 VidBudget API key。上传完成后调用 complete，再查询任务取得最终素材 URL。

### cURL · 普通上传

```
curl https://vidbudget.com/v1/uploads -H "Authorization: Bearer $VIDBUDGET_API_KEY" -F "file=@reference.png"
```

### HTTP · 大文件流程

```
POST /v1/uploads/presign
{"content_type":"video/mp4","size":100000000}

PUT <upload_url>  （原始文件字节，使用返回的 headers）
POST /v1/uploads/<upload_id>/complete
GET /v1/tasks/<task_id>
```

## Webhook 与兼容性

当前 VidBudget 未实现生成结果 Webhook，不要传 callback_url 并期待通知。请使用 GET /v1/videos/jobs/{id} 或 GET /v1/tasks/{id} 轮询。

原版同步上传、其他支付方式、其他模型及旧站特有响应字段不适用于本接口。账户接口：GET /v1/balance、GET /v1/tasks、GET /v1/models。


---

# Seedance 2.0 · VidBudget API

POST https://vidbudget.com/v1/videos/generations

视频保留 7 天，请及时下载。

## 快速开始：提交 → 查询 → 下载

这是异步视频接口。提交成功返回 HTTP 202 和任务 ID，视频生成及素材处理在后台完成；不要等待提交请求直接返回视频。

保存任务 ID，每 5–10 秒查询一次。请求中断后继续查询原任务；需要重发提交时使用同一个幂等键和完全相同的参数。

### cURL · 提交

```
curl https://vidbudget.com/v1/videos/generations \
  -H "Authorization: Bearer $VIDBUDGET_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: YOUR_UNIQUE_REQUEST_ID" \
  -d '{
  "model": "seedance-2.0",
  "prompt": "湖面泛起微光，镜头缓缓向前推进",
  "duration": 5,
  "resolution": "720P",
  "aspect_ratio": "16:9"
}'
```

### cURL · 查询

```
curl https://vidbudget.com/v1/videos/jobs/TASK_ID -H "Authorization: Bearer $VIDBUDGET_API_KEY"
```

### Python · 完整轮询

```
import os, time, uuid, requests

BASE = "https://vidbudget.com"
headers = {"Authorization": "Bearer " + os.environ["VIDBUDGET_API_KEY"]}
response = requests.post(BASE + "/v1/videos/generations", headers={**headers,
    "Idempotency-Key": str(uuid.uuid4())}, json={'model': 'seedance-2.0', 'prompt': '湖面泛起微光，镜头缓缓向前推进', 'duration': 5, 'resolution': '720P', 'aspect_ratio': '16:9'}, timeout=85)
response.raise_for_status()
job = response.json()
print("保存任务 ID，断线后继续查询：", job["id"])
deadline = time.monotonic() + 7200
while job["status"] not in ("succeeded", "failed") and time.monotonic() < deadline:
    time.sleep(5)
    response = requests.get(BASE + "/v1/videos/jobs/" + job["id"], headers=headers, timeout=85)
    response.raise_for_status()
    job = response.json()
if job["status"] == "succeeded":
    url = job["data"][0]["url"]
    if not url.startswith("https://media.vidbudget.com/videos/"):
        raise RuntimeError("Unexpected media URL")
    with requests.get(url, stream=True, timeout=85) as media:
        media.raise_for_status()
        with open("video.mp4", "wb") as output:
            for chunk in media.iter_content(1024 * 1024):
                output.write(chunk)
elif job["status"] == "failed":
    print("失败，预扣已退回：", job["error"])
else:
    print("尚未完成，请继续查询原任务：", job["id"])

```

### Node.js · 完整轮询

```
// Node.js 18+; save the task ID before polling.
const base = "https://vidbudget.com";
const headers = {Authorization: `Bearer ${process.env.VIDBUDGET_API_KEY}`};
async function request(path, options = {}) {
  const response = await fetch(base + path, {...options, signal: AbortSignal.timeout(85000)});
  const data = await response.json();
  if (!response.ok) throw new Error(JSON.stringify(data.error));
  return data;
}
let job = await request('/v1/videos/generations', {method: 'POST', headers: {...headers,
  'Content-Type': 'application/json', 'Idempotency-Key': crypto.randomUUID()},
  body: JSON.stringify({"model": "seedance-2.0", "prompt": "湖面泛起微光，镜头缓缓向前推进", "duration": 5, "resolution": "720P", "aspect_ratio": "16:9"})});
console.log('Task ID:', job.id);
const deadline = Date.now() + 7200000;
while (!['succeeded', 'failed'].includes(job.status) && Date.now() < deadline) {
  await new Promise(resolve => setTimeout(resolve, 5000));
  job = await request('/v1/videos/jobs/' + job.id, {headers});
}
console.log(job.status === 'succeeded' ? job.data[0].url : job);

```

## 认证与请求头

在控制台创建 API 密钥。密钥仅创建时显示一次，不放入浏览器公开代码、URL 或日志。

### HTTP

```
Authorization: Bearer YOUR_API_KEY
Content-Type: application/json
Idempotency-Key: YOUR_UNIQUE_REQUEST_ID
```

## 规格与适用范围

参考图片用来描述人物、场景、商品和风格，不是指定首帧或尾帧。请确保拥有输入素材及人物肖像的使用权。

模型规格和可接单的通道组合不同；无可用配置时返回 503，不能仅凭模型出现在列表中认定可以生成。

| 项目 | 支持范围 |
| --- | --- |
| 时长 | 5 / 6 / 7 / 8 / 9 / 10 / 11 / 12 / 13 / 14 / 15 秒 |
| 分辨率 | 720P |
| 画幅 | 16:9 / 9:16 / 1:1 / 4:3 / 3:4 |
| 参考图片 | 9 张以内 |
| 参考视频 | 3 段以内 |
| 参考音频 | 不支持 |
| 提示词 | 最多 5000 UTF-8 字节（汉字通常占 3 字节） |
| 计费 | 按生成秒数 |
| 视频保留 | 7 天，请及时下载 |

## 请求参数

Content-Type 为 application/json。不要把未支持的参数当作生效设置：当前不提供 callback_url、n 多条生成、指定首尾帧或流式输出。

| 参数 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- |
| model | string | 是 | seedance-2.0 |
| prompt | string | 是 | 视频描述；素材按 @Image1 / @Video1 / @Audio1 引用，序号对应各数组顺序。 |
| duration | integer | 否 | 默认 5 秒；范围见规格。Clip 竖屏必须显式指定 15 秒。 |
| resolution | string | 否 | 默认 720P；可选 720P |
| aspect_ratio | string | 否 | 默认 16:9；别名 size、ratio；不能互相冲突。 |
| images | array<string> | 否 | HTTPS 图片 URL 数组；别名 reference_images、image_url、image，同一类型只传一个字段。 |
| video_references | array<string> | 否 | HTTPS 视频 URL 数组；本模型不支持时不要传。别名 video_reference、video_url。 |
| audio_references | array<string> | 否 | HTTPS 音频 URL 数组；本模型不支持时不要传。别名 audio_reference、audio_url。 |
| audio | boolean | 否 | 按条产品禁止传此参数；其他产品默认 true。false 不保证能关闭成片音轨，取决于模型本身。别名 generate_audio。 |
| seconds | integer | 否 | duration 的别名；同时传时必须相同。 |
| image_role | string | 否 | 只接受 reference / reference_images / ref；不支持首尾帧。 |

## 价格与扣费

下面读取当前价格配置，与实际扣费函数使用同一来源。提交预扣与任务入库在同一事务完成；成功结算，最终失败退回预扣金额。

| 分辨率 | 单价 USD | 示例费用 |
| --- | --- | --- |
| 720P | 0.15 / 秒 | 5 秒：$0.75 |

## 参考素材与示例

先上传素材并查询上传任务，拿到 media.vidbudget.com 链接后填入相应数组；也可使用可公开下载的 HTTPS 素材 URL。不接受 base64 或 data: URI。

图片可用于角色与风格参考，视频可用于动作和运镜参考，音频可用于声音节奏参考。是否支持各类型及数量以本模型规格为准。

素材下载与验证在后台进行；无法读取、格式不合法或违反内容规则可能导致任务失败。失败时退回预扣余额。

真人照片可能受到生成服务的内容限制；不能保证每次接受或每次输出符合主观预期。

视频保留 7 天，请及时下载。上传素材也保留 7 天，不要把临时链接用作永久存储。

当前接入通道只接 720P。虽然模型基础规格含 480P/1080P，当前不能提交这两档。

### JSON · 图片参考

```
{
  "model": "seedance-2.0",
  "prompt": "湖面泛起微光，镜头缓缓向前推进",
  "duration": 5,
  "resolution": "720P",
  "aspect_ratio": "16:9",
  "images": [
    "https://media.vidbudget.com/inputs/YOUR_IMAGE_UUID.png"
  ]
}
```

## 响应结构

提交响应同样使用此结构，但 status 为 queued，completed_at 为 null、data 为空数组。error 在失败时是错误码字符串，成功或进行中为 null。

### JSON · 查询完成

```
{
  "id": "TASK_UUID",
  "object": "task",
  "kind": "video",
  "model": "seedance-2.0",
  "status": "succeeded",
  "created_at": 1793354400,
  "completed_at": 1793354700,
  "elapsed_seconds": 300,
  "retry_count": 0,
  "error": null,
  "data": [
    {
      "url": "https://media.vidbudget.com/videos/VIDEO_UUID.mp4"
    }
  ]
}
```

## 任务状态与重试

retry_count 只记录额外提交次数，初次为 0，最多额外重试 2 次；查询次数不计入。completed_at 仅在最终成功或失败时写入，耗时包含排队、重试和媒体转存。

同通道自动重试仅针对可确认的可重试失败；提交结果未知时不会盲目重复购买。最终失败只退款一次，每笔余额变化有流水。

| 状态 | 含义 |
| --- | --- |
| queued | 已入库等待后台处理 |
| processing | 素材处理、生成、轮询或转存进行中 |
| succeeded | 已完成，读取 data[0].url |
| failed | 最终失败，读取 error；预扣自动退回 |

## 生成耗时与超时

视频生成可能需要数分钟或更久，时长、分辨率和排队都会影响完成时间。新站尚无真实生成耗时统计，不把原站实测值当作本服务承诺。

HTTP 请求时限为 85 秒，超时返回 JSON 504；生成总任务时限当前为 7200 秒。浏览器关闭不会取消后台任务。

503 表示当前没有匹配的已配置通道；先修复配置或等待服务可用，不要持续重复提交。

## 错误与处理方式

HTTP 错误结构为 {"error":{"code":"..."}}，message 不保证存在。任务失败则通过任务对象的 error 字符串读取。

| HTTP / 任务 | 错误码 | 处理 |
| --- | --- | --- |
| 400 | invalid_request | 检查参数、类型、素材字段和模型约束 |
| 401 | invalid_api_key / login_required | 检查密钥或登录状态 |
| 402 | insufficient_balance | 充值后再提交 |
| 404 | task_not_found | 确认任务属于当前用户，ID 正确 |
| 409 | conflict | 模型关闭或幂等键参数冲突 |
| 413 | payload_too_large | 大文件使用预签名直传 |
| 415 | unsupported_media_type | 仅上传图片/视频/音频 |
| 429 | upload_rate_limit | 等待窗口恢复 |
| 503 | channel_not_configured_or_unavailable | 没有可用通道，稍后重试 |
| 504 | request_timeout | 查询原任务，避免重复购买 |
| 任务 | media_processing_failed | 检查素材能否读取及实际文件格式 |
| 任务 | generation_timeout | 任务达到总时限，预扣退回 |
| 任务 | generation_failed 等安全错误码 | 检查内容与参数，再决定是否新建任务 |

## 素材上传与大文件直传

每 API key 每小时默认限制当前为 150 次。普通上传上限为 90000000 字节；超过此值使用预签名 PUT。直传上限 1000000000 字节，签名有效期 15 分钟。

支持图片、视频、音频 MIME，并在后台解码验证实际内容。推荐 PNG/JPEG/WebP，MP4，MP3/WAV/M4A。批量上传时每个文件单独请求，建议最多 6 路并发，保留添加顺序作为素材编号。

PUT 到 upload_url 时只带返回的上传 headers，不发送 VidBudget API key。上传完成后调用 complete，再查询任务取得最终素材 URL。

### cURL · 普通上传

```
curl https://vidbudget.com/v1/uploads -H "Authorization: Bearer $VIDBUDGET_API_KEY" -F "file=@reference.png"
```

### HTTP · 大文件流程

```
POST /v1/uploads/presign
{"content_type":"video/mp4","size":100000000}

PUT <upload_url>  （原始文件字节，使用返回的 headers）
POST /v1/uploads/<upload_id>/complete
GET /v1/tasks/<task_id>
```

## Webhook 与兼容性

当前 VidBudget 未实现生成结果 Webhook，不要传 callback_url 并期待通知。请使用 GET /v1/videos/jobs/{id} 或 GET /v1/tasks/{id} 轮询。

原版同步上传、其他支付方式、其他模型及旧站特有响应字段不适用于本接口。账户接口：GET /v1/balance、GET /v1/tasks、GET /v1/models。


---

# Seedance 2.0 Fast · VidBudget API

POST https://vidbudget.com/v1/videos/generations

视频保留 7 天，请及时下载。

## 快速开始：提交 → 查询 → 下载

这是异步视频接口。提交成功返回 HTTP 202 和任务 ID，视频生成及素材处理在后台完成；不要等待提交请求直接返回视频。

保存任务 ID，每 5–10 秒查询一次。请求中断后继续查询原任务；需要重发提交时使用同一个幂等键和完全相同的参数。

### cURL · 提交

```
curl https://vidbudget.com/v1/videos/generations \
  -H "Authorization: Bearer $VIDBUDGET_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: YOUR_UNIQUE_REQUEST_ID" \
  -d '{
  "model": "seedance-2.0-fast",
  "prompt": "湖面泛起微光，镜头缓缓向前推进",
  "duration": 5,
  "resolution": "720P",
  "aspect_ratio": "16:9"
}'
```

### cURL · 查询

```
curl https://vidbudget.com/v1/videos/jobs/TASK_ID -H "Authorization: Bearer $VIDBUDGET_API_KEY"
```

### Python · 完整轮询

```
import os, time, uuid, requests

BASE = "https://vidbudget.com"
headers = {"Authorization": "Bearer " + os.environ["VIDBUDGET_API_KEY"]}
response = requests.post(BASE + "/v1/videos/generations", headers={**headers,
    "Idempotency-Key": str(uuid.uuid4())}, json={'model': 'seedance-2.0-fast', 'prompt': '湖面泛起微光，镜头缓缓向前推进', 'duration': 5, 'resolution': '720P', 'aspect_ratio': '16:9'}, timeout=85)
response.raise_for_status()
job = response.json()
print("保存任务 ID，断线后继续查询：", job["id"])
deadline = time.monotonic() + 7200
while job["status"] not in ("succeeded", "failed") and time.monotonic() < deadline:
    time.sleep(5)
    response = requests.get(BASE + "/v1/videos/jobs/" + job["id"], headers=headers, timeout=85)
    response.raise_for_status()
    job = response.json()
if job["status"] == "succeeded":
    url = job["data"][0]["url"]
    if not url.startswith("https://media.vidbudget.com/videos/"):
        raise RuntimeError("Unexpected media URL")
    with requests.get(url, stream=True, timeout=85) as media:
        media.raise_for_status()
        with open("video.mp4", "wb") as output:
            for chunk in media.iter_content(1024 * 1024):
                output.write(chunk)
elif job["status"] == "failed":
    print("失败，预扣已退回：", job["error"])
else:
    print("尚未完成，请继续查询原任务：", job["id"])

```

### Node.js · 完整轮询

```
// Node.js 18+; save the task ID before polling.
const base = "https://vidbudget.com";
const headers = {Authorization: `Bearer ${process.env.VIDBUDGET_API_KEY}`};
async function request(path, options = {}) {
  const response = await fetch(base + path, {...options, signal: AbortSignal.timeout(85000)});
  const data = await response.json();
  if (!response.ok) throw new Error(JSON.stringify(data.error));
  return data;
}
let job = await request('/v1/videos/generations', {method: 'POST', headers: {...headers,
  'Content-Type': 'application/json', 'Idempotency-Key': crypto.randomUUID()},
  body: JSON.stringify({"model": "seedance-2.0-fast", "prompt": "湖面泛起微光，镜头缓缓向前推进", "duration": 5, "resolution": "720P", "aspect_ratio": "16:9"})});
console.log('Task ID:', job.id);
const deadline = Date.now() + 7200000;
while (!['succeeded', 'failed'].includes(job.status) && Date.now() < deadline) {
  await new Promise(resolve => setTimeout(resolve, 5000));
  job = await request('/v1/videos/jobs/' + job.id, {headers});
}
console.log(job.status === 'succeeded' ? job.data[0].url : job);

```

## 认证与请求头

在控制台创建 API 密钥。密钥仅创建时显示一次，不放入浏览器公开代码、URL 或日志。

### HTTP

```
Authorization: Bearer YOUR_API_KEY
Content-Type: application/json
Idempotency-Key: YOUR_UNIQUE_REQUEST_ID
```

## 规格与适用范围

参考图片用来描述人物、场景、商品和风格，不是指定首帧或尾帧。请确保拥有输入素材及人物肖像的使用权。

模型规格和可接单的通道组合不同；无可用配置时返回 503，不能仅凭模型出现在列表中认定可以生成。

| 项目 | 支持范围 |
| --- | --- |
| 时长 | 5 / 6 / 7 / 8 / 9 / 10 / 11 / 12 / 13 / 14 / 15 秒 |
| 分辨率 | 720P |
| 画幅 | 16:9 / 9:16 / 1:1 / 4:3 / 3:4 |
| 参考图片 | 9 张以内 |
| 参考视频 | 3 段以内 |
| 参考音频 | 3 段以内 |
| 提示词 | 最多 5000 UTF-8 字节（汉字通常占 3 字节） |
| 计费 | 按生成秒数 |
| 视频保留 | 7 天，请及时下载 |

## 请求参数

Content-Type 为 application/json。不要把未支持的参数当作生效设置：当前不提供 callback_url、n 多条生成、指定首尾帧或流式输出。

| 参数 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- |
| model | string | 是 | seedance-2.0-fast |
| prompt | string | 是 | 视频描述；素材按 @Image1 / @Video1 / @Audio1 引用，序号对应各数组顺序。 |
| duration | integer | 否 | 默认 5 秒；范围见规格。Clip 竖屏必须显式指定 15 秒。 |
| resolution | string | 否 | 默认 720P；可选 720P |
| aspect_ratio | string | 否 | 默认 16:9；别名 size、ratio；不能互相冲突。 |
| images | array<string> | 否 | HTTPS 图片 URL 数组；别名 reference_images、image_url、image，同一类型只传一个字段。 |
| video_references | array<string> | 否 | HTTPS 视频 URL 数组；本模型不支持时不要传。别名 video_reference、video_url。 |
| audio_references | array<string> | 否 | HTTPS 音频 URL 数组；本模型不支持时不要传。别名 audio_reference、audio_url。 |
| audio | boolean | 否 | 按条产品禁止传此参数；其他产品默认 true。false 不保证能关闭成片音轨，取决于模型本身。别名 generate_audio。 |
| seconds | integer | 否 | duration 的别名；同时传时必须相同。 |
| image_role | string | 否 | 只接受 reference / reference_images / ref；不支持首尾帧。 |

## 价格与扣费

下面读取当前价格配置，与实际扣费函数使用同一来源。提交预扣与任务入库在同一事务完成；成功结算，最终失败退回预扣金额。

| 分辨率 | 单价 USD | 示例费用 |
| --- | --- | --- |
| 720P | 0.075 / 秒 | 5 秒：$0.38 |

## 参考素材与示例

先上传素材并查询上传任务，拿到 media.vidbudget.com 链接后填入相应数组；也可使用可公开下载的 HTTPS 素材 URL。不接受 base64 或 data: URI。

图片可用于角色与风格参考，视频可用于动作和运镜参考，音频可用于声音节奏参考。是否支持各类型及数量以本模型规格为准。

素材下载与验证在后台进行；无法读取、格式不合法或违反内容规则可能导致任务失败。失败时退回预扣余额。

真人照片可能受到生成服务的内容限制；不能保证每次接受或每次输出符合主观预期。

视频保留 7 天，请及时下载。上传素材也保留 7 天，不要把临时链接用作永久存储。

### JSON · 图片参考

```
{
  "model": "seedance-2.0-fast",
  "prompt": "湖面泛起微光，镜头缓缓向前推进",
  "duration": 5,
  "resolution": "720P",
  "aspect_ratio": "16:9",
  "images": [
    "https://media.vidbudget.com/inputs/YOUR_IMAGE_UUID.png"
  ]
}
```

## 响应结构

提交响应同样使用此结构，但 status 为 queued，completed_at 为 null、data 为空数组。error 在失败时是错误码字符串，成功或进行中为 null。

### JSON · 查询完成

```
{
  "id": "TASK_UUID",
  "object": "task",
  "kind": "video",
  "model": "seedance-2.0-fast",
  "status": "succeeded",
  "created_at": 1793354400,
  "completed_at": 1793354700,
  "elapsed_seconds": 300,
  "retry_count": 0,
  "error": null,
  "data": [
    {
      "url": "https://media.vidbudget.com/videos/VIDEO_UUID.mp4"
    }
  ]
}
```

## 任务状态与重试

retry_count 只记录额外提交次数，初次为 0，最多额外重试 2 次；查询次数不计入。completed_at 仅在最终成功或失败时写入，耗时包含排队、重试和媒体转存。

同通道自动重试仅针对可确认的可重试失败；提交结果未知时不会盲目重复购买。最终失败只退款一次，每笔余额变化有流水。

| 状态 | 含义 |
| --- | --- |
| queued | 已入库等待后台处理 |
| processing | 素材处理、生成、轮询或转存进行中 |
| succeeded | 已完成，读取 data[0].url |
| failed | 最终失败，读取 error；预扣自动退回 |

## 生成耗时与超时

视频生成可能需要数分钟或更久，时长、分辨率和排队都会影响完成时间。新站尚无真实生成耗时统计，不把原站实测值当作本服务承诺。

HTTP 请求时限为 85 秒，超时返回 JSON 504；生成总任务时限当前为 7200 秒。浏览器关闭不会取消后台任务。

503 表示当前没有匹配的已配置通道；先修复配置或等待服务可用，不要持续重复提交。

## 错误与处理方式

HTTP 错误结构为 {"error":{"code":"..."}}，message 不保证存在。任务失败则通过任务对象的 error 字符串读取。

| HTTP / 任务 | 错误码 | 处理 |
| --- | --- | --- |
| 400 | invalid_request | 检查参数、类型、素材字段和模型约束 |
| 401 | invalid_api_key / login_required | 检查密钥或登录状态 |
| 402 | insufficient_balance | 充值后再提交 |
| 404 | task_not_found | 确认任务属于当前用户，ID 正确 |
| 409 | conflict | 模型关闭或幂等键参数冲突 |
| 413 | payload_too_large | 大文件使用预签名直传 |
| 415 | unsupported_media_type | 仅上传图片/视频/音频 |
| 429 | upload_rate_limit | 等待窗口恢复 |
| 503 | channel_not_configured_or_unavailable | 没有可用通道，稍后重试 |
| 504 | request_timeout | 查询原任务，避免重复购买 |
| 任务 | media_processing_failed | 检查素材能否读取及实际文件格式 |
| 任务 | generation_timeout | 任务达到总时限，预扣退回 |
| 任务 | generation_failed 等安全错误码 | 检查内容与参数，再决定是否新建任务 |

## 素材上传与大文件直传

每 API key 每小时默认限制当前为 150 次。普通上传上限为 90000000 字节；超过此值使用预签名 PUT。直传上限 1000000000 字节，签名有效期 15 分钟。

支持图片、视频、音频 MIME，并在后台解码验证实际内容。推荐 PNG/JPEG/WebP，MP4，MP3/WAV/M4A。批量上传时每个文件单独请求，建议最多 6 路并发，保留添加顺序作为素材编号。

PUT 到 upload_url 时只带返回的上传 headers，不发送 VidBudget API key。上传完成后调用 complete，再查询任务取得最终素材 URL。

### cURL · 普通上传

```
curl https://vidbudget.com/v1/uploads -H "Authorization: Bearer $VIDBUDGET_API_KEY" -F "file=@reference.png"
```

### HTTP · 大文件流程

```
POST /v1/uploads/presign
{"content_type":"video/mp4","size":100000000}

PUT <upload_url>  （原始文件字节，使用返回的 headers）
POST /v1/uploads/<upload_id>/complete
GET /v1/tasks/<task_id>
```

## Webhook 与兼容性

当前 VidBudget 未实现生成结果 Webhook，不要传 callback_url 并期待通知。请使用 GET /v1/videos/jobs/{id} 或 GET /v1/tasks/{id} 轮询。

原版同步上传、其他支付方式、其他模型及旧站特有响应字段不适用于本接口。账户接口：GET /v1/balance、GET /v1/tasks、GET /v1/models。
