# 鲸鱼API 接口文档 · 鲸鱼日常

兼容 OpenAI 协议的视频生成接口。拿一把 API 密钥，就能用你熟悉的 SDK 直接调。

> 想要一份能离线看、能贴进自己文档、能喂给 AI 的？
> 下载 Markdown 版
> （.md 文件，点一下直接存到「下载」文件夹）
> 或在线打开看全文
> （不想存文件就走这个）
> 地址固定不变（/api-docs.md），内容与本页一致，换版不换链接。
> 手机上如果点了没反应，多半是被微信/QQ 的内置浏览器拦了 ——
> 点右上角「⋯」选「在浏览器中打开」再点一次。

不想写代码？可以直接用「字字动画」的两个插件（视频 + 出图），装上就能在软件里选型号出片 ——
见 第十二节，装法写在
飞书教程 里。
那份教程还包含鲸鱼AI 软件的完整用法，不用懂任何代码。

## 一、准备：拿密钥

1. 登录 控制台。账号由我们开通；充值请加微信 XYSM2015。
2. 左侧 API 密钥 → 添加密钥 → 复制 sk- 开头那串。
3. 按下面的例子调用即可。

密钥就是你的身份，谁拿到都能花你的余额。别发到群里，也别写进前端代码。

## 二、Base URL 与接口地址

所有接口都在这个地址下（带 /v1，用 OpenAI SDK 时填这个作 base_url）：

```bash
Base URL： https://xyjingyuapi.com/v1

POST /videos                  提交一个视频任务
GET  /videos/{id}             查进度（完成后这里会给 artifact.url）
GET  /videos/{id}/content     取成片（返回 mp4 字节流；要带 Authorization，路径没有扩展名）
GET  /models                  当前可用的型号列表
```

> 取片只有 /content 这一条路。查进度时响应里的
> artifact.url 就是它（相对路径，拼上 Base URL 即可）——
> 不要用任何其它地址去下载：上游的内部存储链接需要他们自己的密钥、还有时效，
> 直接访问会 404。详见第三小节末尾那张"四种写法一定会失败"。

> 成片在我们这里保留 24 小时（从生成完成时算）。24 小时内随时可以重复下载、
> 支持断点续传（Range 请求回 206），下载多少次都不再扣费。
> 超过 24 小时再取会返回 410（gone）：片子已经清掉了。
> 请在生产代码里"出片即取"—— 生成完成后立刻下载并转存到你自己的存储，
> 别把它当永久地址用。真要补取历史成片，把任务号发给运营，不要反复重试。

OpenAI SDK 里就这样填：

```bash
from openai import OpenAI
client = OpenAI(base_url="https://xyjingyuapi.com/v1", api_key="sk-你的密钥")
```

下面所有例子都写成完整地址（https://xyjingyuapi.com/v1/videos），
照着复制就能跑，不用自己拼。

出图（图片生成）也在本站：POST /v1/images/generations，
型号见第七节（gpt-image-2 / gpt-image-2.5 / banana-2.1），
用同一把密钥、同一个 Base URL，不用另开账号。

（主站 https://jingyumanju.com/v1 也仍然能出图，那是给软件用的。
两边各自独立：型号清单、售价、上下架都各管各的，接口站这边**直连上游**，
不经过主站 —— 所以别拿一边的型号名去另一边调用，可能那边没有这个型号。）

## 三、快速开始

三步：提交 → 查进度 → 取片。下面每步给一个能直接跑的最小例子，
完整可跑的 Python（含断点续传与完整性校验）在 附录 A。

### 1. 提交任务

```bash
curl https://xyjingyuapi.com/v1/videos \
  -H "Authorization: Bearer sk-你的密钥" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "nh-2.5-t-720p",
    "prompt": "镜头缓慢推近，人物微微转头看向镜头，光影自然流动",
    "size": "720x1280",
    "images": ["https://你的图床/人物.jpg"]
  }'
```

四个必填，缺一个就 400、不提交、不扣费：

| 字段 | 怎么填 | 缺了会回 |
| --- | --- | --- |
| model | 型号名原样抄第七节那张表（区分大小写，含中文括号） | model_not_found |
| prompt | 画面描述，越具体越好 | prompt_required |
| size | 像素 "720x1280" 或比例 "9:16"，两种写法都认、等价 | size_required |
| images | 参考图数组，公网 http(s)，最多 9 张（input_reference 传首帧，单张） | input_reference_required |

为什么 size 必须给：不传就由通道自己决定方向 ——
要竖屏却拿到横屏，而且全程没有提示。size 和 aspect_ratio 都传时
以 aspect_ratio 为准（见第六节）。
参考图怎么填、多张为什么都生效、URL 有什么要求 —— 见第四节。

成功返回任务 id，存下来 —— 查询和取片都靠它。丢了只能重新生成（＝再扣一次费）：

```bash
{
  "id": "Lmvovrjyscomr9",        // ★ 后面的查询、取片都用这个 id
  "object": "video",
  "created_at": 1769900000,
  "status": "queued",
  "model": "nh-2.5-t-720p",
  "progress": 0,
  "seconds": "15"
}
```

### 2. 查进度

```bash
curl https://xyjingyuapi.com/v1/videos/任务id \
  -H "Authorization: Bearer sk-你的密钥"
```

