漫小白API 文档
下载完整 .md ↓

SD / Seedance 视频接入

使用本站 API 创建 Seedance 视频任务,查询进度并下载 MP4。本章核对日期:2026-10-14,面向 Seedance 2.0 Mini 和 Fast;这里的 SD 指 Seedance 视频模型。

不写代码可使用新版创作台,选择“视频生成”和对应 Seedance 模型。创作台支持图片、视频、音频参考和首尾帧。

模型与接入准备

显示名称 请求中的 model 分辨率 常规输出时长
Seedance 2.0 Mini dreamina-seedance-2-0-mini-260615 480p、720p 4–15 秒整数
Seedance 2.0 Fast dreamina-seedance-2-0-fast-260128 480p、720p 4–15 秒整数

在控制台创建具有“视频分组”权限的令牌,并用 GET /v1/models 确认目标模型可用。模型权限和价格以模型广场为准。两个模型使用同一套参数;当前不支持 1080p、2K、4K。

示例使用 https://api.manxiaobai.online;也可统一换成 https://openpix.cc。提交、查询、下载应使用同一入口和同一令牌。将令牌保存在服务端环境变量 MXB_API_KEY,不要写入前端网页、分享链接或代码仓库。

最小文生视频请求

POST /v1/videos,请求为 JSON。下面的命令执行一次付费生成提交;不要给提交命令加自动重试。

bash
curl --fail-with-body --max-time 120 \
  https://api.manxiaobai.online/v1/videos \
  -H "Authorization: Bearer $MXB_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{
    "model": "dreamina-seedance-2-0-mini-260615",
    "prompt": "一只蓝色纸船漂浮在平静的水面,柔和晨光,镜头缓慢推进",
    "seconds": "4",
    "resolution": "480p",
    "ratio": "16:9",
    "generate_audio": false,
    "watermark": false
  }'

受理成功返回视频任务对象。以下仅示意必要字段,ID 使用虚构值:

json
{
  "id": "task_EXAMPLE",
  "object": "video",
  "model": "dreamina-seedance-2-0-mini-260615",
  "status": "queued",
  "progress": 0
}

立即保存本站返回的 id。HTTP 200 表示任务已受理,不代表视频已经完成。实际请求返回的其他字段可以保留,客户端不应依赖字段顺序。

参数表

参数 类型 取值与说明
model string,必填 使用上表中的完整模型 ID
prompt string 非空提示词,最多 6000 个字符;与 metadata.content 中的 text 二选一
seconds string 或 integer 常规为 4–15 秒,省略默认 5 秒;如 "5"
resolution string 480p 或 720p,默认 480p
ratio string 16:9、9:16、1:1、4:3、3:4、21:9;默认 16:9
generate_audio boolean 是否生成声音,建议明确传 true 或 false
watermark boolean 水印开关,建议明确传值
metadata.content array 多模态内容数组,格式见下节;提示词加素材合计最多 13 项
seed integer,可选 -1 至 4294967295;随机种子不保证跨次输出完全相同
return_last_frame boolean,可选 请求附带末帧制品;本文下载流程获取视频主制品

也接受 duration 作为 seconds 的整数别名,size 作为 resolution 的别名;建议每项只传一个字段。这里的 size 应为 480p / 720p,不是像素字符串。宽高比使用 ratio,不要直接复制 Grok 示例中的 aspect_ratio。

高级用法:ratio: "adaptive" 需要至少一个图片或视频视觉输入,纯文本或纯音频不能使用。单视频编辑可传 omni_reference_task_type: "edit",且必须只有一个参考视频、没有其他参考素材;仅此模式允许 duration: -1 跟随输入时长。普通生成仍应使用 4–15 秒。不要同时提交值不同的别名或顶层/metadata 重复参数。

图片、视频与音频参考

使用 metadata.content 数组传入一个 text 项和参考素材。图片、视频、音频的编号分别按各自类型的数组顺序从 1 开始,提示词中可以使用 @图片1、@视频1、@音频1 指明参考对象。

素材 type role 数量上限
参考图片 image_url reference_image 9 张
参考视频 video_url reference_video 3 段
参考音频 audio_url reference_audio 3 段

所有参考素材合计最多 12 个。URL 必须为无需登录即可访问的有效 HTTPS 媒体地址,并在任务处理期间保持可访问;不要使用本机路径、内网地址或带有 API Key 的 URL。/v1/videos 的这两个模型不直接接受二进制附件,先取得素材 HTTPS 地址再提交;本地文件可在新版创作台上传。

下面用环境变量中的自有素材地址创建请求文件,不需要修改示例源码。先设置 MXB_IMAGE_URL、MXB_VIDEO_URL、MXB_AUDIO_URL,分别指向一张图片、一段合规视频和一段合规音频:

python
import json
import os
from pathlib import Path

