API 文档

SeeAPI 客户接入文档

本文档面向第一批 API 客户,覆盖视频任务提交、任务查询、模型列表、余额查询、计费行为、错误码和 Webhook 验签。示例基础地址为预生产地址;正式域名备案完成后,请替换为平台提供的正式基础地址。

基础地址https://api.43.160.233.194.sslip.io
认证方式Authorization: Bearer sk_live_...
幂等请求Idempotency-Key: client-generated-key

认证与安全

外部 API 使用 API Key 认证。创建 API Key 时,完整密钥只展示一次;之后控制台只展示 key_prefix, 请在服务端安全保存密钥,不要放进前端代码、日志、截图或工单。

  • 所有公开 API 请求都带 Authorization: Bearer sk_live_...
  • API Key 无效返回 401 invalid_api_key;被暂停或禁用返回 403 系列错误。
  • 建议每次提交任务都带业务侧唯一 Idempotency-Key,例如订单号或请求流水号。

幂等、计费与恢复规则

  • 同一用户使用同一个 Idempotency-Key 和相同请求体,会返回同一个任务,不会重复扣费。
  • 同一个 Idempotency-Key 搭配不同请求体,会返回 409 idempotency_conflict
  • 平台在创建任务前预扣预计费用;任务成功后按已预扣金额正式结算。
  • 任务失败或取消后,Worker 会自动退款,任务最终通常表现为 refunded。
  • 余额不足返回 402 insufficient_credits,不会创建任务,并会把当前账户下 active API Key 暂停为 balance_paused
  • 充值后只恢复 balance_paused 的 API Key,不恢复 user_disabledadmin_disabledrisk_blocked
POST/v1/video/generations

提交视频任务

创建异步视频生成任务。成功时返回 202 Accepted 和任务 ID。

curl https://api.43.160.233.194.sslip.io/v1/video/generations \
  -H "Authorization: Bearer sk_live_xxx" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: order-20260510-0001" \
  -d '{
  "model": "seedance-2.0-face-video",
  "prompt": "生成一段自然柔光下的电影感真人肖像视频。",
  "face_reference_url": "https://example.com/assets/face.jpg",
  "image_reference_url": "https://example.com/assets/reference.jpg",
  "duration": 5,
  "aspect_ratio": "9:16",
  "callback_url": "https://client.example.com/webhooks/seeapi"
}'

请求体字段

字段要求说明
model必填当前公开模型为 seedance-2.0-face-video。后续多模型会复用同一接口。
prompt必填视频提示词,1 到 4000 字符。
face_reference_url必填真人脸参考资源 URI。当前模型要求提供该字段。
image_reference_url可选画面参考图 URI,用于补充构图或风格。
duration必填整数,当前支持 5 或 10 秒。
aspect_ratio必填当前支持 9:16、16:9、1:1。
callback_url可选接收任务 Webhook 通知的地址。当前 MVP 稳定投递成功事件;生产环境请使用 HTTPS。
GET/v1/tasks/{task_id}

查询任务

轮询任务状态,直到进入 succeeded、refunded、failed 或 canceled。

curl https://api.43.160.233.194.sslip.io/v1/tasks/job_xxx \
  -H "Authorization: Bearer sk_live_xxx"

# 成功响应示例
{
  "id": "job_xxx",
  "object": "task",
  "status": "succeeded",
  "model": "seedance-2.0-face-video",
  "output": {
    "video_url": "https://cdn.example.com/users/usr_xxx/jobs/job_xxx/output/video.mp4",
    "assets": [
      {
        "type": "output_video",
        "url": "https://cdn.example.com/users/usr_xxx/jobs/job_xxx/output/video.mp4",
        "mime_type": "video/mp4",
        "size_bytes": "12345678",
        "checksum": "sha256:..."
      }
    ]
  },
  "error": null,
  "cost": {
    "estimated_units": 5000000,
    "charged_units": 5000000,
    "refunded_units": 0,
    "currency": "credits"
  },
  "created_at": "2026-05-10T08:00:00.000Z",
  "finished_at": "2026-05-10T08:01:00.000Z"
}

任务状态

状态含义
created任务已持久化,等待入队;提交响应中会按 queued 展示。
queued已进入队列,等待 Worker 处理。
running已开始提交或轮询上游模型。
succeeded任务成功,余额正式结算,output 中返回平台存储后的结果地址。
failed任务失败的中间状态;Worker 会继续执行退款流程。
refunded任务失败或取消后已退款,cost.refunded_units 展示退款数量。
canceled任务已取消;如果已预扣,后续会进入退款流程。
GET/v1/models