status 只有四种值，别自己加判断：

| status | 意思 | 你该做什么 |
| --- | --- | --- |
| queued | 排队中（含"通道忙、我们正在自动重发"） | 继续等，不要重新提交 |
| in_progress | 正在出片 | 继续等 |
| completed | 出好了 | 马上去取片（下一步） |
| failed | 终态，不会再好 | 看 error.message；钱已全额退回，改参数重试即可 |

- progress 是粗档，不是真实百分比：只有 0 / 50 / 100 三档 （上游只回状态、不回进度）。别拿它做进度条门槛，也别拿它判断"是不是卡住了"。
- 轮询 15~30 秒一次：15 秒的片子实测 5~20 分钟，30 秒的 10~40 分钟。 别一秒一次 —— 我们按次限流，你只会拿到一堆 429。
- 超时上限 200 分钟：到点还没终态，我们这边已经退款，你去查一次就会看到 failed。
- failed 时多一个 error，code 只有两种： generation_failed（素材或参数不合要求）、 video_cancelled（任务被取消并退款）。

> 不要重复提交：每调一次 POST /v1/videos 就是一条新任务、扣一次费。
> "觉得没动静"就再发一次，是最常见的多扣费原因 —— 先查进度，别猜。

### 3. 取成片：只有一条路

```bash
curl https://xyjingyuapi.com/v1/videos/任务id/content \
  -H "Authorization: Bearer sk-你的密钥" \
  -o 我的片子.mp4
```

返回的是 mp4 字节流本身（不是 JSON、也不是跳转）。地址就是进度响应里的
artifact.url —— 它是相对路径，拼上 Base URL 就能用。照它用，不要自己拼。

- 成片是 H.264（avc1） 的 mp4，浏览器、剪辑软件都能直接打开。
- 支持 Range 断点续传（响应头有 accept-ranges: bytes）：没下完就带 Range: bytes=<已下的字节数>- 接着下，不用从头再来。片子 4~10MB，慢网下这点很重要。
- 保留 24 小时（从生成完成算）：24 小时内重复下载不再扣费；超过返回 410 gone。 ⇒ 出片即取，转存到你自己的存储，别把它当永久地址。

下完必须验完整性 —— 这是最容易忽略、也最难查的一类问题。
只判断"写入了超过 1KB"，一条 8MB 的片子断在半截（比如只下了 2.6MB）也会被当成成功：
宿主打开的是一片没有索引的 mp4，表现就是"打不开 / 拉不进剪辑软件"，
而日志和 API 全都写着成功。三条校验，任何一条不过都不要当成成功：

| 校验 | 怎么做 | 不过说明什么 |
| --- | --- | --- |
| 字节数 | 把落盘大小和响应头 Content-Length 比；续传时按「已有 + 本次应得」算。 | 没下完（网络断了 / 客户端超时）。接着下即可，别重新生成。 |
| 是不是视频 | 前 32 字节里要有 ftyp（mp4 容器魔数）。 | 拿到的是错误页 / JSON（常见于把 4xx 的响应体当文件写了）。 |
| 能不能播 | 文件里要有 moov box（尺寸索引）。 | 结构不完整，播放器打不开。我们的片子 moov 在文件最尾部 （实测 8.1MB 的片子，moov 在 99.8% 处）⇒ 任何半截档都不会有它，所以这一条最能抓住"下了一半"。 |

特别注意：服务端没给 Content-Length 时
（分块传输、或靠关连接界定正文），提前断开很多 HTTP 库不会报错，
会安静地把半截文件交给你 —— 这时只有 moov 那一条能拦住。

现成写法见 附录 A。
下载失败不要盲目重新生成：片子已经生成好了（钱也扣了），重新生成是再扣一次。

下面这四种写法一定会失败（线上每天都有客户踩，所以单独列出来）：

| 错在哪 | 会看到什么 | 为什么 |
| --- | --- | --- |
| 拿响应里任何看起来像直链的地址去下载 | 404 或 403，或过几小时突然失效 | 那是上游内部的存储地址：需要他们的密钥、有时效、而且是别人家的域名。我们不会把它给你。 |
| 自己拼 /v1/videos/{id}/content.mp4 | 404 | 取片口就是 /content，没有扩展名。文件名靠下载时的 Content-Disposition 给（浏览器会存成 mp4）。 |
| 看到 completed 就立刻取，取一次 409 就报错退出 | 409 video_not_ready | 产物从"生成完"到"能下载"之间还有几秒同步时间。等 3~5 秒再取一次即可，不用重新生成。 |
| 用 GET 取片时不带 Authorization | 401 | 取片和提交一样要鉴权（否则知道 id 的人就能下别人的片子）。 |

## 四、参考图：所有视频型号都必须带（最多 9 张）

> 更正（2026-10-30）：这一节以前写「每条任务只收 1 张参考图」，
> 理由是 input_reference 在协议里是单个字符串。
> 那个字段确实只收一个串 —— 但那是字段的限制，不是接口的限制。
> 多图走 images 数组，这个接口一直就收，上限 9 张，
> 而且全部会透到模型（实测：传 4 张，上游收到的就是 4 张）。
> 以前按"只收 1 张"写，客户只传了首帧、丢了几张角色参考，出片对不上还以为是模型不行。
> 保守写小也是撒谎，所以这里改正。