content = [{"type": "text", "text": "参考@图片1的主体与@视频1的运镜,结合@音频1的节奏生成短片"}]
for kind, variable in [("image", "MXB_IMAGE_URL"), ("video", "MXB_VIDEO_URL"), ("audio", "MXB_AUDIO_URL")]:
    content.append({
        "type": kind + "_url",
        kind + "_url": {"url": os.environ[variable]},
        "role": "reference_" + kind,
    })
payload = {
    "model": "dreamina-seedance-2-0-mini-260615",
    "seconds": "4",
    "resolution": "480p",
    "ratio": "16:9",
    "generate_audio": True,
    "watermark": False,
    "metadata": {"content": content},
}
Path("seedance-request.json").write_text(json.dumps(payload, ensure_ascii=False), encoding="utf-8")

执行后使用下方完整客户端的 --request seedance-request.json 提交。数组里已有 text 时,不要再添加顶层 prompt。只有参考图片时,也可使用顶层 prompt 与 images: [图片HTTPS地址] 的简写;不要把简写与 metadata.content 混用。

视频参考要求:MP4/MOV、H.264/H.265、每段不超过 200 MB、3–10 秒、24–60 FPS;宽和高分别在 432–2160 像素,总像素至少 407,696。例如 512×512 不满足总像素下限,640×640 满足像素要求。

音频参考要求:MP3 或 PCM WAV、每段不超过 15 MB、2–30 秒。文件大小按十进制 MB 计算。传入合规素材不代表生成一定成功,还需通过内容和任务校验。

首帧与尾帧

这两个型号要求首尾帧成对提供,各一张;不能只填首帧或只填尾帧,也不能与参考图片、视频、音频混用。

可在普通文生视频请求的 metadata 中加入 first_frame_image、last_frame_image,值为各自的 HTTPS 图片地址。等价的内容数组写法是两个 image_url 项,role 分别为 first_frame 和 last_frame。

python
import json
import os
from pathlib import Path

payload = {
    "model": "dreamina-seedance-2-0-fast-260128",
    "prompt": "镜头平滑地从首帧过渡到尾帧,保持主体一致",
    "seconds": "5",
    "resolution": "720p",
    "ratio": "16:9",
    "generate_audio": False,
    "watermark": False,
    "metadata": {
        "first_frame_image": os.environ["MXB_FIRST_FRAME_URL"],
        "last_frame_image": os.environ["MXB_LAST_FRAME_URL"],
    },
}
Path("seedance-frames.json").write_text(json.dumps(payload, ensure_ascii=False), encoding="utf-8")

查询进度与下载

把真实任务 ID 设置为 MXB_VIDEO_TASK_ID,随后只查询已有任务:

bash
curl --fail-with-body --max-time 30 \
  "https://api.manxiaobai.online/v1/videos/$MXB_VIDEO_TASK_ID" \
  -H "Authorization: Bearer $MXB_API_KEY"

读取顶层 status。本站兼容接口的主要状态为 queued、in_progress、completed、failed;排队和处理中继续查询,完成后下载,明确失败后检查 error 和使用日志。建议每 5 秒查询一次,并设置等待截止时间。客户端等待超时不表示服务端任务失败。

完成后的必要字段示意如下;这里省略时间等附加字段:

json
{
  "id": "task_EXAMPLE",
  "object": "video",
  "status": "completed",
  "progress": 100,
  "usage": {"completion_tokens": 40594, "total_tokens": 40594}
}

从本站带鉴权的视频内容接口下载,不需要解析其他结果 URL:

bash
curl --fail --max-time 180 \
  "https://api.manxiaobai.online/v1/videos/$MXB_VIDEO_TASK_ID/content" \
  -H "Authorization: Bearer $MXB_API_KEY" \
  --output seedance-result.mp4

结果为临时资源,完成后应及时下载到自己的存储。下载失败可以重试同一个内容接口,不必重新生成。不要把令牌附在 URL 查询参数中。

计费与退款

按视频 Token 计费,不是固定每次或每秒收费。以下为 2026-10-14 核对的本站基础额度 / 百万视频 Token;最终适用价格及账号折扣以模型广场和使用日志为准:

模型 无参考视频 有参考视频
Seedance 2.0 Mini 11.638 7.084
Seedance 2.0 Fast 24.42 14.52

只有图片或音频参考时,仍使用“无参考视频”档。480p 和 720p 当前使用同一单价,但分辨率、输出时长和参考视频会影响 Token 数量,不能由单价相同推断总费用相同。

提交时先预扣估算额度,成功后按有效用量结算,多退少补;明确失败或取消的任务退回预扣。以 usage.completion_tokens 为主要结算用量,缺失时可采用有效的 total_tokens;没有可信用量时保留预扣并需核查,不能视为免费。计价依据在受理时确定,后续价格调整不追溯在途任务。