模型列表

返回当前 API Key 可调用的公开或 beta 模型。当前只公开 seedance-2.0-face-video;后续多模型会复用同一接口。

curl https://api.43.160.233.194.sslip.io/v1/models \
  -H "Authorization: Bearer sk_live_xxx"

# 响应示例
{
  "data": [
    {
      "code": "seedance-2.0-face-video",
      "modality": "video",
      "provider": "seedance",
      "execution_mode": "async",
      "status": "public",
      "input_schema": {
        "required": ["model", "prompt", "face_reference_url", "duration", "aspect_ratio"]
      },
      "pricing_rule": {
        "type": "fixed_by_duration",
        "currency": "credits"
      }
    }
  ]
}
GET/v1/credits

查询余额

返回账户余额、币种和因余额不足暂停的 API Key 数量。

curl https://api.43.160.233.194.sslip.io/v1/credits \
  -H "Authorization: Bearer sk_live_xxx"

# 响应示例
{
  "balance_units": 12000000,
  "currency": "credits",
  "paused_api_keys": 0
}

Webhook 事件与签名

提交任务时传入 callback_url 后,可用 Webhook 接收任务通知。当前 MVP 稳定投递 task.succeeded;核心事件格式已覆盖 task.refundedtask.failedtask.canceled,这些非成功终态会在后续版本升级为稳定投递承诺。请始终以 /v1/tasks/{task_id} 作为最终状态兜底;Webhook 投递失败不会改变任务最终状态, 也不会再次触发扣费或退款。

  • Header:x-webhook-idx-webhook-timestampx-webhook-signature
  • 签名算法:HMAC_SHA256(secret, "{timestamp}.{rawBody}"),Header 值格式为 v1=hex_hmac_sha256
  • 验签时必须使用收到的原始请求体 raw body,不要使用重新序列化后的 JSON。
POSTclient callback_url

Webhook Payload

succeeded 事件会包含 output;refunded、failed、canceled 的事件格式中 output 为 null,并包含 error。当前 MVP 请不要依赖非成功终态一定投递。

{
  "id": "evt_xxx",
  "type": "task.succeeded",
  "created_at": "2026-05-10T08:01:00.000Z",
  "data": {
    "id": "job_xxx",
    "status": "succeeded",
    "model": "seedance-2.0-face-video",
    "output": {
      "video_url": "https://cdn.example.com/users/usr_xxx/jobs/job_xxx/output/video.mp4"
    },
    "error": null
  }
}
NODEx-webhook-signature

Node.js Webhook 验签

示例使用 Node.js 内置 http 和 crypto。请将 SEEAPI_WEBHOOK_SECRET 配置为平台为你的回调地址分配的密钥。

import { createHmac, timingSafeEqual } from "node:crypto";
import http from "node:http";

const WEBHOOK_SECRET = process.env.SEEAPI_WEBHOOK_SECRET;

function verifySignature(rawBody, timestamp, signature) {
  if (!WEBHOOK_SECRET || !timestamp || !signature?.startsWith("v1=")) return false;

  const expected = "v1=" + createHmac("sha256", WEBHOOK_SECRET)
    .update(`${timestamp}.${rawBody}`)
    .digest("hex");

  const receivedBuffer = Buffer.from(signature);
  const expectedBuffer = Buffer.from(expected);
  return receivedBuffer.length === expectedBuffer.length
    && timingSafeEqual(receivedBuffer, expectedBuffer);
}

http.createServer((req, res) => {
  if (req.method !== "POST") {
    res.writeHead(405).end();
    return;
  }

  const chunks = [];
  req.on("data", (chunk) => chunks.push(chunk));
  req.on("end", () => {
    const rawBody = Buffer.concat(chunks).toString("utf8");
    const timestamp = req.headers["x-webhook-timestamp"];
    const signature = req.headers["x-webhook-signature"];

    if (!verifySignature(rawBody, String(timestamp ?? ""), String(signature ?? ""))) {
      res.writeHead(401).end("invalid signature");
      return;
    }

    const event = JSON.parse(rawBody);
    console.log(req.headers["x-webhook-id"], event.type, event.data.id);
    res.writeHead(204).end();
  });
}).listen(3000);