至少 1 张，最多 9 张，值是公网能访问的 http(s) 图片地址。两种字段各管一件事：

| 字段 | 类型 | 能带几张 | 用途 |
| --- | --- | --- | --- |
| images | 数组 | 最多 9 张 | 推荐。锁人物 + 场景 + 表情等多张参考都用它，全部生效 |
| input_reference | 单个字符串 | 1 张 | OpenAI Sora 标准写法，放首帧。老代码不改也能用 |
| image / image_url | 字符串 | 1 张 | 兼容老写法与第三方 SDK |

顺序有意义：模型按你给的顺序取参考，越靠前权重越高。首帧（这一镜从哪开始）
建议放第一个。用 images 时第一个元素就是首帧。

> 不传会直接 400（input_reference_required），不会扣费。
> 我们卖的是参考生视频，没有纯文生的型号 —— 不带图就没有画面依据。
> 不要直接传 base64 / data: URI（input_reference_not_url）。
> 硬收会让任务在扣费后才失败 —— 上游拉不到图。
> 本地图片请先用我们的上传接口换成 URL（见下面「4.1 上传本地图片」）。
> 以前这里写着"我们没有转存服务"，那是当时的实情，现在不必再自己挂图床了 ——
> 尤其是别往公共图床上传客户素材，那种服务随时会挂、也可能被别人看到。
> 单张参考图上限 16MB，而且不用你自己压：我们在提交前会自动把每张压到
> 长边 1920、约 100~250KB（JPEG 高质量，肉眼无差），再交给模型。
> 参考图是"定画面"用的，压缩不影响出片效果，所以你直接把原图给我们就行。
> 超过 16MB 会被拒收（不扣费），并告诉你是第几张。
> 链接要能被外网直接读到：不能是登录后才可见的地址、分享页或网盘预览页。

顺便说一句参考音频：如果你要带音频参考，只支持 WAV / MP3，且每条不超过 5 秒。
音频是辅助项，出问题我们会在提交时自动去掉重试一次，不会连累整条片子。

三种写法都认：

```bash
// ① 多张（推荐）。images 是数组，最多 9 张，全部生效
"images": [
  "https://你的图床/首帧.jpg",
  "https://你的图床/人物B.jpg",
  "https://你的图床/场景.jpg"
]

// ② 只带首帧（OpenAI Sora 标准写法）
"input_reference": "https://你的图床/首帧.jpg"

// ③ 兼容老写法 / 第三方 SDK
"image": "https://…/a.jpg"

// ①+② 一起给也没问题：两张都算，同一张重复给会自动去重
```

> 为什么 input_reference 不能传数组：这个字段在协议里定义成
> 单个字符串，传数组会直接 400（input_reference_not_url）。
> 要传多张用 images —— 别因为一个字段只收一张，就以为整个接口只收一张。
> 超过 9 张怎么办：只收前 9 张。要精确控制顺序，把最要紧的放前面。
> 不用自己压图，我们压：每张参考图在提交前会被自动压到
> 长边 1920、约 100~250KB（JPEG 高质量重编码，长边小于 1920 的原样不放大）。
> 带透明通道的 PNG、动图（GIF/WebP）原样转发，不会被转成 JPEG（那样透明会变黑底）。
> 实测：9 张各 1.26MB（共 11.4MB）压完每张约 105KB，整个提交 3~7 秒（多次实测 2.6 / 4.1 / 6.7 秒；不改之前是 19.5 秒，而且 15 秒就被掐断）。
> 仍然建议客户端超时给够：60 秒以上。慢的从来不是模型，是
> "把这些图上传到我们这儿"这一段（走你自己的上行带宽）；图片越大越久，
> 而且这是在你的网络里发生的，我们没法替你加速。
> 我们建议：一个分镜一般 2~4 张就够（首帧 + 主要角色 + 关键场景）。
> 参考图越多，模型越容易"折中"，反而更容易偏。

越接近你想要的画面（构图、人物、光线）出片越准。参考图必须是图片，不要传视频链接。

### 4.1 上传本地图片（本地文件 → 公网 URL）

如果你手里是本地文件（软件截图、手机照片），先调一次上传，拿到 URL 再提交视频。

```bash
POST https://xyjingyuapi.com/v1/uploads
Authorization: Bearer sk-你的Key
Content-Type: application/json

{
  "filename": "ref.png",
  "content_type": "image/png",
  "data": "<图片字节的 base64>"
}

→ 200 {"object":"file","url":"https://…/ref.png","size":123456}
```

- 只收图片（PNG / JPG / WEBP，按文件内容的真实类型判，不看文件名）。其它类型 415。
- 单个文件上限 16MB（提交前我们会自动压到长边 1920，压完一般 100~250KB，不影响出片）。超了回 413 —— 我们不收下再被上游拒，那样你会白等一趟。
- data 可以带 data:image/png;base64, 前缀，我们会自动剥掉。
- 会把文件头（魔数）验一遍：内容其实不是图片就回 415（防止把文本/半截文件当图片传上去，最后出片才失败）。
- 上传不收费，也不消耗额度；但 Key 必须有效（已吊销的 Key 用不了）。
- 拿到的 url 放进 images 数组（或 input_reference）即可； 同一张图重复上传只占一份（按内容哈希存）。
- 不接受音频：这个接口的视频不收参考音频，传上去也不会被使用。