例如 Mini 的 40,594 视频 Token,在无参考视频、适用倍率为 1 时,基础费用约为 40594 / 1000000 × 11.638 = 0.472433 额度;实际扣账会按站内额度精度取整。使用日志可能拆为预扣、补扣或退款记录,应按同一任务合计核对。

错误与重试

情况 处理方式
401 / 403 检查本站令牌是否有效、是否具有视频分组及模型权限
参数错误 / 400 检查时长、分辨率、素材上限;不要同时提供 prompt 和内容数组中的 text
素材读取或校验失败 确认 HTTPS 地址可公开访问,核对格式、时长、像素和大小
429 / 临时服务不可用 查询可以退避重试;提交应先确认是否已创建任务
提交连接中断或超时,未拿到 ID 先到任务/使用日志核查,或联系支持,避免重复付费生成
已有 ID,但查询超时或下载失败 保存 ID,继续查询或下载原任务,不重发生成请求
任务明确失败 检查错误与退款记录,修正问题后再决定是否创建新任务

反馈请提供本站任务 ID、模型、分组、请求时间及 request_id(如果有),不要提供完整 Key。没有统一取消或提交幂等承诺,自动重发 POST 可能产生多个任务和多笔费用。

可运行的 Python 客户端

下载 Python 3.10+ 示例。仅使用标准库,提交一次并保存任务状态,后续运行默认续查;只通过本站内容接口下载视频,不读取结果中的外部地址。命令使用的环境变量语法以 Bash 为例;Windows PowerShell 可使用 $env:MXB_API_KEY 设置同名环境变量。

bash
# 已设置服务端环境变量 MXB_API_KEY
python mxb-seedance.py --prompt "蓝色纸船在水面上缓缓漂浮"

# 再次执行自动从 seedance-task.json 续查,不重新生成
python mxb-seedance.py

# 新素材任务应指定一个新的状态文件
python mxb-seedance.py --request seedance-request.json --state reference-task.json

# 使用已知任务 ID,只查询与下载
python mxb-seedance.py --task "$MXB_VIDEO_TASK_ID" --output result.mp4

状态文件若标记为“提交结果未知”,客户端会停止并要求核查,不会自行补发。每个新任务使用独立 --state 文件;不要删除状态文件来处理超时。

python
"""Python 3.10+; submit once, resume a saved task, download from this site's API."""
import argparse
import json
import os
import re
import sys
import time
import urllib.error
import urllib.parse
import urllib.request
from pathlib import Path


class NoRedirect(urllib.request.HTTPRedirectHandler):
    def redirect_request(self, req, fp, code, msg, headers, newurl):
        return None  # Never forward the API key to another URL.


class ApiError(Exception):
    def __init__(self, status):
        self.status = status
        super().__init__(f"HTTP {status}; check the task and usage logs")


class VideoClient:
    def __init__(self, key, base="https://api.manxiaobai.online"):
        if base.rstrip("/") not in ("https://api.manxiaobai.online", "https://openpix.cc"):
            raise ValueError("Use one of the documented site origins")
        if not key or "\n" in key or "\r" in key:
            raise ValueError("Set MXB_API_KEY in your server environment")
        self.base, self.key = base.rstrip("/"), key
        self.opener = urllib.request.build_opener(NoRedirect())

    def open(self, path, payload=None, timeout=30):
        data = None if payload is None else json.dumps(payload).encode("utf-8")
        headers = {"Authorization": "Bearer " + self.key}
        if data is not None:
            headers["Content-Type"] = "application/json"
        request = urllib.request.Request(self.base + path, data=data, headers=headers)
        try:
            return self.opener.open(request, timeout=timeout)
        except urllib.error.HTTPError as error:
            error.close()
            raise ApiError(error.code) from None

    def json(self, path, payload=None):
        with self.open(path, payload, timeout=120 if payload is not None else 30) as response:
            result = json.load(response)
        if not isinstance(result, dict):
            raise ValueError("Unexpected API response; check logs before submitting again")
        return result

    @staticmethod
    def task_path(task):
        if not isinstance(task, str) or not re.fullmatch(r"[A-Za-z0-9_-]{1,200}", task):
            raise ValueError("Invalid task ID")
        return "/v1/videos/" + urllib.parse.quote(task, safe="")

    def wait(self, task, seconds):
        deadline = time.monotonic() + seconds
        while time.monotonic() < deadline:
            try:
                result = self.json(self.task_path(task))
            except (urllib.error.URLError, TimeoutError):
                time.sleep(5)
                continue
            except ApiError as error:
                if error.status not in (429, 500, 502, 503, 504):
                    raise
                time.sleep(5)
                continue
            if result.get("id") != task:
                raise ValueError("Response task ID mismatch; saved task remains available")
            status = str(result.get("status", "")).lower()
            if status in ("completed", "succeeded", "success"):
                return result
            if status in ("failed", "failure", "cancelled", "canceled", "expired"):
                raise ValueError("Task failed; inspect its error and refund in the site logs")
            if status not in ("queued", "pending", "submitted", "in_progress", "running", "processing"):
                raise ValueError("Unknown task state; keep the ID and check site logs")
            time.sleep(5)
        raise TimeoutError("Waiting stopped; run again with the saved state or --task to resume")

    def download(self, task, output):
        temporary = output.with_name(output.name + ".part")
        try:
            with self.open(self.task_path(task) + "/content", timeout=180) as response:
                if response.status != 200:
                    raise ValueError("Unexpected download response")
                first = response.read(32)
                if b"ftyp" not in first:
                    raise ValueError("The content response is not an MP4 file")
                with temporary.open("wb") as stream:
                    stream.write(first)
                    size = len(first)
                    while chunk := response.read(1024 * 1024):
                        size += len(chunk)
                        if size > 512 * 1024 * 1024:
                            raise ValueError("Download exceeds the example's 512 MiB limit")
                        stream.write(chunk)
            temporary.replace(output)
        except BaseException:
            temporary.unlink(missing_ok=True)
            raise