统一错误格式

所有平台错误都使用统一结构;message 可用于排查,业务判断请优先依赖稳定的 code。

{
  "error": {
    "code": "insufficient_credits",
    "message": "Insufficient credits.",
    "type": "billing_error",
    "request_id": "req_xxx"
  }
}
错误码类型HTTP说明
invalid_requestinvalid_request_error400请求结构不合法时的通用说明;当前代码更常返回 validation_error、invalid_json 或 missing_required_field。
validation_errorinvalid_request_error400请求体字段缺失、类型错误或不符合模型 input_schema。
unauthorizedauthentication_error401未登录或认证失败;API Key 无效时通常返回 invalid_api_key。
insufficient_creditsbilling_error402余额不足,平台不会创建任务,并会暂停当前账户下 active API Key。
idempotency_conflictinvalid_request_error409同一 Idempotency-Key 已用于不同请求体。
model_not_foundmodel_error404模型不存在。
model_disabledmodel_error403模型已禁用。
provider_not_configuredmodel_error500模型对应的 Provider 未配置。
provider_invalid_requestprovider_error422上游认为请求无效。
provider_unavailableprovider_error502上游暂不可用。
provider_rate_limitedprovider_error429上游限流。
provider_timeoutprovider_error503上游超时。
internal_errorinternal_error500平台内部错误。
NODE/v1/video/generations

Node.js 提交并轮询

适用于 Node.js 18+,API Key 从服务端环境变量读取。

const BASE_URL = "https://api.43.160.233.194.sslip.io";
const API_KEY = process.env.SEEAPI_API_KEY;

async function main() {
  const submit = await fetch(`${BASE_URL}/v1/video/generations`, {
    method: "POST",
    headers: {
      Authorization: `Bearer ${API_KEY}`,
      "Content-Type": "application/json",
      "Idempotency-Key": "order-20260510-0001"
    },
    body: JSON.stringify({
      "model": "seedance-2.0-face-video",
      "prompt": "生成一段自然柔光下的电影感真人肖像视频。",
      "face_reference_url": "https://example.com/assets/face.jpg",
      "image_reference_url": "https://example.com/assets/reference.jpg",
      "duration": 5,
      "aspect_ratio": "9:16",
      "callback_url": "https://client.example.com/webhooks/seeapi"
})
  });

  const task = await submit.json();
  if (!submit.ok) throw new Error(JSON.stringify(task));

  while (true) {
    const res = await fetch(`${BASE_URL}/v1/tasks/${task.id}`, {
      headers: { Authorization: `Bearer ${API_KEY}` }
    });
    const current = await res.json();
    if (!res.ok) throw new Error(JSON.stringify(current));

    if (["succeeded", "refunded", "failed", "canceled"].includes(current.status)) {
      console.log(current);
      return;
    }
    await new Promise((resolve) => setTimeout(resolve, 3000));
  }
}

main().catch((error) => {
  console.error(error);
  process.exitCode = 1;
});
PYTHON/v1/video/generations

Python 提交并轮询

示例使用 requests;API Key 从服务端环境变量 SEEAPI_API_KEY 读取。

import os
import time
import requests

BASE_URL = "https://api.43.160.233.194.sslip.io"
API_KEY = os.environ["SEEAPI_API_KEY"]

payload = {
  "model": "seedance-2.0-face-video",
  "prompt": "生成一段自然柔光下的电影感真人肖像视频。",
  "face_reference_url": "https://example.com/assets/face.jpg",
  "image_reference_url": "https://example.com/assets/reference.jpg",
  "duration": 5,
  "aspect_ratio": "9:16",
  "callback_url": "https://client.example.com/webhooks/seeapi"
}

headers = {
    "Authorization": f"Bearer {API_KEY}",
    "Content-Type": "application/json",
    "Idempotency-Key": "order-20260510-0001",
}

submit = requests.post(f"{BASE_URL}/v1/video/generations", json=payload, headers=headers, timeout=30)
submit.raise_for_status()
task = submit.json()

while True:
    res = requests.get(
        f"{BASE_URL}/v1/tasks/{task['id']}",
        headers={"Authorization": f"Bearer {API_KEY}"},
        timeout=30,
    )
    res.raise_for_status()
    current = res.json()
    if current["status"] in ["succeeded", "refunded", "failed", "canceled"]:
        print(current)
        break
    time.sleep(3)