多张参考图直接传就行。 上面这个上传接口只是"把本地图换成 URL"，
拿到 URL 后放进 images 数组，最多 9 张、全部生效（见第四节）。

## 五、seconds 怎么填：看型号

每个型号的时长是型号本身定好的，不需要你选。但要不要传 seconds，
取决于这个型号怎么计费 —— 下表「seconds」一列写得很清楚，照它来就行。

- 固定时长 + 按条计费（大多数）：不用传。传了也必须等于它的固定秒数 （比如固定 15 秒就传 "15"），传别的值会 400 fixed_duration。
- 按秒计费：必须传，不传会 400 并告诉你该传多少。范围见下表。

为什么要管得这么严：早期版本的接口会把 seconds 当成计价倍率，
客户传对了秒数反而被多收好几倍，而且接口返回 200、片子照出，月底对账才发现。
现在一律按型号的固定时长生成，秒数只用于确认，不会因为传了它多收你钱。

## 六、画面比例 ≠ 分辨率档位

这两个经常被混在一起问，其实是两件事：

| 你说的 | 接口里是 | 怎么给 | 能不能改 |
| --- | --- | --- | --- |
| 横屏 / 竖屏 | 画面比例（16:9 / 9:16） | size（像素）或 aspect_ratio（推荐，显式） | 能：同一个型号横竖都行 |
| 清楚到什么程度 | 分辨率档位（720p / 1080p） | 写在型号名里：型号名带 1080p 就是 1080p，带 720p 就是 720p | 不能：换档位＝换型号＝换价格 |

为什么档位不能用参数改：价格是按型号定的。如果允许传一个
resolution=720p 去跑 1080p 的型号，那就成了"按 1080p 收钱给 720p 的片"
（或者反过来白送），两边都说不清。所以档位只认型号。

像素与比例的对应（这几组一定对得上，别自己拼数字）：

- 720p 型号：16:9 = 1280x720，9:16 = 720x1280
- 1080p 型号：16:9 = 1792x1024，9:16 = 1024x1792

两个都给时以 aspect_ratio 为准。只给 size 也能用
（从宽高比推），但显式给不会推错 —— 竖屏需求被推成横屏，客户往往出了片才发现。

## 七、型号与价格

型号名请原样复制（区分大小写，也含中文和括号）。表里的名字与软件里、
后台里、教程里显示的是同一个 —— 换行、少一个括号都会对不上。

> - 下表里的名字（如 minimax-h3-720p）直接可用 —— 和软件里、后台里、教程里显示的一致；
> - GET /v1/models 返回的 id 就是同一个字符串 （本站在 /v1/models 与下单接口之间不做改名），所以两者永远一致。 但我们可能给某个型号换上游通道，那时可用性由我们负责、名字不变 —— 所以照下表抄最稳，别自己拼。

| 型号 | 时长 | seconds | 价格 | 计费 |
| --- | --- | --- | --- | --- |
| c-sd-2.0-1080p（巨慢！） | 固定 15 秒 | 传 15（不影响计费） | ¥2.00 | 按次 |
| nh-2.5-t-720p | 固定 30 秒 | 传 30（不影响计费） | ¥8.00 | 按次 |
| a-2.5-bd-t-720p | 固定 30 秒 | 传 30（不影响计费） | ¥7.50 | 按次 |
| a-2.5-cd-t-720p | 固定 30 秒 | 传 30（不影响计费） | ¥6.50 | 按次 |
| xf-2.5-720p | 固定 30 秒 | 传 30（不影响计费） | ¥6.50 | 按次 |
| j-2.5-t-720p | 固定 30 秒 | 传 30（不影响计费） | ¥7.00 | 按次 |
| S-2.5-t-720p | 固定 30 秒 | 传 30（不影响计费） | ¥7.00 | 按次 |
| Seedance 2.0-720P-s | 固定 15 秒 | 传 15（不影响计费） | ¥2.50 | 按次 |
| Seedance 2.5满血满参720p | 固定 30 秒 | 传 30（不影响计费） | ¥6.50 | 按次 |
| minimax-h3-720p | 固定 15 秒 | 传 15（不影响计费） | ¥0.80 | 按次 |
| 高质量sd2.5 | 固定 30 秒 | 传 30（不影响计费） | ¥7.00 | 按次 |
| 特价sd2.5 | 固定 30 秒 | 传 30（不影响计费） | ¥6.50 | 按次 |
| DB-SD-2.0 | 固定 15 秒 | 传 15（不影响计费） | ¥2.50 | 按次 |
| kling-v4-720-30s | 固定 30 秒 | 传 30（不影响计费） | ¥7.00 | 按次 |
| re-sd-2.0-720 | 固定 15 秒 | 传 15（不影响计费） | ¥2.00 | 按次 |
| 86-sd2.0-pro | 固定 15 秒 | 传 15（不影响计费） | ¥2.50 | 按次 |

控制台余额直接以元显示（充 10 元就是 10 元）。上表的「点」是软件里同一套计价单位，
10 点 = 1 元。每次调用按上表扣一次；失败会自动退回，不用找我们。