def get_task(client, state, payload):
    if state.exists():
        saved = json.loads(state.read_text(encoding="utf-8"))
        if saved.get("base") != client.base:
            raise ValueError("Saved origin differs; use the same --base as the original task")
        if not saved.get("task_id"):
            raise ValueError("Submission result unknown: check site logs before creating another task")
        client.task_path(saved["task_id"])
        return saved["task_id"]
    # Reserve the state file BEFORE sending POST. A crash must not trigger a duplicate.
    with state.open("x", encoding="utf-8") as stream:
        json.dump({"base": client.base, "status": "submission_unknown"}, stream)
        stream.flush()
        os.fsync(stream.fileno())
    result = client.json("/v1/videos", payload)
    task = result.get("id")
    client.task_path(task)
    print("Accepted task:", task, flush=True)
    temporary = state.with_name(state.name + ".tmp")
    temporary.write_text(json.dumps({"base": client.base, "task_id": task}), encoding="utf-8")
    temporary.replace(state)
    return task


def main():
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument("--base", default="https://api.manxiaobai.online")
    parser.add_argument("--prompt", default="A blue paper boat floating gently on calm water")
    parser.add_argument("--model", choices=["dreamina-seedance-2-0-mini-260615", "dreamina-seedance-2-0-fast-260128"], default="dreamina-seedance-2-0-mini-260615")
    parser.add_argument("--request", type=Path, help="UTF-8 JSON request file")
    parser.add_argument("--state", type=Path, default=Path("seedance-task.json"))
    parser.add_argument("--task", help="Existing task ID; never submits a generation")
    parser.add_argument("--output", type=Path, default=Path("seedance-result.mp4"))
    parser.add_argument("--wait", type=int, default=1800, help="Polling deadline in seconds")
    args = parser.parse_args()
    client = VideoClient(os.environ.get("MXB_API_KEY", ""), args.base)
    payload = None
    if not args.task and not args.state.exists():
        payload = json.loads(args.request.read_text(encoding="utf-8-sig")) if args.request else {
            "model": args.model, "prompt": args.prompt, "seconds": "4",
            "resolution": "480p", "ratio": "16:9", "generate_audio": False, "watermark": False,
        }
        if not isinstance(payload, dict):
            raise ValueError("Request file must contain a JSON object")
    task = args.task or get_task(client, args.state, payload)
    client.task_path(task)
    print("Querying task:", task, flush=True)
    client.wait(task, args.wait)
    client.download(task, args.output)
    print("Saved video:", args.output)


if __name__ == "__main__":
    try:
        main()
    except (OSError, ValueError, ApiError) as error:
        # Do not dump request headers, raw API responses or signed media addresses.
        print(f"Stopped ({type(error).__name__}). Keep the task state and check site logs; do not auto-resubmit.", file=sys.stderr)
        sys.exit(1)

验证范围

模型 ID、请求校验、任务状态和当前报价与本站部署核对;已有真实记录覆盖 Mini/Fast 的 4 秒 480p 文生视频,以及视频加音频混合参考并成功播放。文档示例另做本地参数和客户端流程验证,本次文档更新未新增付费生成。

720p、全部比例、素材数量上限、首尾帧及高级编辑参数按当前接口支持范围列出,未逐一做新的付费生成验证。精确输出像素和容器时长可能与名义档位略有差异。

漫小白 API 接入文档 · 2026-10-14 核对
模型权限与费用以当前模型广场及账单为准。反馈文档问题 ↗

复制 Markdown

浏览器未允许自动复制,内容已选中,可按 Ctrl+C / ⌘C 复制。