生成第一个视频
在你自己的服务器上跑通一次完整调用:提交、等待、下载,最后核对费用。示例请求一段五秒的 Seedance 2.0 Fast 视频。
开始前
需要一个已验证邮箱、有可用 Credits 的账户,项目已开通要调用的模型。在服务端设置两个环境变量:
| 变量 | 用途 |
|---|---|
VIZO_API_KEY | 项目 Key,需要 models:read、generations:create、generations:read、generations:list(回包丢失后用来找回任务)和 assets:download。 |
VIZO_BILLING_KEY | 只读账单 Key,需要 billing:read,只在最后核对费用时使用。 |
获取 API Key
GET/v1/models在控制台的 API Keys 创建项目 Key。完整 Key 只显示一次,请立即存进服务端的密钥管理。不要把 Key 放进网页、App 或代码库。
用
listModels确认 Key 可用。返回的每一项是项目可以下单的一个规格,包含modelId、operation、inputSchema和参考价。cURL
# Keep the key on the server only export VIZO_API_KEY="va_xxxxxxxx" curl https://api.vizoapi.com/v1/models \ -H "Authorization: Bearer $VIZO_API_KEY"Python
import os, requests api = "https://api.vizoapi.com/v1" headers = {"Authorization": f"Bearer {os.environ['VIZO_API_KEY']}"} models = requests.get(f"{api}/models", headers=headers, timeout=30).json() for m in models["entries"]: print(m["modelId"], m["operation"])Node.js
const api = "https://api.vizoapi.com/v1"; const headers = { Authorization: `Bearer ${process.env.VIZO_API_KEY}`, "Content-Type": "application/json", }; const models = await fetch(`${api}/models`, { headers }).then((r) => r.json()); console.log(models.entries.map((m) => `${m.modelId} ${m.operation}`));响应200{ "entries": [ { "modelId": "seedance-2-0-fast", "operation": "text-to-video", "routeId": "seedance-2-0-fast-720p", "inputSchema": { "…": "…" } } ], "next_cursor": "" }提交生成
POST/v1/generations把
model、operation和input直接发给POST /v1/generations。返回 202 表示任务已受理,请立即保存返回的id。新任务可能先处于
pending_funding:额度确认前不会冻结任何 Credits,确认后变为queued。额度被拒绝时,任务会关闭,error.code为insufficient_balance、budget_exceeded或spending_blocked。把
metadata.client_reference设为你自己的订单号。回包丢失时,可以用它找回任务。创建请求只发送一次。每个被受理的 POST 都是独立任务,会分别冻结和扣费,内容完全相同的第二次提交也一样。请关闭 HTTP 客户端和代理对这个请求的自动重试。
超时或断连时先查询,再决定是否重新提交。已经拿到任务 ID,就用
getGeneration查询原任务。没有拿到 ID,就用listGenerations查看近期任务,按metadata.client_reference找回它。列表按创建时间从新到旧排列,可用model、source、status、created_after(含)和created_before(不含)缩小范围。cURL
# Send once: do not let a client or proxy retry this request curl https://api.vizoapi.com/v1/generations \ -H "Authorization: Bearer $VIZO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "seedance-2-0-fast", "operation": "text-to-video", "input": { "prompt": "池塘里漂着一只纸船", "duration_seconds": 5 }, "metadata": { "client_reference": "order-1042" } }'Python
import os, requests api = "https://api.vizoapi.com/v1" headers = {"Authorization": f"Bearer {os.environ['VIZO_API_KEY']}"} # Send once: do not retry this request automatically task = requests.post(f"{api}/generations", headers=headers, json={ "model": "seedance-2-0-fast", "operation": "text-to-video", "input": {"prompt": "池塘里漂着一只纸船", "duration_seconds": 5}, "metadata": {"client_reference": "order-1042"}, }, timeout=60).json() task_id = task["id"] # save it nowNode.js
const api = "https://api.vizoapi.com/v1"; const headers = { Authorization: `Bearer ${process.env.VIZO_API_KEY}`, "Content-Type": "application/json", }; // Send once: do not retry this request automatically const task = await fetch(`${api}/generations`, { method: "POST", headers, body: JSON.stringify({ model: "seedance-2-0-fast", operation: "text-to-video", input: { prompt: "池塘里漂着一只纸船", duration_seconds: 5 }, metadata: { client_reference: "order-1042" }, }), }).then((r) => r.json()); const taskId = task.id; // save it now响应202{ "id": "gen_01JD8Z5QW3XR", "status": "pending_funding", "execution_status": "pending_funding", "delivery_status": "pending", "billing_status": "pending", "version": 1, "price": { "unit": "credit", "reserved": "0.000", "quoted": "1400.000", "charged": "0.000" }, "allowed_actions": [ "cancel" ], "customer_resolution_deadline": "2026-11-03T08:00:12Z", "created_at": "2026-11-02T08:00:12Z", "model": "seedance-2-0-fast", "source": "api", "closed_at": null, "late_result_available": false, "error": null, "result": null, "metadata": { "client_reference": "order-1042" } }等待结果
GET/v1/generations/{id}轮询原任务,直到
closed_at不为空。任务有三个独立状态,分别看:execution_status:模型生成进度delivery_status:结果是否已保存,ready表示可以下载billing_status:冻结中、已扣费或已释放
closed_at有值而status不是succeeded时,没有可下载的结果。读取error.code查看原因,例如insufficient_balance、budget_exceeded或spending_blocked。没有拿到额度的任务,charged为0.000。不想轮询?在控制台创建回调端点,提交时传
webhook_endpoint_id。收到通知后先验签,再用任务 ID 查询最新状态,以查询结果为准。cURL
curl https://api.vizoapi.com/v1/generations/$GENERATION_ID \ -H "Authorization: Bearer $VIZO_API_KEY"Python
import os, requests api = "https://api.vizoapi.com/v1" headers = {"Authorization": f"Bearer {os.environ['VIZO_API_KEY']}"} import time while True: task = requests.get(f"{api}/generations/{task_id}", headers=headers, timeout=30).json() if task["closed_at"]: break time.sleep(5) if task["status"] != "succeeded": # Closed without a result, for example insufficient_balance, budget_exceeded or spending_blocked raise SystemExit(f"task closed: {task['status']} {(task['error'] or {}).get('code')}") print(task["status"], task["price"]["charged"])Node.js
const api = "https://api.vizoapi.com/v1"; const headers = { Authorization: `Bearer ${process.env.VIZO_API_KEY}`, "Content-Type": "application/json", }; let task; do { await new Promise((r) => setTimeout(r, 5000)); task = await fetch(`${api}/generations/${taskId}`, { headers }).then((r) => r.json()); } while (!task.closed_at); if (task.status !== "succeeded") { // Closed without a result, for example insufficient_balance, budget_exceeded or spending_blocked throw new Error(`task closed: ${task.status} ${task.error?.code}`); } console.log(task.status, task.price.charged);响应200{ "id": "gen_01JD8Z5QW3XR", "status": "succeeded", "execution_status": "succeeded", "delivery_status": "ready", "billing_status": "settled", "version": 4, "price": { "unit": "credit", "reserved": "1400.000", "quoted": "1400.000", "charged": "1400.000" }, "allowed_actions": [], "customer_resolution_deadline": "2026-11-03T08:00:12Z", "created_at": "2026-11-02T08:00:12Z", "model": "seedance-2-0-fast", "source": "api", "closed_at": "2026-11-02T08:01:43Z", "late_result_available": false, "error": null, "result": { "asset_id": "ast_01JD8Z9M2C4V", "outputs": [ { "index": 0, "asset_id": "ast_01JD8Z9M2C4V", "content_type": "video/mp4" } ] }, "metadata": { "client_reference": "order-1042" } }下载结果
GET/v1/assets/{id}/download用
result.asset_id申请下载授权,返回一个短期有效的url。再单独请求这个链接拿到文件,不要附带 API Key。链接过期后,对同一个素材重新申请即可。一次任务有多个结果时,按
result.outputs的顺序逐个下载。成品保存 7 天,可以用deleteAsset提前删除。cURL
# 1. Ask for a short-lived download URL curl https://api.vizoapi.com/v1/assets/$ASSET_ID/download \ -H "Authorization: Bearer $VIZO_API_KEY" # 2. Fetch the returned url on its own, without the API key curl -o result.mp4 "$DOWNLOAD_URL"Python
import os, requests api = "https://api.vizoapi.com/v1" headers = {"Authorization": f"Bearer {os.environ['VIZO_API_KEY']}"} if task["status"] != "succeeded" or not task["result"]: raise SystemExit("no result to download: read task['error']['code']") asset_id = task["result"]["asset_id"] dl = requests.get(f"{api}/assets/{asset_id}/download", headers=headers, timeout=30).json() # Fetch the signed URL on its own, without the API key with requests.get(dl["url"], stream=True, timeout=300) as r, open("result.mp4", "wb") as f: for chunk in r.iter_content(1 << 20): f.write(chunk)Node.js
import { writeFile } from "node:fs/promises"; if (task.status !== "succeeded" || !task.result) throw new Error("no result to download: read task.error.code"); const dl = await fetch(`${api}/assets/${task.result.asset_id}/download`, { headers }).then((r) => r.json()); // Fetch the signed URL on its own, without the API key const media = await fetch(dl.url); await writeFile("result.mp4", Buffer.from(await media.arrayBuffer()));响应200{ "url": "https://media.vizoapi.com/d/ast_01JD8Z9M2C4V?sig=…", "url_expires_at": "2026-11-02T08:16:43Z" }核对费用
GET/v1/ledger用只读账单 Key 读取
/v1/ledger,沿着next_cursor读完所有分页。把这个任务的结算分录减去冲正分录,结果应等于任务的price.charged。生成失败或超时的任务,
charged为0.000,冻结额度已经退回可用余额。cURL
curl "https://api.vizoapi.com/v1/ledger?limit=100" \ -H "Authorization: Bearer $VIZO_BILLING_KEY" # When next_cursor is not empty, read the next page curl "https://api.vizoapi.com/v1/ledger?limit=100&cursor=$NEXT_CURSOR" \ -H "Authorization: Bearer $VIZO_BILLING_KEY"Python
import os, requests api = "https://api.vizoapi.com/v1" billing = {"Authorization": f"Bearer {os.environ['VIZO_BILLING_KEY']}"} entries, cursor = [], "" while True: page = requests.get(f"{api}/ledger", headers=billing, timeout=30, params={"limit": 100, "cursor": cursor}).json() entries += page["items"] or [] cursor = page.get("next_cursor") if not cursor: breakNode.js
const api = "https://api.vizoapi.com/v1"; const billing = { Authorization: `Bearer ${process.env.VIZO_BILLING_KEY}` }; const entries = []; let cursor = ""; do { const page = await fetch(`${api}/ledger?limit=100&cursor=${cursor}`, { headers: billing }).then((r) => r.json()); entries.push(...(page.items ?? [])); cursor = page.next_cursor; } while (cursor);响应200{ "items": [ { "id": "le_01JD8ZA1T6", "reference": "gen_01JD8Z5QW3XR", "from": "wallet:held", "to": "platform:settled", "credits": "1400.000", "created_at": "2026-11-02T08:01:43Z" } ], "next_cursor": "" }
示例响应中的金额和 ID 仅作演示,你的请求会返回各自的值。