这张表跟着线上在售数据自动更新（每 15 分钟一次）。也可以随时拉接口，拿到的就是当前在售清单：

```bash
curl https://xyjingyuapi.com/v1/models \
  -H "Authorization: Bearer sk-你的密钥"
```

> 下架的型号不会出现；临时维护中的会出现，但带 maint: true ——
> 那种暂时调不通（提交会明确报错并自动退款），不是我们悄悄吞掉你的请求。
> 所以：先看 maint 再提交，比调完再猜省事。上表和接口是同一份数据，
> 都跟着线上在售每 15 分钟刷新一次。

### 图片型号（出图，同步返回）

出图走 POST /v1/images/generations，同步返回
（response_format 默认给 URL，也可以要 b64_json）。
必须传 size（像素或比例都行），不传会被拒 —— 我们不替你猜画幅。

| 型号 | 能力 | 单价 | 说明 |
| --- | --- | --- | --- |
| gpt-image-2 | 文生图 | ¥0.20 / 张 | 快、稳，日常素材图（约 1 分钟） |
| gpt-image-2.5 | 文生图 | ¥0.25 / 张 | 细节更好，版本更新 |
| banana-2.1 | 文生图 | ¥0.40 / 张 | 风格化更强，适合封面 / 海报 |

> 这几个型号都支持「图生图」（按你给的参考图改）。
> 传 image（一张公网图片地址）或上传文件，我们就会按那张图改，
> 而不是无视它另画一张。
> 一次只收 1 张：多张会明确回 400（"一次只能带 1 张参考图"），
> 不会悄悄只取第一张就当成你要的样子。要改多张就分几次调用，每次各扣一次。
> 实测举例：给一张「深色背景的角色立绘」+ 提示词「保持角色不变，背景换纯白」，
> 出来的就是同一个角色、白色背景 —— 参考图真的起作用了。

图片按张计费：n 只能是 1，要几张就调几次。
失败自动退回，和视频一个口径。

### 文本型号（对话 / 写作，按 token 计费）

走标准 OpenAI 对话接口 POST /v1/chat/completions，同步返回，
你原来调 OpenAI 的代码改个 base_url 就能用。

> 它的计费方式和视频、图片都不一样：视频按条、图片按张，
> 而文本按实际用量（输入 token + 输出 token）扣。
> 问一句话几分钱，写一万字也才几毛 —— 不会出现"一句话扣一条片子的钱"。

| 型号 | 输入价 | 输出价 | 适合 |
| --- | --- | --- | --- |
| tt-6-luna | ¥2 / 百万 token | ¥8 / 百万 token |  |
| deepseek-v4.1-flash | ¥3 / 百万 token | ¥8 / 百万 token |  |
| tt-5.6-luna | ¥3 / 百万 token | ¥9 / 百万 token |  |
| gem-3.8-flash | ¥5 / 百万 token | ¥15 / 百万 token |  |

输出比输入贵（各家都这样），因为生成的每个字都要算一遍。
上面是"每百万 token"的单价 —— 一篇文章通常几千 token，实际扣的是分位。

```bash
curl https://xyjingyuapi.com/v1/chat/completions \\
  -H "Authorization: Bearer sk-你的密钥" \\
  -H "Content-Type: application/json" \\
  -d '{"model":"tt-5.2",
       "messages":[{"role":"user","content":"把这句话改得更口语：此事尚需进一步商讨。"}]}'
```

```bash
{
  "id": "chatcmpl-...",
  "object": "chat.completion",
  "choices": [ { "message": { "role": "assistant", "content": "这事儿还得再商量商量。" } } ],
  "usage": { "prompt_tokens": 28, "completion_tokens": 12, "total_tokens": 40 }
}
```

扣费就是照 usage 算的：
prompt_tokens × 输入价 + completion_tokens × 输出价，再除以一百万。
上面这条按 tt-5.2 算约 0.00008 元（不到一分钱）。

想边生成边看（长文更明显）：加 "stream": true 走 SSE 流。
流式同样按实际用量结算，不多收。

文本不占"次数"：余额就是钱 —— 视频扣 2.5 元、文本扣 0.00008 元，
都从同一份余额里扣，互不影响。

## 八、每个型号能吃多少素材

准备素材前先看这张表：参考图 / 参考视频 / 参考音频各能吃几段、
描述词最多写多少字、出片分辨率。数字取自我们服务端的真实上限
（不是模型厂商文档里那个更宽松的数字）—— 超了会被拒绝或裁掉，按这张表准备最省事。

| 型号 | 分辨率 | 上游版本 | 参考图 | 参考视频 | 参考音频 | 提示词上限 |
| --- | --- | --- | --- | --- | --- | --- |
| c-sd-2.0-1080p（巨慢！） | 1080p | — | 9 | 0 | 0 | 不限 |
| nh-2.5-t-720p | 720 | — | 9 | 0 | 0 | 不限 |
| a-2.5-bd-t-720p | 720p | — | 9 | 0 | 0 | 不限 |
| a-2.5-cd-t-720p | 720 | — | 9 | 0 | 0 | 不限 |
| xf-2.5-720p | 720 | — | 9 | 0 | 0 | 不限 |
| j-2.5-t-720p | 720 | — | 9 | 0 | 0 | 不限 |
| S-2.5-t-720p | 720 | — | 9 | 0 | 0 | 不限 |
| Seedance 2.0-720P-s | 720P | — | 9 | 0 | 0 | 不限 |
| Seedance 2.5满血满参720p | 720P | — | 9 | 0 | 0 | 不限 |
| minimax-h3-720p | 720P | — | 9 | 0 | 0 | 不限 |
| 高质量sd2.5 | 720 | — | 9 | 0 | 0 | 不限 |
| 特价sd2.5 | 720 | — | 9 | 0 | 0 | 不限 |
| DB-SD-2.0 | — | — | 9 | 0 | 0 | 不限 |
| kling-v4-720-30s | — | — | 9 | 0 | 0 | 不限 |
| re-sd-2.0-720 | — | — | 9 | 0 | 0 | 不限 |
| 86-sd2.0-pro | — | — | 9 | 0 | 0 | 不限 |

怎么读这张表

「上游版本」是同一款模型的第几代（900 / 903 / 933 这样）。同一个名字在上游有好几代在跑，画质、稳定性、成本都不同 —— 客户按型号名看不出区别，所以我们把这代标出来。它不是分辨率：实测同一个 903，在一个通道出 1920×1080、在另一个通道出 1256×720，出片尺寸以「分辨率」那列和实际成片为准。

参考图这一列写的是「本接口能传几张」，不是型号原生上限。
视频型号现在写 9（images 数组，见第四节）。参考视频 / 参考音频 写 0，
不是"模型不支持"，是本接口还没有这两个字段。

★ 2026-10-30 更正：这里以前写 1，理由是
input_reference 只能一个字符串 —— 那是字段的限制，不是接口的限制。
多图走 images，一直就是 9 张。

提示词上限写不限的型号，我们这边不截断，但建议控制在 2000 字以内：
描述越长，模型越容易只抓住其中一部分；个别上游对超长描述会直接报错或静默截断。
真遇到描述词相关的报错，先把描述砍到 1000 字以内再试。

参考图 0 的型号：这条型号我们当前不会把你的参考图发出去（拿不到上游的参考能力），
所以写 0 —— 传了也不会生效，别以为"人物锁住了"。

这张表跟着线上下发的能力位每 15 分钟刷新一次，与 GET /v1/models
的 ref_max / prompt_max / resolution 是同一份数据。

## 九、常见报错

| code | 意思 | 怎么办 |
| --- | --- | --- |
| input_reference_required | 没传参考图 | 加 input_reference（见第四节） |
| input_reference_not_url | 参考图不是公网地址 | 传 https://…，不要 base64 |
| fixed_duration | seconds 与型号的固定时长不符 | 删掉 seconds，或按上表填 |
| seconds_out_of_range | 秒数超出该型号范围 | 按上表范围传 |
| model_not_found | 型号名拼错或已下架 | 对照上表，注意大小写 |
| size_required | 没传 size | 加 "size":"720x1280"（像素）或 "9:16"（比例）。不传不扣费 |
| size_ambiguous | size 只写了档位名（如 "720p"）—— 没有方向 | 档位名分不出横竖屏，改写成像素或比例 |
| size_not_supported | 这个型号不认这种 size 写法 | 改用像素（1280x720 / 720x1280）或比例（16:9 / 9:16） |
| insufficient_quota | 余额不足 | 充值是人工的，加微信 XYSM2015 |
| authentication_error | 密钥不对或已删除 | 在控制台重新建一把 |
| generation_failed | 出片失败（出现在查进度的 error.code 里，status 同时是 failed） | 已自动全额退款，按 error.message 改一改再试 |
| video_failed | 取片时这条任务已经失败，没有内容可取（409） | 同上；先看一次 GET /v1/videos/{id} 的 error.message |
| video_cancelled | 这条任务被取消（status 也是 failed） | 已全额退款；不是你的参数问题，直接重发一次 |
| video_not_ready | 片子还在生成，或刚完成还在同步（取片回 409） | 等 3~5 秒再取一次；不要重新生成 |
| video_not_found | 这个 id 不是你这把密钥下的任务（或写错了） | 用提交时返回的 id；我们不区分"不存在"和"不是你的" |

报错都是这个形状，message 直接写清怎么改：

```bash
{
  "code": "input_reference_required",
  "message": "This model requires at least one reference image. Pass it as "input_reference": "https://…/image.jpg" (public http(s) URL)."
}
```

## 十、账单与答疑

- 控制台 → 使用日志：每次调用都有记录（哪个型号、扣多少、成功还是失败）。
- 余额在钱包页查看，充值请加微信 XYSM2015。
- 失败的调用自动全额退回，不需要申请；failed 之后再查一次就能看到余额回来。

用了上面还搞不定，把三样发我，能马上定位：

1. 任务 id
2. 型号名
3. 报错原文

微信：XYSM2015（加时说一句"API"，通过得快些）

## 十一、出图（图片生成）

> 本节有两种接入，别混：
> · 本站（推荐）：POST https://xyjingyuapi.com/v1/images/generations，
> 用本站的 sk- 密钥，型号 gpt-image-2 /
> gpt-image-2.5 / banana-2.1。
> 返回的是图片外链（data[0].url），直链可直接下载、外链有时效。
> · 主站：https://jingyumanju.com/v1/images/generations，
> 用 ak_ 开头的密钥（给软件用的那条链路）。
> 返回的是 base64（data[0].b64_json）。
> 两边的型号名不是同一套，别互相套用。

协议与 OpenAI 的图片接口一致，同步返回（等出图完成才回包，通常 5~60 秒）：

```bash
curl https://xyjingyuapi.com/v1/images/generations \
  -H "Authorization: Bearer sk-你的本站密钥" \
  -H "Content-Type: application/json" \
  -d '{"model":"gpt-image-2","prompt":"雨夜的旧书店，暖黄灯光，电影感","size":"1024x1024"}'
```

本站返回的是外链（不是 base64）：

```bash
{
  "created": 1793175091,
  "data": [ { "url": "https://…/20261028161158_18e2a34522c0e7e24bac.png" } ]
}
```

主站那条链路返回 base64（data[0].b64_json），例子见本节末尾的老示例——按你要接哪边选。

```bash
curl https://jingyumanju.com/v1/images/generations   -H "Authorization: Bearer ak_你的密钥"   -H "Content-Type: application/json"   -d '{"model":"tt-image-2","prompt":"雨夜的旧书店，暖黄灯光，电影感","size":"16:9"}'
```

返回的是 base64 图片数据（不回外链，省得你还要再发一次带鉴权的下载请求）：

```bash
{
  "created": 1769900000,
  "data": [ { "b64_json": "iVBORw0KGgoAAAANSUhEUg..." } ],
  "id": "Lmvovrjyscomr9"
}
```

三个参数要点（两边都适用）：

- size 必填。写比例（16:9 / 9:16 / 1:1）或像素 （1920x1088）都行。不传会 400，我们不会偷偷给你一张默认画幅的图。
- n 只收 1（一次一张，一次一扣）。要多张就多调几次。
- image / images：参考图（图生图），两条路都行：  ① 给公网 http(s) 地址（本地文件先用 POST /v1/uploads 换地址）；  ② 直接上传文件（multipart，字段名 image）。  我们就把这张图真的交给模型按它改，而不是无视它另画一张。 一次只收 1 张 —— 多张会明确回 400（不会只取第一张了事）。

可用型号与价格随时变，以 GET /v1/models 为准
（看 supported_endpoint_types 里带 image-generation 的那几条）。
这几个图片型号都支持图生图。

### 10.1 出图会遇到的报错

| code | 意思 | 怎么办 |
| --- | --- | --- |
| prompt_required | 没传 prompt | 把画面描述放进去 |
| model_not_found | 型号名不对或已下架 | 先 GET /v1/models 看 capability:"image" 的 id |
| size_required | size 没传，或写法不接受 | 传像素（1920x1088）或比例（16:9）。不传不扣费 |
| n_not_supported | n 不是 1 | 一次一张；要多张就多调几次（每次扣一次） |
| ref_not_supported | 这张参考图我们没法用（不是图片、或地址取不到） | 换一张公网能打开的图片地址，或改成直接上传文件 |
| ref_too_many | 一次带了多张参考图 | 图生图一次只收 1 张；要改多张就分几次调用（每次各扣一次） |
| image_failed | 上游没出成 | 已自动全额退款，改改提示词重试 |
| insufficient_quota | 余额不足 | 充值请加微信 XYSM2015 |

## 十二、不想写代码？用「字字动画」插件

如果你只是想在剪辑/动画软件里生成画面，不用自己调接口 —— 我们把它做成了
字字动画的两个插件（视频 + 出图），装上就能在字字里选型号、填描述、直接出片。

查看插件说明 / 下载插件包 →

- 压缩包里是两个插件文件夹（video_plugin_jingyu + image_plugin_jingyu） 和一份说明书。把这两个文件夹分别复制到字字动画安装目录下的 _internal\plugins\video_plugins\ 与 _internal\plugins\image_plugins\， 然后重启字字动画。（没有安装程序 —— 不用双击任何 exe，就是复制这两个文件夹。）
- 当前版本 0.17（视频插件、出图插件同版本）。装过 0.16 的请换一次： 修「出图插件型号名写错」—— 0.16 那一版填的是主站型号名（tt-image-*）， 而这个插件连的是本站，站里没有这些型号，选了会提交失败；现在换成本站真实的 gpt-image-2 / gpt-image-2.5 / banana-2.1 （没填 Key 时下拉的兜底表也对得上了）。 同时把一件事说清：出图是「文生图」，暂不支持参考图 —— 分镜带了参考图时日志会明确说"已忽略 N 张"，不会假装用上了； 要按图锁人物/场景请用视频型号（那些都收参考图）。  此前 0.15： 修「选完型号没保存」（假选中）：认不出线上型号时不再把配置改成第一个；下拉始终显示你选的那个。 此前 0.14：报错说人话（英文 JSON 转成中文并给改法），并修掉「空的参考视频字段被上游判为不支持的字段」。 此前 0.13 的说明：：提交前用客户自己的 Key 核对在售清单，不在清单里就拦下（不提交、不扣费）并给出改法；「提交型号」会写进运行日志便于查证。下拉也补上了型号名（线上清单原本没有显示名，只有内部代号）。0.12 的上传前压图仍在内。 此前 0.12 的说明：真机实测 4.2MB 的图 41.9 秒 → 2.3 秒（省 95%）。真因是体积（参考图常是 1.5~4MB 的 PNG，而上传只收 base64，再涨 1/3）。现在上传前本地压到长边 1280 / JPEG q85；透明图与动图不碰（转 JPEG 会压成黑底）、压不小就发原图、压缩组件不可用时自动退回原图。另外 0.11 修了「大图并行把上行挤断」（按体积自适应）、0.10 修了「选什么型号都生成同一个型号」。0.10 那三件事 —— ① 「选什么型号都生成同一个型号」：面板拉完线上清单会重建下拉，而重建只改了界面显示、 没把选择写回配置 ⇒ 界面显示你选的、提交用的是旧型号。现在重建后会回写，看到什么就发什么。 ② 参考图上传：以前一张一张排队传且完全不重试，6~9 张图既慢、又会被一次连接抖动整批打断 （报 Connection aborted / SSLError EOF）。现在 3 张并行、连接类错误重试 2 次、 同一张图传过一次不再重复传；顺序仍按入参，@图片1 编号不会乱。 ③ 上传彻底失败时给中文人话，不再甩异常栈。 你的 Key 和设置不受影响，装上 0.10 会自动读回来。
- 插件里的型号是联网拉线上清单的 —— 我们上下架型号不用你重装插件。
- 插件只连本站（xyjingyuapi.com），用 sk- 开头的密钥。 主站（jingyumanju.com + ak_ 密钥）是软件自己用的那条链路， 两条互不影响，密钥也不通用。
- 视频插件能用的型号＝面板里拉到的那份线上清单（型号名后面带「秒数 · 价格」）。这里不再写死数量：上下架随时会变，写死的数字一定会过期。清单里没有某个型号只有两种可能：它下架了，或那条供货通道被临时停用了。
- 地址永久不变，换版本也不用重新找链接： https://xyjingyuapi.com/download/plugin-pack

插件和接口是同一套东西：插件用的是本文档这些 /v1/* 接口，
参数完全一致。所以插件里遇到问题，把「运行日志」里的请求体拿出来，
对照本文档就能判断是谁的问题。

插件怎么装、怎么填 Key、每个选项什么意思 —— 写在飞书教程里
（「在「字字动画」里用插件」这一章），比这里详细：
打开飞书教程。

## 附录 A：完整 Python 示例（提交 → 轮询 → 取片）

照抄就能跑。注意最后一步用的是响应里给的 artifact.url，
并且落盘后要验完整性（字节数 / ftyp / moov）——
只判断"写了多少字节"会把半截片子当成功，播放器却打不开。

```bash
import os, time, requests

BASE = "https://xyjingyuapi.com"          # 不含 /v1
API = BASE + "/v1"
H = {"Authorization": "Bearer sk-你的密钥"}

r = requests.post(API + "/videos", headers=H, json={
    "model": "nh-2.5-t-720p",
    "prompt": "镜头缓慢推近，人物微微转头看向镜头",
    "size": "720x1280",          # ★ 必填：竖屏 9:16。不传会 400（size_required）
    "aspect_ratio": "9:16",      # ★ 建议：显式给比例，服务端优先用它（和 size 说的是同一件事）
    "images": ["https://你的图床/ref.jpg"],   # ★ 必带；首帧用 input_reference
})
r.raise_for_status()
vid = r.json()["id"]

while True:
    s = requests.get(API + "/videos/" + vid, headers=H).json()
    print(s["status"], s.get("progress"))
    if s["status"] in ("completed", "failed"):
        break
    time.sleep(15)               # 15~30 秒一次就够，别更密

if s["status"] != "completed":
    print("失败原因：", s["error"]["message"], "（钱已退回）")
    raise SystemExit(1)

# ★ 用响应里给的地址（相对路径），不要自己拼、也不要去找任何"直链"
url = BASE + s["artifact"]["url"]
path = "片子.mp4"
for _try in range(3):
    have = os.path.getsize(path) if os.path.exists(path) else 0
    hdrs = dict(H)
    if have:
        hdrs["Range"] = "bytes=%d-" % have            # 断点续传
    with requests.get(url, headers=hdrs, stream=True, timeout=180) as resp:
        if resp.status_code == 416:                   # 已经下全了
            break
        resp.raise_for_status()
        # 200 = 整份重给 ⇒ 必须从头覆盖；206 = 续传 ⇒ 接着写
        append = (resp.status_code == 206 and have > 0)
        total = resp.headers.get("Content-Length")
        with open(path, "ab" if append else "wb") as f:
            for chunk in resp.iter_content(256 * 1024):
                if chunk:
                    f.write(chunk)
    size = os.path.getsize(path)
    ok_size = (not total) or size == (have if append else 0) + int(total)
    blob = open(path, "rb").read()
    if ok_size and b"ftyp" in blob[:32] and b"moov" in blob:
        break                                          # 三条都过才算下好
    print("这次没下完（%d 字节），接着下…" % size)
else:
    raise RuntimeError("下载不完整 —— 别重新生成（会再扣一次），到控制台「日志」里手动下载")

print("已保存，字节数 =", os.path.getsize(path))
```

需要"下成片"这个单独函数的话，把上面 for _try in range(3):
那一段抽出来就是 —— 不要另写一份，两份必然漂移。
