# 漫小白 API 完整接入文档

在线入口：https://openpix.cc/docs/

更新：2026-11-02；含 28 个章节。基于当前目录与接口契约核对，本轮核对中质量分组的4K文生图；其他能力以各章已验证范围为准。

## 文档首页

按本站实际接口完成模型选择、鉴权、提交、任务查询和结果下载。图片、视频、语言接口分别说明，避免把不同型号的参数和计费方式混用。

**更新：2026-11-01。** 新增 MJ、Seedream、GPT 图片分组规则、香蕉、Grok 图片、AS Gemini 工具与视频理解、语言阶梯费用七个专章；保留当前 7 个视频型号的专属接入说明。模型权限、可用性和价格随服务调整，以[模型广场](https://openpix.cc/pricing)、当前 Key 的模型目录和实际账单为准。

### 按你的任务开始

| 目标 | 从这里阅读 |
|---|---|
| 第一次使用本站 API | [快速上手](https://openpix.cc/docs/quickstart.html) → [鉴权与地址](https://openpix.cc/docs/authentication.html) |
| 选一个视频模型 | [视频型号对照与最小请求](https://openpix.cc/docs/video.html) |
| Seedance Mini / Fast，按视频 Token 计费 | [参数、参考素材与计费](https://openpix.cc/docs/seedance.html) |
| SD-2.0-MZ1，按秒计费 | [参数与音频参考](https://openpix.cc/docs/sd-seconds.html) |
| MiniMax-H3，按秒计费 | [参数与首尾帧](https://openpix.cc/docs/minimax-h3.html) |
| Grok 视频，按次计费 | [固定 6 秒与可变时长区别](https://openpix.cc/docs/grok-video.html) |
| 本地图片、视频、音频作为参考 | [素材上传与格式限制](https://openpix.cc/docs/video-reference.html) |
| 已创建视频任务，查询或下载 | [状态、轮询、内容与制品](https://openpix.cc/docs/video-tasks.html) |
| 直接使用完整视频客户端 | [Python、Node.js、PowerShell](https://openpix.cc/docs/video-examples.html) |
| MJ 三个版本、多图返回和高级参数 | [MJ 生图](https://openpix.cc/docs/midjourney.html) |
| Seedream Lite 4K / Pro 2K 与参考图 | [Seedream 生图](https://openpix.cc/docs/seedream.html) |
| GPT2 / Flare / Sunburst、福利组与 Adobe 中质量 | [GPT 图片与分组规则](https://openpix.cc/docs/gpt-image.html) |
| 香蕉 Pro / Flash / 2.1 | [香蕉生图](https://openpix.cc/docs/banana.html) |
| Grok 图片 | [Grok Imagine 图片](https://openpix.cc/docs/grok-image.html) |
| AS Gemini 函数调用与 MP4 视频理解 | [AS Gemini](https://openpix.cc/docs/as-gemini.html) |
| GPT / Grok 新语言型号及长上下文费用 | [语言型号与阶梯费用](https://openpix.cc/docs/language-pricing.html) |
| 文生图、图生图 | [图片生成](https://openpix.cc/docs/images.html) · [编辑与参考图](https://openpix.cc/docs/edits.html) · [完整代码](https://openpix.cc/docs/examples.html) |
| 文本对话 | [Chat Completions 与 Responses](https://openpix.cc/docs/text.html) |

### 接口地图

| 用途 | 请求 | 结果类型 |
|---|---|---|
| 当前 Key 可用模型 | GET /v1/models | 模型目录 JSON |
| 公开报价 | GET /api/pricing | 价格、分组与规则 JSON，不代表 Key 权限 |
| 创建视频 | POST /v1/videos | 任务对象，保存 id |
| 查询视频 | GET /v1/videos/{task_id} | 状态对象 |
| 下载视频 | GET /v1/videos/{task_id}/content | 视频二进制，需要鉴权 |
| 查看制品 | GET /v1/tasks/{task_id}/artifacts | 实际可用制品列表 |
| 上传视频参考图片 | POST /v1/video-reference-images | 素材 URL |
| 上传参考视频/音频 | POST /v1/video-reference-media/{model}/{kind} | 素材 URL 与媒体信息 |
| 异步文生图 | POST /v1/images/generations/async | 图片任务，通常 HTTP 202 |
| 异步图生图 | POST /v1/images/edits/async | 图片任务，通常 HTTP 202 |
| 查询图片 | GET /v1/images/tasks/{task_id} | 图片任务状态与结果 |
| 同步图片 | POST /v1/images/generations 或 /v1/images/edits | 等待后返回图片数据 |
| Gemini 原生 | POST /v1beta/models/{model}:generateContent | 原生 candidates/parts |
| 语言模型 | POST /v1/chat/completions 或 /v1/responses | 文本响应或 SSE |

### 推荐接入顺序

1. 创建相应分组的 Key，用 GET /v1/models 确认完整 model。
2. 查看该型号专属参数，先使用最小请求。生成调用会产生费用。
3. 发出一次 POST，立即保存本站任务 ID。
4. 查询同一任务直到终态，校验结果，再保存到自己的存储。
5. 根据[分组与计费](https://openpix.cc/docs/pricing.html)核对同一任务的消费和退款。

提交结果未知时先核查记录。不要把超时当作未创建，也不要靠重复 POST 恢复轮询。Key 只放在自己的服务端，日志不记录完整凭据或私有素材链接。

### 阅读、搜索和代码下载

左侧目录按接口类型划分，顶部可搜索型号、字段和错误说明。每页可复制 Markdown，或[下载完整手册](https://openpix.cc/docs/manxiaobai-api-docs.md)。代码框只复制内容，不会自动调用生成接口。

示例中的 example、示意任务 ID、素材链接和时间戳需要替换成真实值。图片、工具及视频示例按对应实际接口契约做离线校验；本次文档更新没有新发起付费生成，不把离线验证等同于每种规格都已实测出片。

---

## 快速上手

下面使用 GPT Image 2.5，完成一次“提交 → 查询 → 下载”。生成请求会按所选分组计费；查询模型清单不会生成图片。

### 1. 创建令牌

登录[令牌管理](https://openpix.cc/keys)，创建 API Key，选择具有目标模型权限的分组。检查账户余额、令牌额度、有效期及模型限制。最低价需要对应的分组，并不是所有 Key 默认享有。

将 Key 保存在服务端环境变量 `MXB_API_KEY` 中。示例不会替你创建或储存真实 Key。

```bash
# Bash：输入时不回显，避免 Key 出现在命令历史中
read -r -s -p 'API Key: ' MXB_API_KEY
export MXB_API_KEY
printf '\n'
```

上述命令使用 Bash 语法。Windows PowerShell 7 可在当前会话安全输入 Key，再使用本页后面的 PowerShell 示例：

```powershell
$secureKey = Read-Host 'API Key' -AsSecureString
$env:MXB_API_KEY = [System.Net.NetworkCredential]::new('', $secureKey).Password
```

不要把 Key 提交到 Git 或放进网页源码。

### 2. 检查模型权限

```bash
curl --fail-with-body --max-time 30 \
  https://openpix.cc/v1/models \
  -H "Authorization: Bearer $MXB_API_KEY"
```

检查响应 `data[].id` 是否包含 `gpt-image-2.5`。没有该模型时调整令牌分组或模型限制，不要反复提交生成请求验证权限。

### 3. 提交一次文生图

```bash
curl --fail-with-body --max-time 60 \
  https://openpix.cc/v1/images/generations/async \
  -H "Authorization: Bearer $MXB_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{
    "model": "gpt-image-2.5",
    "prompt": "白色桌面上的蓝色陶瓷茶壶，柔和自然光，产品摄影",
    "size": "1024x1024",
    "n": 1
  }'
```

正常接纳返回 HTTP **202**，包含 `task_id`、`status`、`poll_url` 和 `poll_after_ms`。下面是结构示意，ID 不能直接查询：

```json
{
  "id": "imgtask_example",
  "task_id": "imgtask_example",
  "object": "image.generation.task",
  "status": "processing",
  "state": "queued",
  "poll_url": "https://openpix.cc/v1/images/tasks/imgtask_example",
  "poll_after_ms": 3000
}
```

立即保存真实 `task_id`。`--max-time 60` 是客户端等待提交响应的时间，不是模型生成时限。没有拿到响应时，无法据此断定服务端未收到请求，不要自动重发。

### 4. 查询同一任务

```bash
# 将返回的真实 task_id 赋给 MXB_TASK_ID
curl --fail-with-body --max-time 30 \
  "https://openpix.cc/v1/images/tasks/$MXB_TASK_ID" \
  -H "Authorization: Bearer $MXB_API_KEY"
```

使用创建任务时的同一 Key，按 `poll_after_ms` 间隔查询；当前默认 3000 毫秒。

- `processing`：继续等待，不重新提交。
- `completed`：检查 `data[]` 非空，再读取图片 URL 或 Base64 结果。
- `failed`：停止查询，读取 `error.message`。`submission_uncertain:true` 表示提交结果不确定，先核对记录。

### 5. 下载图片

```bash
# 将真实 data[0].url 赋给 MXB_IMAGE_URL；下载图片无需附带 API Key
curl --fail --location --proto '=https,http' --proto-redir '=https,http' \
  --max-time 120 "$MXB_IMAGE_URL" --output result.png
```

输出文件名不会转换图片格式；实际格式与像素以返回图片为准。完整示例会校验 HTTP 状态、响应类型和非空结果，并支持用已有任务 ID 继续查询，见[完整代码](https://openpix.cc/docs/examples.html)。

### PowerShell 7 提交方式

```powershell
# 前提：当前进程已设置 $env:MXB_API_KEY
$headers = @{ Authorization = "Bearer $env:MXB_API_KEY" }
$body = @{
    model = 'gpt-image-2.5'
    prompt = '白色桌面上的蓝色陶瓷茶壶，产品摄影'
    size = '1024x1024'
    n = 1
} | ConvertTo-Json
$task = Invoke-RestMethod -Method Post `
    -Uri 'https://openpix.cc/v1/images/generations/async' `
    -Headers $headers -ContentType 'application/json; charset=utf-8' `
    -Body ([System.Text.Encoding]::UTF8.GetBytes($body)) -TimeoutSec 60
$task | ConvertTo-Json -Depth 10 | Set-Content -Encoding utf8 './mxb-task.json'
$task.task_id
```

Windows 使用 curl 命令时请写 `curl.exe`，避免部分 PowerShell 版本把 `curl` 当成其他命令的别名。

---

## 鉴权与地址

主入口为 https://openpix.cc，也可使用 https://api.manxiaobai.online。已有任务尽量保持同一入口与原 Key。

### 三种地址不要混用

| 用途 | 地址 | 如何拼接 |
|---|---|---|
| 原始 HTTP 请求 | `https://openpix.cc` | 自己添加完整 `/v1/...` 或 `/v1beta/...` 路径 |
| 自动附加资源路径的 OpenAI 兼容客户端 | `https://openpix.cc/v1` | 客户端再添加 `/images/generations` 等路径 |
| Gemini 原生 HTTP | `https://openpix.cc/v1beta/models/{model}:generateContent` | `{model}` 替换成本站公开模型 ID |

最常见错误是出现 `/v1/v1/images/...`，或把管理后台地址 `/dashboard` 当成 API Base URL。某些第三方工具会自己附加 `/v1`，应以它最终发出的 URL 为准。

### API Key 鉴权

```http
Authorization: Bearer YOUR_API_KEY
Content-Type: application/json
```

把 `YOUR_API_KEY` 换成[令牌管理](https://openpix.cc/keys)生成的实际 Key；`Bearer` 后有一个空格。Key 属于敏感凭证，不能写入客户端网页、公开仓库、截图或反馈日志。

网页的登录 Cookie 与 API Key 是两种用途不同的凭证。接入程序使用令牌管理创建的 Key，不要复制浏览器 Cookie。

### Key、分组与模型

一个 Key 的可用性同时受到用户权限、所选分组、令牌额度/有效期、模型限制及可用渠道影响。首页与模型广场的最低价是公开分组的最低报价，不代表当前 Key 已选择该分组。

请求体中的 `model` 只能选择模型，不能用 `group` 字段切换令牌的计费分组。需要换组时在控制台调整或创建相应分组的 Key；执行中的任务继续用原 Key 查询。

```bash
curl --fail-with-body https://openpix.cc/v1/models \
  -H "Authorization: Bearer $MXB_API_KEY"
```

响应常见结构为 `{"object":"list","data":[{"id":"gpt-image-2.5",...}]}`。使用返回的完整模型 ID，不自行添加或删除后缀。

### 同源代理与跨域

站内页面有 `/api-proxy/` 兼容入口，例如 `/api-proxy/images/generations/async`，路径中**没有第二个 `/v1`**。它不是公开免鉴权代理；第三方服务应使用正常 `/v1/` 地址。

第三方网页跨域 fetch 当前没有完整 CORS 支持。正确部署方式为：用户浏览器 → 你自己的后端 → 本站 API。后端注入 Key，并对你自己的用户做鉴权、额度和速率控制。

### 请求与日志

- JSON 使用 UTF-8，明确 `Content-Type: application/json`。
- multipart 上传让 HTTP 库生成 boundary，不要手动写不匹配的 Content-Type。
- 保存模型、时间、HTTP 状态、`task_id` 和返回的 `request_id`。图片任务 ID 与计费 request_id 不等价。
- 不要将完整请求头或 Base64 图片写进公共日志；诊断日志也应脱敏 Key。

---

## 模型与规格

以下按2026-11-01本站公开目录整理，共 **51 个完整ID**。公开目录提供型号和报价，当前Key实际权限必须用 `GET /v1/models` 确认；可见条目不等于全部能力已经逐项实测。

### 先选择接口类型

| 类型 | 系列与说明 |
|---|---|
| 图片 | [MJ](https://openpix.cc/docs/midjourney.html)、[Seedream](https://openpix.cc/docs/seedream.html)、[GPT Image](https://openpix.cc/docs/gpt-image.html)、[香蕉](https://openpix.cc/docs/banana.html)、[Grok图片](https://openpix.cc/docs/grok-image.html) |
| 视频生成 | [七个视频型号](https://openpix.cc/docs/video.html)：Mini/Fast、SD按秒、MiniMax-H3、Grok；输出视频任务 |
| 语言与工具 | [Chat/Responses](https://openpix.cc/docs/text.html)、[AS Gemini工具及视频理解](https://openpix.cc/docs/as-gemini.html)、[语言阶梯费用](https://openpix.cc/docs/language-pricing.html) |

Seedream图片与Seedance视频不同；Gemini视频理解是语言请求，不是生成视频。Images的异步任务与Gemini原生响应也不使用同一解析方式。

### 图片参数不要跨系列照搬

| 系列 | 尺寸 | 质量 | 输出数量 |
|---|---|---|---|
| MJ | size比例，默认16:9 | 数值0.25/0.5/1/2 | n=1仍为4张单图，可附拼图 |
| Seedream | resolution + size比例 | 最小请求省略 | n=1，一张 |
| GPT Image | 推荐像素size，福利组支持K档 | 字符串档位，按组 | 推荐n=1 |
| 香蕉 | Images像素；原生imageConfig | 不套用GPT的max | 推荐单图 |
| Grok图片 | 从1024x1024开始 | 首次省略 | 推荐单图 |

Adobe中质量统一medium，默认2K、可明确请求4K，不再自动缩小。福利组仅在专章列出的型号范围支持1K名称请求2K/4K。型号后缀、请求size与实际原图像素是三个概念，不能只凭其中一个推断全部能力。

### 当前完整型号索引

下表分组仅列当前公开倍率表中的分组；Key还可能有模型限制或账户级权限。具体报价、默认值、范围及验证边界请进入详细章节。

| 完整model | 当前公开分组 | 接入文档 |
|---|---|---|
| `MiniMax-H3` | 视频分组 | [视频](https://openpix.cc/docs/video.html) |
| `SD-2.0-MZ1` | seedance低价渠道，质量自测 | [视频](https://openpix.cc/docs/video.html) |
| `dreamina-seedance-2-0-fast-260128` | 视频分组 | [视频](https://openpix.cc/docs/video.html) |
| `dreamina-seedance-2-0-mini-260615` | 视频分组 | [视频](https://openpix.cc/docs/video.html) |
| `gemini-3-flash-preview` | as gemini分组 | [AS Gemini](https://openpix.cc/docs/as-gemini.html) |
| `gemini-3-pro-image-preview` | default、香蕉谷歌生图分组 | [详细参数](https://openpix.cc/docs/banana.html) |
| `gemini-3-pro-preview` | as gemini分组 | [AS Gemini](https://openpix.cc/docs/as-gemini.html) |
| `gemini-3.1-flash-image` | as gemini分组 | [AS Gemini](https://openpix.cc/docs/as-gemini.html) |
| `gemini-3.1-flash-image-preview` | 香蕉谷歌生图分组、default | [详细参数](https://openpix.cc/docs/banana.html) |
| `gemini-3.1-flash-lite-image` | default、香蕉谷歌生图分组 | [详细参数](https://openpix.cc/docs/banana.html) |
| `gemini-3.1-pro-preview` | as gemini分组 | [AS Gemini](https://openpix.cc/docs/as-gemini.html) |
| `gemini-3.5-flash` | 谷歌语言模型、as gemini分组 | [AS Gemini](https://openpix.cc/docs/as-gemini.html) |
| `gemini-3.6-flash` | 谷歌语言模型 | [详细参数](https://openpix.cc/docs/language-pricing.html) |
| `gemini-3.7-flash` | 谷歌语言模型 | [详细参数](https://openpix.cc/docs/language-pricing.html) |
| `gemini-3.8-flash` | as gemini分组、谷歌语言模型 | [AS Gemini](https://openpix.cc/docs/as-gemini.html) |
| `gemini-4-argon` | as gemini分组 | [AS Gemini](https://openpix.cc/docs/as-gemini.html) |
| `gemini-nano-banana-2.1` | default、香蕉谷歌生图分组 | [详细参数](https://openpix.cc/docs/banana.html) |
| `gpt-5.5` | gptpro语言模型分组 | [详细参数](https://openpix.cc/docs/language-pricing.html) |
| `gpt-5.6` | gptpro语言模型分组 | [详细参数](https://openpix.cc/docs/language-pricing.html) |
| `gpt-5.6-sol` | gptpro语言模型分组 | [详细参数](https://openpix.cc/docs/language-pricing.html) |
| `gpt-6-astra` | gptpro语言模型分组 | [详细参数](https://openpix.cc/docs/language-pricing.html) |
| `gpt-6-sol` | gptpro语言模型分组 | [详细参数](https://openpix.cc/docs/language-pricing.html) |
| `gpt-6.1-sol` | gptpro语言模型分组 | [详细参数](https://openpix.cc/docs/language-pricing.html) |
| `gpt-image-2` | adobe2全质量专属分组、gpt生图逆向分组、oai和adobe混组、客户全质量gpt2模型、adobe福利1k生图分组、default、gpt生图相对稳定高价分组、GPT生图福利分组 | [详细参数](https://openpix.cc/docs/gpt-image.html) |
| `gpt-image-2-1k` | adobe福利1k生图分组、default、GPT生图福利分组 | [详细参数](https://openpix.cc/docs/gpt-image.html) |
| `gpt-image-2-2k` | adobe2全质量专属分组、adobe中质量专属分组 | [详细参数](https://openpix.cc/docs/gpt-image.html) |
| `gpt-image-2-4k` | adobe2全质量专属分组 | [详细参数](https://openpix.cc/docs/gpt-image.html) |
| `gpt-image-2.5` | adobe福利1k生图分组、default、GPT生图福利分组 | [详细参数](https://openpix.cc/docs/gpt-image.html) |
| `gpt-image-2.5-1k` | adobe福利1k生图分组、default、GPT生图福利分组 | [详细参数](https://openpix.cc/docs/gpt-image.html) |
| `gpt-image-2.5-flare` | oai和adobe混组、oai生图专属分组、gpt生图逆向分组、adobe全参专属分组、adobe福利1k生图分组、default、GPT生图福利分组 | [详细参数](https://openpix.cc/docs/gpt-image.html) |
| `gpt-image-2.5-flare-1k` | adobe全参专属分组、adobe福利1k生图分组、default、GPT生图福利分组 | [详细参数](https://openpix.cc/docs/gpt-image.html) |
| `gpt-image-2.5-flare-2k` | adobe全参专属分组 | [详细参数](https://openpix.cc/docs/gpt-image.html) |
| `gpt-image-2.5-flare-4k` | adobe全参专属分组 | [详细参数](https://openpix.cc/docs/gpt-image.html) |
| `gpt-image-2.5-sunburst` | adobe全参专属分组、default、GPT生图福利分组、gpt生图逆向分组、oai和adobe混组、oai生图专属分组、adobe福利1k生图分组、gpt生图相对稳定高价分组 | [详细参数](https://openpix.cc/docs/gpt-image.html) |
| `gpt-image-2.5-sunburst-1k` | adobe全参专属分组、adobe福利1k生图分组、default、GPT生图福利分组 | [详细参数](https://openpix.cc/docs/gpt-image.html) |
| `gpt-image-2.5-sunburst-2k` | adobe全参专属分组 | [详细参数](https://openpix.cc/docs/gpt-image.html) |
| `gpt-image-2.5-sunburst-4k` | adobe全参专属分组 | [详细参数](https://openpix.cc/docs/gpt-image.html) |
| `grok-4.5` | grokheavy速刷语言 | [详细参数](https://openpix.cc/docs/language-pricing.html) |
| `grok-4.6` | grokheavy速刷语言 | [详细参数](https://openpix.cc/docs/language-pricing.html) |
| `grok-4.7` | grokheavy速刷语言 | [详细参数](https://openpix.cc/docs/language-pricing.html) |
| `grok-imagine-image` | grok生图分组 | [详细参数](https://openpix.cc/docs/grok-image.html) |
| `grok-imagine-image-2.0` | grok生图分组 | [详细参数](https://openpix.cc/docs/grok-image.html) |
| `grok-imagine-image-quality` | grok生图分组 | [详细参数](https://openpix.cc/docs/grok-image.html) |
| `grok-imagine-video-1.5-6s` | 视频分组 | [视频](https://openpix.cc/docs/video.html) |
| `grok-imagine-video-6s` | 视频分组 | [视频](https://openpix.cc/docs/video.html) |
| `grok-video-1.5` | default、视频分组 | [视频](https://openpix.cc/docs/video.html) |
| `mj-v6.1` | mj和Seedream生图专用分组 | [详细参数](https://openpix.cc/docs/midjourney.html) |
| `mj-v7` | mj和Seedream生图专用分组 | [详细参数](https://openpix.cc/docs/midjourney.html) |
| `mj-v8.1` | mj和Seedream生图专用分组 | [详细参数](https://openpix.cc/docs/midjourney.html) |
| `seedream-5.0` | mj和Seedream生图专用分组 | [详细参数](https://openpix.cc/docs/seedream.html) |
| `seedream-5.0-lite` | mj和Seedream生图专用分组 | [详细参数](https://openpix.cc/docs/seedream.html) |

### 目录存在但需要单独确认的项目

`as gemini分组` 的 `gemini-3-pro-preview` 与 `gemini-3.1-flash-image` 不在五个已验证语言型号的能力/Token费率说明内；无preview的image型号不能用香蕉组报价代替。Flash Lite图片也不能直接外推Pro/Flash/2.1的4K实测结论。

当前MJ不含v8.2/niji7。旧Seedance标准2.0/2.5不在当前新建目录；使用本页七个当前视频型号。已有任务继续用原ID查询，不因型号调整重复生成。

### 目录变化与应用接入

应用启动或切换Key后重新读取目录，失败时明确提示，不能当作所有模型都有权限。服务地址使用 `https://openpix.cc`；OpenAI兼容Base URL通常为 `https://openpix.cc/v1`，不要拼出/v1/v1。各专章提供的curl采用完整URL。

---

## 视频总览与型号对照

通过同一组本站接口完成视频的 **提交 → 查询 → 下载**。核对日期为 **2026-10-29**。不同型号的时长、画面比例、参考素材和计费单位不同，请先按下表选型号，再使用对应章节的请求体。

### 当前可选型号与费用单位

下表为当前公开目录快照。新建令牌后先执行 `GET /v1/models`，确认自己的分组和模型权限；公开目录中的模型不代表任意 Key 都能调用。表中价格是 **¤ 站内额度**，是核对时所列分组的报价，不是永久价格承诺。

| 请求中的完整 model | 分组 | 规格摘要 | 报价 / 计费单位 | 详细说明 |
|---|---|---|---|---|
| `dreamina-seedance-2-0-mini-260615` | 视频分组 | 480p / 720p，4–15 秒；图片、视频、音频参考 | 无参考视频 12.65；有参考视频 7.7 / 百万视频 Token | [Seedance Mini/Fast](https://openpix.cc/docs/seedance.html) |
| `dreamina-seedance-2-0-fast-260128` | 视频分组 | 480p / 720p，4–15 秒；图片、视频、音频参考 | 无参考视频 26.455；有参考视频 15.73 / 百万视频 Token | [Seedance Mini/Fast](https://openpix.cc/docs/seedance.html) |
| `SD-2.0-MZ1` | seedance低价渠道，质量自测 | 固定 720p，5–15 秒；图片和音频参考 | 0.16 / 请求的输出秒数 | [SD 按秒视频](https://openpix.cc/docs/sd-seconds.html) |
| `MiniMax-H3` | 视频分组 | 固定 768P，1–15 秒；关键帧及多模态参考 | 0.06 / 请求的输出秒数 | [MiniMax-H3](https://openpix.cc/docs/minimax-h3.html) |
| `grok-imagine-video-6s` | 视频分组 | 固定 6 秒 / 720p；横屏或竖屏 | 0.08 / 次 | [Grok 视频](https://openpix.cc/docs/grok-video.html) |
| `grok-imagine-video-1.5-6s` | 视频分组 | 固定 6 秒 / 720p；横屏或竖屏 | 0.10 / 次 | [Grok 视频](https://openpix.cc/docs/grok-video.html) |
| `grok-video-1.5` | default、视频分组 | 720p；按章节选择离散时长 | 0.85 / 次 | [Grok 视频](https://openpix.cc/docs/grok-video.html) |

之前文档中的 `dreamina-seedance-2-0-260128`、`dreamina-seedance-2-5-260628` 已不在当前新建任务目录中。不要继续复制旧的 2.0/2.5 Token 请求。已有任务仍应保存原 ID 查询；不要用新型号重新提交来“恢复”旧任务。

### 按需求选择

- 需要视频参考、混合音频参考，或希望选择 480p / 720p：查看 Mini/Fast。
- 希望按明确的输出秒数估算费用：查看 SD 或 MiniMax-H3；两者的素材格式与声音选项不同。
- 希望使用固定 6 秒短片：选择名称带 `-6s` 的 Grok 型号。
- 已接入 `grok-video-1.5`：沿用其专属参数；不要把 `-6s` 型号的限制或参考图写法直接套过来。

模型名称中的 Fast、Mini、版本数字描述型号，不是生成时延、画质或可用性的保证。开始批量任务前，应以自己的真实素材验证目标规格。

### 统一接口地图

| 方法与路径 | 用途 | 成功后的处理 |
|---|---|---|
| `GET /v1/models` | 检查当前 Key 的模型权限 | 从 `data[].id` 取完整模型名 |
| `POST /v1/videos` | 创建一个视频任务 | 保存返回的本站 `id`，开始查询 |
| `GET /v1/videos/{task_id}` | 查询任务状态 | 处理 queued、in_progress、completed、failed |
| `GET /v1/videos/{task_id}/content` | 下载完成视频 | 以二进制保存；检查 HTTP 状态与文件内容 |
| `HEAD /v1/videos/{task_id}/content` | 读取可用结果的响应头 | 可用于检查下载响应，不代替状态查询 |
| `GET /v1/tasks/{task_id}/artifacts` | 获取可用制品列表 | 适用于提供制品的任务；如请求并实际返回了末帧 |

所有请求均发送到 `https://openpix.cc`，携带 `Authorization: Bearer <本站 API Key>`。也支持 `https://api.manxiaobai.online`；同一任务保持同一入口和原 Key 最容易排查问题。

不需要把视频发送到 `/v1/chat/completions`。新接入优先使用本页统一接口，不需要复制任何专用路由前缀，也不需要填写额外的平台或服务商参数。

### 第一个视频请求

先在[令牌管理](https://openpix.cc/keys)创建具有“视频分组”和目标模型权限的 Key，并在服务端设置环境变量 `MXB_API_KEY`。下面创建一个固定 6 秒视频；执行 POST 会产生一次真实生成任务。

```bash
curl --fail-with-body --max-time 120 \
  https://openpix.cc/v1/videos \
  -H "Authorization: Bearer $MXB_API_KEY" \
  -H 'Content-Type: application/json' \
  --data-binary @grok-6s-text.json
```

下载 [grok-6s-text.json](https://openpix.cc/docs/examples/grok-6s-text.json) 到命令执行目录，其完整内容如下：

```json
{
  "model": "grok-imagine-video-6s",
  "prompt": "清晨湖面上的蓝色纸船缓缓漂动，柔和光线，镜头平稳推进",
  "seconds": "6",
  "resolution": "720p",
  "aspect_ratio": "16:9"
}
```

成功受理的结构示例，ID 为虚构值：

```json
{
  "id": "task_example_video_001",
  "object": "video",
  "model": "grok-imagine-video-6s",
  "status": "queued",
  "progress": 0,
  "created_at": 1793203200
}
```

HTTP 200 表示提交请求得到任务回执，**不是视频已经生成完成**。不要按图片接口的 `data[0].url` 解析这一步。

将真实 `id` 保存为环境变量 `MXB_VIDEO_TASK_ID`，每 5 秒左右查询同一任务：

```bash
curl --fail-with-body --max-time 30 \
  "https://openpix.cc/v1/videos/$MXB_VIDEO_TASK_ID" \
  -H "Authorization: Bearer $MXB_API_KEY"
```

状态为 `completed` 后下载：

```bash
curl --fail --max-time 180 \
  "https://openpix.cc/v1/videos/$MXB_VIDEO_TASK_ID/content" \
  -H "Authorization: Bearer $MXB_API_KEY" \
  --output result.mp4
```

没有使用 `--retry` 或自动重发 POST。下载或查询失败可以继续访问原任务，不必重新生成。完整程序见 [Python、Node.js 与 PowerShell](https://openpix.cc/docs/video-examples.html)。

### 请求体通用规则

| 项目 | 要求 |
|---|---|
| JSON 编码 | UTF-8；`Content-Type: application/json` |
| 型号 | `model` 为完整公开 ID；区分大小写与后缀 |
| 时长 | 推荐使用 `seconds`；可用值按型号，不统一为任意正整数 |
| 宽高比 | Seedance / SD / H3 用 `ratio`；Grok 推荐 `aspect_ratio` |
| 分辨率 | `resolution` 的合法值按型号；不要把共享价格字段当成能力清单 |
| 参考素材 | 按具体型号选择字段；`images`、`reference_images`、`metadata.content` 不可随意互换 |
| 布尔值 | JSON 使用 `true` / `false`，不是字符串；型号不支持的开关应省略 |
| 多个别名 | 一个含义只填写一个字段；不要传冲突的 seconds/duration 或顶层/metadata 值 |
| 任务数量 | 每次提交对应一个任务；不要用未说明的 `n` 参数批量创建 |
| 分组 | 由 Key 配置决定；请求 JSON 中增加 group 不能切换价格分组 |

### 返回字段与完整生命周期

具体响应字段、失败状态、HEAD/Range 下载、末帧获取、超时、重试与账单核对见 [视频任务与下载](https://openpix.cc/docs/video-tasks.html)。参考素材的 URL、文件上传、首尾帧与多图区别见 [视频参考素材](https://openpix.cc/docs/video-reference.html)。

本站没有在本手册承诺统一的任务取消、回调 webhook、POST 幂等、生成时延或永久媒体托管。不要将其他平台的功能默认视为本站所有型号都支持。

### 本次核对范围

型号、分组、价格和请求校验与 2026-10-29 的当前部署核对；下载示例经过离线流程验证。规格表表示当前接口接受范围，不代表每个比例、时长、参考素材组合都重新执行了付费生成。模型、分组或价格调整后，以当前模型广场、自己的模型列表和实际账单为准。

---

## Seedance Mini / Fast

本页适用于当前“视频分组”中的 **Mini 和 Fast**。核对日期：**2026-10-29**。二者通过 `POST /v1/videos` 创建任务，支持多模态参考，按实际视频 Token 用量计费。

**按秒计费的 `SD-2.0-MZ1` 请看[专门章节](https://openpix.cc/docs/sd-seconds.html)**，不要复制本页的 480p、4 秒、视频参考或关闭音频参数。

### 型号与当前规格

| 型号 | 完整 model | 分辨率 | 普通生成时长 |
|---|---|---|---|
| Mini | dreamina-seedance-2-0-mini-260615 | 480p、720p | 4–15 秒整数 |
| Fast | dreamina-seedance-2-0-fast-260128 | 480p、720p | 4–15 秒整数 |

旧文档中的 `dreamina-seedance-2-0-260128` 和 `dreamina-seedance-2-5-260628` 已从当前新任务目录移除。旧任务仍按原 ID 查询，本页不再提供它们的新建请求或报价。

Mini/Fast **不支持 1080p、2K、4K**。公开价格元数据中的分辨率枚举可能由多个型号共享，不能把其中所有值当作当前型号实际可提交的能力。

### 最小文生视频请求

下载 [seedance-mini-text.json](https://openpix.cc/docs/examples/seedance-mini-text.json)：

```json
{
  "model": "dreamina-seedance-2-0-mini-260615",
  "prompt": "清晨湖面上的蓝色纸船缓缓漂动，柔和光线，镜头平稳推进",
  "seconds": "4",
  "resolution": "480p",
  "ratio": "16:9",
  "generate_audio": false,
  "watermark": false
}
```

```bash
curl --fail-with-body --max-time 120 \
  https://openpix.cc/v1/videos \
  -H "Authorization: Bearer $MXB_API_KEY" \
  -H 'Content-Type: application/json' \
  --data-binary @seedance-mini-text.json
```

将 model 换成 Fast 的完整 ID 即可使用 Fast。成功受理后立即保存本站 `id`；查询和下载流程见[视频任务与下载](https://openpix.cc/docs/video-tasks.html)。

### 完整参数表

| 参数 | 类型 | 默认 / 取值 | 说明 |
|---|---|---|---|
| model | string，必填 | 上表完整 ID | Key 必须有对应模型权限 |
| prompt | string | 1–6000 个 Unicode 字符 | 与 content 中的唯一 text 项二选一 |
| seconds | string 或 integer | 默认 5；4–15 整数 | 推荐普通生成使用此字段 |
| duration | integer，可选 | seconds 的别名 | JSON 中为整数；-1 仅用于下述单视频编辑 |
| resolution | string | 默认 480p；480p / 720p | 大小写会归一，但推荐小写 |
| ratio | string | 默认 16:9 | 16:9、9:16、1:1、4:3、3:4、21:9、adaptive |
| generate_audio | boolean，可选 | 建议显式 true 或 false | 是否请求生成声音；省略时不在本手册承诺默认结果 |
| watermark | boolean，可选 | 建议显式 true 或 false | 水印请求参数，不保证所有输出无可见标记 |
| seed | integer，可选 | -1 至 4294967295 | 相同种子不保证跨次输出逐帧一致 |
| return_last_frame | boolean，可选 | true / false | 请求末帧；仅实际返回末帧时才有对应制品 |
| content / metadata.content | array，可选 | 1–13 项 | 一个 text 与最多 12 个素材，二选一 |
| images | string[]，可选 | 最多 9 张 HTTPS 图片 | 普通参考图简写；不是首尾帧 |
| input_reference / image | string，可选 | HTTPS 图片 | 普通参考图简写，避免多个别名同时填写 |
| metadata.first_frame_image | string，可选 | HTTPS 图片 | 必须同时提供 last_frame_image |
| metadata.last_frame_image | string，可选 | HTTPS 图片 | 必须同时提供 first_frame_image |
| size | string，可选 | 480p / 720p | resolution 别名，不是像素宽高或比例 |
| omni_reference_task_type | string，可选 | 仅 edit | 只用于下述单视频编辑 |

metadata 允许 content、first_frame_image、last_frame_image，以及 duration、resolution、ratio、generate_audio、watermark、seed、return_last_frame、omni_reference_task_type。同一参数顶层和 metadata 若都出现，值必须相同，建议只传一处。

`ratio:"adaptive"` 需要图片或视频视觉输入。纯文本、纯音频参考不能使用 adaptive。普通文本生成建议使用固定比例。

### 参考素材规则

| 素材 | type | role | 数量上限 |
|---|---|---|---:|
| 图片 | image_url | reference_image | 9 |
| 视频 | video_url | reference_video | 3 |
| 音频 | audio_url | reference_audio | 3 |
| 所有参考素材合计 | — | — | 12 |

text 不计入 12 个媒体素材，但整个 content 数组最多 13 项。必须恰好一项 text；如果已有顶层 prompt，不要再放 text 项。

URL 必须是无需登录即可访问的有效 HTTPS 地址，生成期间保持可访问。不能发送本地文件路径、浏览器 blob URL、私网地址或带本站 API Key 的 URL。此型号的 `/v1/videos` 不接受直接二进制附件；先使用自己的素材存储或创作台获取有效链接。

完整混合参考示例：

```json
{
  "model": "dreamina-seedance-2-0-mini-260615",
  "seconds": "5",
  "resolution": "480p",
  "ratio": "16:9",
  "generate_audio": true,
  "metadata": {
    "content": [
      {"type": "text", "text": "参考@图片1的主体，结合@视频1的运镜和@音频1的节奏制作短片"},
      {"type": "image_url", "image_url": {"url": "https://media.example.com/character.png"}, "role": "reference_image"},
      {"type": "video_url", "video_url": {"url": "https://media.example.com/movement.mp4"}, "role": "reference_video"},
      {"type": "audio_url", "audio_url": {"url": "https://media.example.com/music.wav"}, "role": "reference_audio"}
    ]
  }
}
```

将 example.com 示例地址全部换成自己的真实素材。图片、视频、音频按各自类型的出现顺序编号；提示词可明确写 @图片1、@视频1、@音频1。

只用普通参考图片时可简写为 `prompt` 与 `images:[HTTPS地址]`。不能再同时传 content、image、input_reference 或关键帧字段。更多文件准备说明见[视频参考素材](https://openpix.cc/docs/video-reference.html)。

### 首尾帧

Mini/Fast 的首尾帧必须 **成对提供，各一张**。不能只给首帧或只给尾帧，不能与普通参考图、视频、音频混用。

```json
{
  "model": "dreamina-seedance-2-0-fast-260128",
  "prompt": "自然衔接首帧与尾帧，保持主体造型和运动连贯",
  "seconds": "5",
  "resolution": "720p",
  "ratio": "16:9",
  "generate_audio": false,
  "metadata": {
    "first_frame_image": "https://media.example.com/first.png",
    "last_frame_image": "https://media.example.com/last.png"
  }
}
```

等价的 content 写法是两个 image_url 项，角色分别为 first_frame 和 last_frame。二者仅改变素材表达形式，不改变必须成对提供的规则。

需要单首帧功能时，不要删除本例尾帧碰运气，应选择明确支持单首帧的 [H3](https://openpix.cc/docs/minimax-h3.html) 或 [Grok](https://openpix.cc/docs/grok-video.html)。

### 单视频编辑

高级编辑需要 **恰好一个参考视频**，不能再加入图片、音频或关键帧：

```json
{
  "model": "dreamina-seedance-2-0-mini-260615",
  "duration": -1,
  "resolution": "480p",
  "ratio": "adaptive",
  "omni_reference_task_type": "edit",
  "metadata": {
    "content": [
      {"type": "text", "text": "保留运镜与主体动作，将环境改为柔和的黄昏光线"},
      {"type": "video_url", "video_url": {"url": "https://media.example.com/clip.mp4"}, "role": "reference_video"}
    ]
  }
}
```

duration=-1 仅在该编辑模式中用于跟随参考视频时长，不代表普通生成可以使用任意时长。不要同时传 seconds 与冲突的 duration。此组合经过请求契约核对，使用自己的素材前仍应做小规模实际效果验证。

### 视频 Token 计费

以下为核对时视频分组倍率为 1 的基础单价，单位 **¤ / 百万视频 Token**：

| 模型 | 没有参考视频 | 有参考视频 |
|---|---:|---:|
| Mini | 12.65 | 7.7 |
| Fast | 26.455 | 15.73 |

仅图片或音频参考仍属于“没有参考视频”。480p 和 720p 当前单价相同，不代表总费用相同；分辨率、输出时长、参考视频及实际用量会影响最终金额。

估算公式为：`视频 Token 数 ÷ 1,000,000 × 适用单价 × 实际适用倍率`。网页已展示折算后的最终单价时，不要再重复乘倍率。

例如 Mini、没有参考视频、倍率 1，若完成用量为 40,594，则费用约为 `40594 / 1000000 × 12.65 = ¤0.5135141`，账单按站内精度结算。这只是用量计算例子，不是 4 秒固定价。

提交会先预留估算额度，成功后依据有效的视频用量结算，多退少补。正常结果中可见 usage.completion_tokens / total_tokens，但不能因为缺少 usage 就自行推定免费。明确失败的任务核对退款记录；客户端超时或停止轮询不会自动取消任务。

### 末帧、任务状态与排查

return_last_frame=true 不会让主下载入口变成图片。主视频仍从 `/v1/videos/{id}/content` 下载；完成后可通过 `/v1/tasks/{id}/artifacts` 查看是否实际存在 last_frame 制品。仅在列表有该制品时读取其返回的本站链接。

常见失败原因包括：1080p/4K 参数、4–15 范围外的普通时长、重复 text、首尾帧缺一、关键帧与参考素材混用、素材链接无法读取。按原任务返回的安全错误说明修正，不要自动重复创建。完整状态与错误处理见 [视频任务与下载](https://openpix.cc/docs/video-tasks.html)。

---

## SD 按秒视频

本页只适用于 **`SD-2.0-MZ1`**，使用“seedance低价渠道，质量自测”分组。它与 [Mini/Fast](https://openpix.cc/docs/seedance.html) 的规格和收费方式不同：**固定 720p、5–15 秒整数、按请求的输出秒数计费**。核对日期：2026-10-29。

### 最小文生视频

`POST /v1/videos`，JSON 请求。下载 [sd-text.json](https://openpix.cc/docs/examples/sd-text.json)：

```json
{
  "model": "SD-2.0-MZ1",
  "prompt": "清晨湖面上的蓝色纸船缓缓漂动，柔和光线，镜头平稳推进",
  "seconds": "5",
  "resolution": "720p",
  "ratio": "16:9",
  "generate_audio": true
}
```

```bash
curl --fail-with-body --max-time 120 \
  https://openpix.cc/v1/videos \
  -H "Authorization: Bearer $MXB_API_KEY" \
  -H 'Content-Type: application/json' \
  --data-binary @sd-text.json
```

返回的任务 ID 按[视频任务与下载](https://openpix.cc/docs/video-tasks.html)处理。默认开启生成音频；如果不需要音轨，可在下载后由自己的应用进行静音处理，不能在这个型号的请求里填写 `generate_audio:false`。

### 完整参数

| 字段 | 类型 / 必填 | 默认与合法值 | 说明 |
|---|---|---|---|
| model | string / 是 | `SD-2.0-MZ1` | 必须有对应分组权限 |
| prompt | string / 与 content.text 二选一 | 1–12000 个 Unicode 字符 | 不允许空白提示词 |
| seconds | string 或 integer / 建议显式写 | 默认 5，范围 5–15 | 必须为整数；不接受 4 秒、16 秒 |
| duration | integer 或数字字符串 / 可选 | seconds 的别名 | 建议只使用 seconds |
| resolution | string / 建议显式写 | `720p` | 不支持 480p、1080p、2K、4K |
| ratio | string / 可选 | 默认 `16:9` | 16:9、9:16、1:1、4:3、3:4、21:9、adaptive |
| images | string[] / 可选 | 最多 9 张 | 每项为有效 HTTPS 图片地址 |
| audio_urls | string[] / 可选 | 最多 3 段 | 有音频参考时至少需要一张参考图片 |
| metadata.content | array / 可选 | 最多 13 项 | 一个 text 与最多 12 个图片/音频项 |
| generate_audio | boolean / 可选 | 省略或 `true` | 不支持关闭，不能填 false |
| watermark | boolean / 可选 | 省略或 `false` | 不能填 true；此字段不构成输出无任何可见标记的保证 |
| size | string / 可选 | 720p 或下表尺寸 | 会同时约束比例，不能与 ratio 冲突 |

`metadata` 仅可使用 `duration`、`resolution`、`ratio`、`content`、`generate_audio`、`watermark`。不支持的字段会被拒绝；不要复制 Mini/Fast 的 `seed`、`return_last_frame`、`omni_reference_task_type`。

### 比例与 size

优先填写 `resolution:"720p"` 和 `ratio`。如工具只能传 size，可采用以下参数对应关系；它们是请求比例映射，不承诺最终文件逐像素不变。

| 比例 | size |
|---|---|
| 16:9 | 1280x720 |
| 9:16 | 720x1280 |
| 1:1 | 720x720 |
| 4:3 | 960x720 |
| 3:4 | 720x960 |
| 21:9 | 1680x720 |

使用 `adaptive` 时建议提供参考图片，使画面比例有明确参考。需要确定输出方向时直接传固定比例。

### 图片与音频参考

简写适合常见组合：

```json
{
  "model": "SD-2.0-MZ1",
  "prompt": "保持参考图中的人物与场景，按照音频的节奏自然运动",
  "seconds": "5",
  "resolution": "720p",
  "ratio": "16:9",
  "images": ["https://media.example.com/character.png"],
  "audio_urls": ["https://media.example.com/voice.wav"]
}
```

示例 `media.example.com` 是保留示例域名，必须替换成自己可公开访问的真实素材。图片最多 9 张、音频最多 3 段；音频不能单独提交。

也可用 `metadata.content`：

```json
{
  "model": "SD-2.0-MZ1",
  "seconds": "5",
  "resolution": "720p",
  "ratio": "16:9",
  "metadata": {
    "content": [
      {"type": "text", "text": "以参考图片为主体，根据音频节奏制作短片"},
      {"type": "image_url", "image_url": {"url": "https://media.example.com/character.png"}, "role": "reference_image"},
      {"type": "audio_url", "audio_url": {"url": "https://media.example.com/voice.wav"}, "role": "reference_audio"}
    ]
  }
}
```

两种写法二选一。使用 content 后不要同时传 images、image、input_reference 或 audio_urls；已有 text 项时也不要再传顶层 prompt。

### 明确不支持的组合

| 请求 | 处理建议 |
|---|---|
| 参考视频 video_url | 此型号不支持；查看 Mini/Fast 或 H3 |
| first_frame / last_frame | 此型号没有首尾帧模式；普通参考图片不是关键帧约束 |
| 仅音频参考 | 至少同时提供一张图片 |
| generate_audio:false | 省略该字段或使用 true |
| watermark:true | 省略该字段或使用 false |
| 4 秒或 1080p | 改为 5–15 秒、720p |
| 上传本地二进制文件或 Data URL | 先取得可公开读取的 HTTPS 素材地址 |

不支持的参数有时会在可用模型筛选阶段体现为“无可用模型/服务”，不能只等某个固定 HTTP 400 文本；先核对本页参数和 Key 分组。

### 费用计算

核对时该分组基础价为 **¤0.16 / 输出秒数**，当前倍率为 1：

| 请求 seconds | 示例费用 |
|---:|---:|
| 5 | ¤0.80 |
| 10 | ¤1.60 |
| 15 | ¤2.40 |

按请求中的名义秒数计算，不按最终 MP4 容器中多出的零点几秒重新增加费用。不能把 Mini/Fast 的视频 Token 单价套到本型号。实际适用报价与账户倍率以模型广场和账单为准。

保存任务 ID 后持续查询同一个任务；等待时间超过客户端截止时间不表示任务已失败，也不应触发自动重新提交。

---

## MiniMax-H3

本页仅描述本站当前 **`MiniMax-H3`** 的接口：视频分组、**固定 768P、1–15 秒整数**、按请求的输出秒数计费。核对日期：2026-10-29。不要套用其他平台同品牌模型的 2K、10 秒固定档或其他参数。

### 文生视频最小请求

`POST /v1/videos`。下载 [h3-text.json](https://openpix.cc/docs/examples/h3-text.json)：

```json
{
  "model": "MiniMax-H3",
  "prompt": "清晨湖面上的蓝色纸船缓缓漂动，柔和光线，镜头平稳推进",
  "seconds": "5",
  "resolution": "768P",
  "ratio": "16:9"
}
```

```bash
curl --fail-with-body --max-time 120 \
  https://openpix.cc/v1/videos \
  -H "Authorization: Bearer $MXB_API_KEY" \
  -H 'Content-Type: application/json' \
  --data-binary @h3-text.json
```

成功后保存本站 `id`，通过 `GET /v1/videos/{id}` 查询，完成后从 `/content` 下载。

### 请求字段

| 字段 | 类型 | 取值与说明 |
|---|---|---|
| model | string，必填 | `MiniMax-H3` |
| prompt | string | 1–7000 个 Unicode 字符；也可放在 content 的唯一 text 项中 |
| seconds | string 或 integer | 默认 5；1–15 秒整数 |
| duration | integer，可选 | seconds 的别名，JSON 中建议使用整数 |
| resolution | string，可选 | 默认且仅支持 `768P`，注意大写 P |
| ratio | string，可选 | 21:9、16:9、4:3、1:1、3:4、9:16、adaptive |
| images | string[]，可选 | 简写模式：第一张为首帧，第二张为尾帧，最多两张 |
| input_reference / image | string，可选 | 单张 HTTPS 图片简写，作为首帧 |
| content / metadata.content | array，可选 | 一项 text 与最多 12 项参考素材；二选一 |
| metadata.first_frame_image | string，可选 | 首帧 HTTPS URL |
| metadata.last_frame_image | string，可选 | 尾帧 HTTPS URL |
| size | string，可选 | 768P 或下表画布尺寸；不要与 ratio 冲突 |

本接口未开放统一的 `generate_audio`、`watermark`、`seed` 或 `callback_url` 参数，不要随意添加。没有自动回调，请轮询状态。

没有视觉素材时 ratio 默认 16:9，有图片或视频时默认 adaptive。为了稳定复现，建议显式传入比例。adaptive 需要图片或视频，纯文字或纯音频不能使用。

### 比例与画布

| ratio | 可用 size |
|---|---|
| 21:9 | 1536x672 |
| 16:9 | 1344x768 |
| 4:3 | 1024x768 |
| 1:1 | 768x768 |
| 3:4 | 768x1024 |
| 9:16 | 768x1344 |

这些是可接受的参数映射；成片应读取实际媒体信息。不要将 size 写成 720p、1080p 或 2K。

### 单首帧与首尾帧

单首帧请求：

```json
{
  "model": "MiniMax-H3",
  "prompt": "以首帧画面开始，镜头缓慢向前，保持产品外观一致",
  "seconds": "5",
  "resolution": "768P",
  "ratio": "16:9",
  "input_reference": "https://media.example.com/first.png"
}
```

请将示例域名替换成真实自有素材。需要首尾帧时，将 input_reference 改成：

```json
{
  "metadata": {
    "first_frame_image": "https://media.example.com/first.png",
    "last_frame_image": "https://media.example.com/last.png"
  }
}
```

这是要加入完整请求的字段片段。也可以用 `images:[首帧URL,尾帧URL]`，不要同时使用两套字段。

H3 的接口接受单首帧，也接受单尾帧的显式角色；首尾帧各最多一张。关键帧模式不能与 reference_image、reference_video、reference_audio 混用。这与 Mini/Fast 必须成对提供首尾帧的规则不同。

### 多参考图、视频与音频

三张以上图片必须用显式参考角色，不能放进 images 简写：

```json
{
  "model": "MiniMax-H3",
  "seconds": "5",
  "resolution": "768P",
  "ratio": "16:9",
  "metadata": {
    "content": [
      {"type": "text", "text": "参考人物造型与场景，制作自然流畅的短片"},
      {"type": "image_url", "image_url": {"url": "https://media.example.com/person.png"}, "role": "reference_image"},
      {"type": "video_url", "video_url": {"url": "https://media.example.com/movement.mp4"}, "role": "reference_video"},
      {"type": "audio_url", "audio_url": {"url": "https://media.example.com/music.wav"}, "role": "reference_audio"}
    ]
  }
}
```

| 类型 | 单类数量上限 | 必须使用的角色 |
|---|---:|---|
| 图片 | 9 | reference_image |
| 视频 | 3 | reference_video |
| 音频 | 3 | reference_audio |
| 全部参考素材 | 合计 12 | 不包括唯一 text 项 |

content 内必须有且只有一个 text。媒体字段统一使用 `{"url":"https://..." }` 对象格式，不能把整个媒体对象换成裸 URL 字符串。多图必须明确 role；不要依赖单图默认首帧的行为来表达参考图。

视频接口只接收可读取的 HTTPS 媒体地址，不接受此型号的二进制 multipart 文件或 Base64 图片。参考素材兼容情况要用自己的真实文件验证，不要把 Mini/Fast 的具体文件时长限制直接当作 H3 的统一限制。

### 计费与失败处理

核对时基础价为 **¤0.06 / 请求的输出秒数**，视频分组倍率为 1。1 秒约 ¤0.06，5 秒约 ¤0.30，15 秒约 ¤0.90。参考图片数量和成片容器时长不改变本规则中的名义输出秒数。

提交受理后通常先预扣，任务完成或明确失败后按账单记录核对。客户端停止等待不等于任务取消；下载失败不等于生成失败。具体状态与恢复流程见 [视频任务与下载](https://openpix.cc/docs/video-tasks.html)。

---

## Grok 视频

本站目前提供三个公开 Grok 视频型号。**名称带 `-6s` 的两个型号与 `grok-video-1.5` 使用不同的参考图字段和时长规则。** 核对日期：2026-10-29。

### 型号对照

| model | 时长 | 分辨率 / 比例 | 分组 | 核对时报价 |
|---|---|---|---|---|
| grok-imagine-video-6s | 固定 6 秒 | 720p；16:9 或 9:16 | 视频分组 | ¤0.08 / 次 |
| grok-imagine-video-1.5-6s | 固定 6 秒 | 720p；16:9 或 9:16 | 视频分组 | ¤0.10 / 次 |
| grok-video-1.5 | 接受 4、6、8、10、12、15 秒离散值 | 720p；建议 16:9 或 9:16 | default、视频分组 | ¤0.85 / 次 |

以上当前为按次计费，不是 0.08/0.10/0.85 每秒。旧的 `grok-video-1.0` 不在当前目录中，不作为新建任务示例。价格与可用性以当前模型广场和 Key 权限为准。

### 固定 6 秒：文生视频

下载 [grok-6s-text.json](https://openpix.cc/docs/examples/grok-6s-text.json)，发送至 `POST /v1/videos`：

```json
{
  "model": "grok-imagine-video-6s",
  "prompt": "清晨湖面上的蓝色纸船缓缓漂动，柔和光线，镜头平稳推进",
  "seconds": "6",
  "resolution": "720p",
  "aspect_ratio": "16:9"
}
```

如选择 `grok-imagine-video-1.5-6s`，只更换 model，其他本节参数规则保持一致。

| 字段 | 类型 | 默认 / 限制 |
|---|---|---|
| model | string，必填 | 上表两个 -6s 型号之一 |
| prompt | string，必填 | 1–7000 个 Unicode 字符，不允许空白 |
| seconds | string 或 integer | 默认且仅接受 6 或 "6" |
| resolution | string | 默认 720p；接受 720p / 720P |
| aspect_ratio | string | 默认 16:9；仅 16:9、9:16 |
| ratio | string，可选 | aspect_ratio 的别名，不要重复传冲突值 |
| size | string，可选 | 1280x720 或 720x1280；也接受对应比例字符串 |
| input_reference | string 或文件，可选 | 单张首帧图，与 reference_images 二选一 |
| reference_images | object[]，可选 | 1–7 张参考图；每项使用 {"url":"..."} |
| metadata | object，可选 | 仅 resolution、aspect_ratio、ratio、seconds、duration |

没有统一的音频参考、参考视频、首尾帧对、seed、watermark、generate_audio 开关；不要随意加入这些字段。

### 固定 6 秒：单首帧

JSON 可填写 HTTPS 图片 URL，也可使用完整的 `data:image/png;base64,...`、`data:image/jpeg;base64,...` 或 `data:image/webp;base64,...`。Data URL 必须包含真实完整的编码内容，不能直接把省略号发给 API。

```json
{
  "model": "grok-imagine-video-1.5-6s",
  "prompt": "保持首帧中的产品外观，镜头轻轻向右移动",
  "seconds": "6",
  "resolution": "720p",
  "aspect_ratio": "16:9",
  "input_reference": "https://media.example.com/first-frame.png"
}
```

示例媒体域名必须替换为自己的真实可访问地址。上传本地文件可使用 multipart：

```bash
curl --fail-with-body --max-time 120 \
  https://openpix.cc/v1/videos \
  -H "Authorization: Bearer $MXB_API_KEY" \
  -F 'model=grok-imagine-video-1.5-6s' \
  -F 'prompt=保持首帧主体一致，镜头缓慢向前' \
  -F 'seconds=6' \
  -F 'resolution=720p' \
  -F 'aspect_ratio=16:9' \
  -F 'input_reference=@reference.png;type=image/png'
```

PNG、JPEG、WebP 文件单张最多 **20 MiB**。让 curl 自动生成 Content-Type 与 boundary；不要手工增加 JSON Content-Type。

### 固定 6 秒：多参考图

```json
{
  "model": "grok-imagine-video-6s",
  "prompt": "结合人物造型和场景参考，生成自然连贯的镜头",
  "seconds": "6",
  "resolution": "720p",
  "aspect_ratio": "16:9",
  "reference_images": [
    {"url": "https://media.example.com/character.png"},
    {"url": "https://media.example.com/scene.png"}
  ]
}
```

reference_images 为 1–7 项对象数组，不是字符串数组。每项只能放 `url`。多张本地文件可重复使用 `-F 'reference_images[]=@文件路径'`，不能同时填写文本 reference_images 和上传的 reference_images[] 文件。

参考图模式与首帧模式二选一：不要同时传 input_reference 与 reference_images。已上传文件和 Data URL 的参考图累计最多 **60 MiB**，单张最多 20 MiB；Base64 传输体积更大，仍需满足入口整体请求体限制。HTTPS 链接图片也应控制大小，不能将 URL 写法当成绕过素材限制的方式。

### grok-video-1.5：可选时长

下载 [grok-variable-text.json](https://openpix.cc/docs/examples/grok-variable-text.json)：

```json
{
  "model": "grok-video-1.5",
  "prompt": "清晨湖面上的蓝色纸船缓缓漂动，柔和光线，镜头平稳推进",
  "seconds": "6",
  "resolution": "720p",
  "aspect_ratio": "16:9"
}
```

| 字段 | 推荐写法 | 说明 |
|---|---|---|
| seconds | "6"、"8"、"10"、"12"、"15" | 当前校验也接受 "4"；首次集成建议 6 秒，目标组合用实际素材确认 |
| duration | 同 seconds | 兼容别名；建议只传 seconds |
| resolution | "720p" | 此公开型号按 720p 档处理；更高 size 不能保证升级分辨率 |
| aspect_ratio | "16:9" 或 "9:16" | 显式填写避免依赖尺寸推断 |
| image_urls | string[] | 最多 7 张图片，顺序保留；推荐只使用这一套字段 |
| prompt | 非空 string | 描述运动、镜头和需要保持的主体 |

5 秒、7 秒等任意整数不能使用。未传时长的旧行为默认为 4 秒，因此建议总是显式填写。

参考图请求片段：

```json
{
  "image_urls": [
    "https://media.example.com/character.png",
    "https://media.example.com/background.png"
  ]
}
```

也存在 images、reference_images、image、input_reference 等兼容别名，它们可能被合并计数，**不要同时填写多个别名**。不要把 -6s 型号的 reference_images 对象数组与本型号的 image_urls 字符串数组混淆。

JSON 可使用 HTTPS 图片或已支持的完整图片 Data URL；multipart 使用 input_reference 文件字段。接口最多接受 7 张参考图片，不代表全部“多图 × 时长 × 内容”组合都已实际生成验证。首次接入从文生视频或单图开始，再验证自己的多图请求。

### 查询、结果与费用

统一保存本站返回的 id，使用 `GET /v1/videos/{id}` 查询，再用 `GET /v1/videos/{id}/content` 下载。不同历史任务可能携带不同兼容字段，不应依赖结果直链固定在某个 metadata 字段。

按次报价只对应当前型号的一次任务。不要把 response 中的 seconds 再乘一次按次价。若任务超时或结果传输异常，保留原 ID 查询和下载，不要自动重复 POST。详见[任务生命周期](https://openpix.cc/docs/video-tasks.html)。

---

## 参考素材与本地上传

先分清三种输入语义：**普通参考图**用于主体或风格参考，**首帧**指定起始画面，**首尾帧**约束开始与结束。相同图片 URL 放到不同字段，含义可能不同。

### 各型号支持什么

| 型号 | 单首帧 | 首尾帧对 | 普通参考图片 | 视频参考 | 音频参考 | 直接 multipart 图片文件 |
|---|---|---|---|---|---|---|
| Seedance Mini/Fast | 不单独接受 | 支持，必须成对 | 最多 9 张 | 最多 3 段 | 最多 3 段 | 不支持；先上传取得 URL |
| SD-2.0-MZ1 | 不支持关键帧模式 | 不支持 | 最多 9 张 | 不支持 | 最多 3 段，必须有图片 | 不支持；先上传取得 URL |
| MiniMax-H3 | 支持 | 支持 | 最多 9 张 | 最多 3 段 | 最多 3 段 | 不支持；先上传取得 URL |
| 两个 Grok -6s 型号 | input_reference | 不支持独立首尾帧对 | reference_images 最多 7 张 | 不支持 | 不支持 | 支持 |
| grok-video-1.5 | 参考图方式生成 | 无统一关键帧对承诺 | image_urls 最多 7 张 | 不在本页支持范围 | 不在本页支持范围 | 支持图片输入 |

Seedance Mini/Fast 和 H3 的图片/视频/音频总数最多 12 个，不能把三个单类上限相加成 15 个。关键帧模式不能与普通参考素材混用。

### 选择素材地址

输入 URL 必须真实指向媒体文件，并在整个任务处理期间可读取。

- 使用 HTTPS；不要使用电脑路径、localhost、局域网 IP、blob: 或浏览器会话 URL。
- 不能依赖登录 Cookie、自定义 Authorization 或只能自己浏览器打开的网盘页面。
- 不要在素材链接中放本站 API Key。临时签名链接应留足生成时间。
- 图片下载 URL 应返回图片字节，而不是 HTML 预览页、验证码或 JSON。
- 文件扩展名、MIME 和实际编码应一致；只改后缀不能转换格式。

公开媒体链接意味着持有链接的人可能访问素材。上传前确认素材适合用于该任务；链接不要进入公开日志或无关的聊天记录。

### 本站提供的三种上传入口

| 用途 | 请求 | 请求体 |
|---|---|---|
| 参考图片 | POST /v1/video-reference-images | JSON，image 为完整图片 Data URL |
| 参考视频 | POST /v1/video-reference-media/{model}/video | 文件原始二进制，不是 multipart |
| 参考音频 | POST /v1/video-reference-media/{model}/audio | 文件原始二进制，不是 multipart |

视频与音频入口会使用本站 Key 检查当前模型权限。model 只填写当前可用的完整 ID；SD-2.0-MZ1 只开放 audio，Grok 不通过这两个入口上传视频/音频参考。

上传只获得可引用的素材 URL，不会自动创建生成任务。拿到 URL 后再放入所选模型的请求中；上传成功也不代表全部后续生成参数合法。

### 上传一张图片

下载 [mxb-upload.py](https://openpix.cc/docs/examples/mxb-upload.py)，在本机准备真实 reference.png：

```bash
python mxb-upload.py --kind image --file reference.png
```

脚本从服务端环境变量读取 MXB_API_KEY，将图片转为 Data URL，执行：

```text
POST https://openpix.cc/v1/video-reference-images
Content-Type: application/json
Authorization: Bearer <本站 API Key>

{"image":"完整的 data:image/png;base64,..."}
```

以上是协议示意，不要把省略号直接提交。脚本会完成真实文件编码。成功响应结构为 `{"url":"本站生成的图片地址"}`；将该 url 放入 images、image_urls 或 metadata.content 中的正确字段。

推荐 PNG、JPEG、WebP。上传工具为示例主动设置图片大小上限 20 MiB，便于控制 Base64 体积；这不表示所有型号的最终图片输入能力完全相同。

### 上传参考视频

Mini/Fast 示例：

```bash
curl --fail-with-body --max-time 120 \
  https://openpix.cc/v1/video-reference-media/dreamina-seedance-2-0-mini-260615/video \
  -H "Authorization: Bearer $MXB_API_KEY" \
  -H 'Content-Type: video/mp4' \
  --data-binary @reference.mp4
```

也可运行：

```bash
python mxb-upload.py --kind video \
  --model dreamina-seedance-2-0-mini-260615 --file reference.mp4
```

不要使用 `-F file=@...`。此接口直接读取文件字节；发送 multipart 包装会导致 MIME 或媒体解析失败。

#### Mini/Fast 视频上传限制

| 项目 | 当前上传校验 |
|---|---|
| 文件大小 | 每段不超过 200 MB，按十进制字节计算 |
| 时长 | 3–10 秒 |
| 容器 / 视频编码 | MP4 或 MOV，H.264 或 H.265 |
| 视频流 | 一条实际视频流；封面附图不算视频流 |
| 内嵌音轨 | 如有，AAC 或 MP3 |
| 帧率 | 24–60 FPS |
| 每个边长 | 300–6000 像素 |
| 宽高比 | 0.4–2.5 |
| 总像素 | 407,696–8,295,044 |

这些条件要同时满足。例如 512×512 虽然边长满足，像素总数仍不足。720×1280 满足像素和比例条件；还需满足时长、格式与大小。

#### H3 视频上传限制

每段不超过 **50 MB**，至少 **2 秒**，MP4/MOV、H.264/H.265，一条视频流，已有音轨为 AAC/MP3。H3 上传规则不沿用 Mini/Fast 的 3–10 秒和像素范围。

上传返回的 effective_duration 对 H3 最多记录 15 秒；它是素材参考时长信息，不是本次生成输出时长，也不能替代 seconds。不要据此承诺任意长素材的全部内容都会影响生成。

### 上传音频

```bash
curl --fail-with-body --max-time 120 \
  https://openpix.cc/v1/video-reference-media/SD-2.0-MZ1/audio \
  -H "Authorization: Bearer $MXB_API_KEY" \
  -H 'Content-Type: audio/wav' \
  --data-binary @reference.wav
```

| 型号 | 文件大小 | 时长 | 格式与 MIME |
|---|---|---|---|
| Mini/Fast | 最多 15 MB | 2–30 秒 | MP3：audio/mpeg；PCM WAV：audio/wav 或 audio/x-wav |
| SD-2.0-MZ1 | 最多 15 MB | 2–30 秒 | 同上；生成请求必须同时有图片参考 |
| MiniMax-H3 | 最多 15 MB | 至少 2 秒 | MP3、PCM WAV，另接受 AAC / M4A |

H3 的 M4A 使用 audio/mp4 或 audio/x-m4a，AAC 使用 audio/aac。上传独立音频文件，不要把含视频画面的 MP4 当作音频上传。WAV 需要 PCM 编码，扩展名为 .wav 不足以证明符合要求。

### 上传响应与有效期

视频/音频上传成功 HTTP 200 的结构示意：

```json
{
  "url": "https://openpix.cc/reference-media/example.mp4",
  "size": 1200000,
  "ext": "mp4",
  "duration": 5,
  "effective_duration": 5,
  "width": 1280,
  "height": 720,
  "expires_at": 1793376000
}
```

URL 和时间为示意，不可直接下载。size 是字节数；duration 是素材容器时长；width/height 只在视频响应中出现。当前视频/音频素材保留窗口为约 **48 小时**，以返回 expires_at 为准；图片上传不返回相同的 expires_at 字段，不要给它强行套用音视频保留期。

应上传后及时提交生成任务，长期工作流使用自己的可靠素材存储。保留期不是生成等待时间承诺。

### 上传错误处理

| 状态 | 含义 / 操作 |
|---|---|
| 400 | 模型/素材类型不支持、空文件等，修正后再传 |
| 401 / 403 | Key 或目标模型权限不可用 |
| 413 | 文件或整个请求过大 |
| 415 | Content-Type 不符合当前素材类型 |
| 422 | 真实媒体编码、时长、像素或流结构不符合要求 |
| 429 | 上传繁忙或过于频繁，退避后再试 |
| 503 | 暂时无法验证权限，稍后重试 |
| 507 | 临时素材存储容量不足，稍后重试 |

上传失败时不要继续发送缺少素材的生成请求，也不要静默降级为纯文生视频。上传可以重试，但应避免无意义重复存储；生成 POST 必须单独遵守“提交一次、结果未知先核查”的规则。

---

## 视频任务、轮询与下载

视频任务有三个不同阶段：**提交被受理、生成达到终态、结果成功下载**。你的应用应分别记录它们。用户关闭页面、客户端超时或下载失败，都不能直接当作“没有生成”或“任务已取消”。

### 1. 创建与保存任务

`POST /v1/videos` 创建一个任务。请求头、model 和请求体见对应型号章节。正常受理返回 JSON 任务对象，主要字段如下。

| 字段 | 类型 | 含义 / 建议 |
|---|---|---|
| id | string | 本站任务 ID；一收到就持久化保存 |
| task_id | string，可选 | 旧客户端兼容字段；新接入优先 id |
| object | string | 通常为 video |
| model | string | 公开模型名 |
| status | string | 任务状态，不能只看 HTTP 200 |
| progress | integer | 通常 0–100；是进度提示，不是剩余时间 |
| created_at | integer | Unix 秒时间戳，不是毫秒 |
| completed_at | integer，可选 | 完成时间；未完成可能省略 |
| expires_at | integer，可选 | 若提供则表示相应过期提示，不能据此承诺永久可查 |
| seconds | string，可选 | 时长回显；不能据此认定实际容器时长 |
| size / resolution | string，可选 | 规格回显；以实际媒体为准 |
| usage | object，可选 | 型号相关的公开用量，可能只在完成后出现 |
| error | object，可选 | 失败说明；读取 message 与存在时的 code/type |

不要要求所有可选字段同时存在。兼容旧查询结果时可读取 `data.id`、`data.task_id` 等历史封装，但统一新接入应优先顶层 id。示例客户端会检查 ID 格式和查询响应身份。

建议自己的数据库记录：业务唯一 ID、本站入口、model、Key 的内部引用、分组、参数摘要、提交时间、task_id、最近状态、最后查询时间、下载路径和账单核对状态。不要把完整 Key 存进任务日志或分享给前端。

### 2. 查询状态

`GET /v1/videos/{task_id}`，携带原 API Key。

```bash
curl --fail-with-body --max-time 30 \
  "https://openpix.cc/v1/videos/$MXB_VIDEO_TASK_ID" \
  -H "Authorization: Bearer $MXB_API_KEY"
```

| 统一状态 | 含义 | 应做什么 |
|---|---|---|
| queued | 已受理、等待执行 | 保留 ID，间隔查询 |
| in_progress | 处理中 | 继续查询，不再创建相同任务 |
| completed | 任务报告成功 | 开始下载、校验视频文件 |
| failed | 任务报告失败 | 停止正常轮询，读错误并核对账单 |
| unknown 或未识别值 | 暂无法按已有状态处理 | 保存原响应摘要，有界续查或人工核对，不能当成功 |

兼容旧任务时，可能遇到 pending、submitted、processing、running、success、succeeded、done、failure、error、cancelled、canceled、expired 等值。客户端可以将已知成功/失败别名归一，但不要对任意未知字符串做成功判断。

普通成功结果结构示意：

```json
{
  "id": "task_example_video_001",
  "object": "video",
  "model": "dreamina-seedance-2-0-mini-260615",
  "status": "completed",
  "progress": 100,
  "seconds": "5",
  "usage": {"completion_tokens": 40594, "total_tokens": 40594}
}
```

数值与 ID 为示意；不是任意 5 秒视频的固定用量。按秒/按次视频可能没有这一套 usage。

### 3. 轮询频率与截止时间

建议初始每 **5 秒**查询一次，多个任务加入随机抖动。将三种超时分别配置：

| 超时 | 示例设置 | 到期后含义 |
|---|---:|---|
| 单次创建连接 | 120 秒 | 提交结果可能未知，不能自动重发 |
| 单次 GET 查询 | 30 秒 | 仅这一次查询失败，继续使用原 ID |
| 总等待时间 | 30 分钟 | 客户端暂停等待，可稍后继续查询 |

这些是示例配置，不是生成时延保证或服务 SLA。任务可能较慢，不能因 progress 停在某个数字或多次不变就重发 POST。

429 或临时 5xx 可对 **GET** 做有界退避。服务器提供 Retry-After 时尊重其值；未提供时逐步增加间隔并设置上限。401/403 应先修复凭据或权限，400 参数错误不能原样重试。

### 4. 下载主视频

`GET /v1/videos/{task_id}/content` 返回视频二进制。此入口需要本站 API Key；不要直接把它当作浏览器 video 标签的免鉴权 URL。

```bash
curl --fail --max-time 180 \
  "https://openpix.cc/v1/videos/$MXB_VIDEO_TASK_ID/content" \
  -H "Authorization: Bearer $MXB_API_KEY" \
  --output result.mp4
```

下载到临时文件（如 .part），校验成功后再替换正式文件。至少检查：

1. HTTP 为预期的 200，或断点请求的 206。
2. 响应确为视频，而不是被保存成 .mp4 的 JSON/HTML 错误页。
3. 文件非空，大小合理，能被实际播放器或媒体工具解码。
4. 业务要求精确尺寸、时长、帧率时，读取真实媒体信息，而不是只看请求参数。

结果链接可能临时有效。完成后及时保存到自己的存储，不要承诺本站永久托管。

### 5. HEAD 与 Range

完成后可读取响应头：

```bash
curl --head --fail --max-time 30 \
  "https://openpix.cc/v1/videos/$MXB_VIDEO_TASK_ID/content" \
  -H "Authorization: Bearer $MXB_API_KEY"
```

读取开头一段数据：

```bash
curl --fail --max-time 60 \
  "https://openpix.cc/v1/videos/$MXB_VIDEO_TASK_ID/content" \
  -H "Authorization: Bearer $MXB_API_KEY" \
  -H 'Range: bytes=0-1048575' \
  --output first-part.bin
```

支持部分内容时返回 206 与 Content-Range。若返回 200，应按完整文件处理，**不能直接追加到已有断点文件后面**。416 表示范围不适用，重新检查当前资源长度。一次只发送一个连续字节范围；本手册不承诺多段 Range。

HEAD 不能代替任务状态查询。结果尚未准备好时下载接口可能返回 400；不存在或不可访问任务可能 404，资源失效可能 410，临时下载问题可能 5xx。都先查询原任务，不应立即创建新视频。

### 6. 可用制品与末帧

某些型号可附带末帧。完成后查询：

```bash
curl --fail-with-body --max-time 30 \
  "https://openpix.cc/v1/tasks/$MXB_VIDEO_TASK_ID/artifacts" \
  -H "Authorization: Bearer $MXB_API_KEY"
```

只使用实际返回列表中的制品。Mini/Fast 请求 return_last_frame=true 且任务实际返回末帧时，才会存在 last_frame。其他型号不应假设有封面、字幕或缩略图。

制品内容通用入口为 `GET /v1/tasks/{task_id}/artifacts/{artifact_key}/content`。artifact_key 使用实际列表值，不自行猜测。带本站访问签名的返回链接应作为有访问能力的临时链接保护，不公开日志或长期缓存。

对于任何响应里的 URL：先校验 HTTPS 与预期来源，再决定访问方式。**本站 Key 只发送到固定本站 API origin**；访问外部媒体 URL 不携带本站 Key。示例客户端直接使用本站内容入口，并拒绝自动跨地址重定向。

### 7. 结果未知、失败与重新提交

| 情况 | 如何恢复 |
|---|---|
| POST 成功，已有 ID | 始终查询原 ID |
| POST 断线/超时，没拿到 ID | 标记“提交结果未知”；查看控制台任务与调用记录，避免自动重提 |
| GET 查询临时失败 | 保存 ID，稍后重试同一次查询 |
| 客户端总等待到期 | 停止本次等待，保留 ID，之后续查 |
| status=failed | 读错误、核对账单；修正原因后再决定创建新任务 |
| completed，但下载失败 | 重试原 content 入口；不要重新生成 |
| 已下载但文件损坏 | 校验下载响应和文件完整性，重新下载原任务 |

目前没有跨所有视频型号统一可依赖的客户端 Idempotency-Key 去重承诺；同样的提示词也不构成自动去重。示例程序在发送 POST 前先记录“提交结果未知”，避免进程中断后无意重复收费。

没有公开统一的 DELETE 取消任务能力。停止轮询、关闭页面、撤销请求或删除自己保存的状态文件，不等于服务端停止生成。

### 8. 错误与排查顺序

| 情况 | 检查重点 |
|---|---|
| 401 | Bearer 格式、Key 是否有效/过期/撤销 |
| 403 或权限提示 | Key 分组、模型限制、账户权限 |
| model_not_found / 无可用模型 | 完整型号、当前模型目录、型号参数、实时可用性 |
| 参数错误 | 所选型号的时长、分辨率、参考素材字段及互斥规则 |
| 素材失败 | URL 是否真正返回媒体、是否需要 Cookie、是否过期、格式是否兼容 |
| 额度不足 | 账户余额、Key 额度及提交时所需的预留额度 |
| 404 查询任务 | ID、入口、账户/原 Key、任务是否仍在可访问范围 |
| 200 且 failed | 任务层失败，不能当 HTTP 调用成功就忽略 |
| 429 / 临时 5xx | GET 退避；POST 先确定是否已经受理 |

定位时提供：公开 model、分组名称、本站任务 ID、请求时间与时区、HTTP 状态、本站 request_id（如有）、错误摘要与已脱敏参数。不要提供完整 Key、Cookie、私有素材签名或整段 Authorization。

### 9. 账单核对

按次、按秒、视频 Token 是三种独立计费方式。提交预留、完成补扣或退款可能分成多条记录，应关联同一个任务合计核对。任务进度字段、HTTP 状态、客户端的超时异常，都不是独立的收退款凭证。

更换 Key 分组会影响新提交任务的权限与报价，不能靠在查询 JSON 中加 group 改变已受理任务费用。具体规则见[分组与计费](https://openpix.cc/docs/pricing.html)。

---

## 视频代码与下载

下面的客户端覆盖“提交一次、保存 ID、轮询、下载”，适用于本手册的 7 个视频型号。**选择型号通过请求 JSON 文件完成**，程序不替你把不同型号的参数强行转换成同一套。

### 下载文件

| 文件 | 用途 |
|---|---|
| [mxb-video.py](https://openpix.cc/docs/examples/mxb-video.py) | Python 3.10+ 标准库，无需 pip 依赖 |
| [mxb-video.mjs](https://openpix.cc/docs/examples/mxb-video.mjs) | Node.js 22+，无需 npm 依赖 |
| [mxb-upload.py](https://openpix.cc/docs/examples/mxb-upload.py) | 本地参考图片、视频、音频上传 |
| [grok-6s-text.json](https://openpix.cc/docs/examples/grok-6s-text.json) | Grok 固定 6 秒 |
| [grok-6s-v15-text.json](https://openpix.cc/docs/examples/grok-6s-v15-text.json) | Grok 1.5 固定 6 秒 |
| [grok-variable-text.json](https://openpix.cc/docs/examples/grok-variable-text.json) | grok-video-1.5 |
| [seedance-mini-text.json](https://openpix.cc/docs/examples/seedance-mini-text.json) | Seedance Mini，480p / 4 秒 |
| [seedance-fast-text.json](https://openpix.cc/docs/examples/seedance-fast-text.json) | Seedance Fast，720p / 5 秒 |
| [sd-text.json](https://openpix.cc/docs/examples/sd-text.json) | SD-2.0-MZ1，720p / 5 秒 |
| [h3-text.json](https://openpix.cc/docs/examples/h3-text.json) | MiniMax-H3，768P / 5 秒 |

旧 [mxb-seedance.py](https://openpix.cc/docs/examples/mxb-seedance.py) 地址保留，现只提供当前 Mini/Fast 默认型号，仍可通过 --request 指定 JSON。新接入建议使用通用客户端。

### 准备 Key 和文件

将脚本与选中的 JSON 下载到同一目录。先修改 JSON 的 prompt，再按[鉴权与地址](https://openpix.cc/docs/authentication.html)设置自己的 Key。用 GET /v1/models 核对权限，不通过重复生成检查权限。

Bash 安全输入示例：

```bash
read -r -s -p 'API Key: ' MXB_API_KEY
export MXB_API_KEY
printf '\n'
```

PowerShell：

```powershell
$secureKey = Read-Host 'API Key' -AsSecureString
$env:MXB_API_KEY = [System.Net.NetworkCredential]::new('', $secureKey).Password
```

不要将真实 Key 写进下载的源文件、请求 JSON、命令历史或截图。不同任务使用不同的 --state 路径。

### Python：新建与续查

```bash
python mxb-video.py --request grok-6s-text.json --state my-video.json --output my-video.mp4
```

新建时必须提供 --request。程序在 POST 前创建状态文件，拿到 ID 后更新它。再次运行同一命令会读取已保存的 ID，只查询和下载，不再次生成：

```bash
python mxb-video.py --state my-video.json --output my-video.mp4
```

已有真实任务 ID 时：

```bash
python mxb-video.py --task "$MXB_VIDEO_TASK_ID" --output my-video.mp4
```

总等待时间可加 --wait 1800，单位为秒。切换备用本站入口可加 --base https://api.manxiaobai.online；已有状态文件必须继续使用原入口。

### Node.js：相同用法

```bash
node mxb-video.mjs --request h3-text.json --state h3-task.json --output h3.mp4
node mxb-video.mjs --state h3-task.json --output h3.mp4
node mxb-video.mjs --task "$MXB_VIDEO_TASK_ID" --output h3.mp4
```

第二行、第三行是恢复方式，按实际情况选用。不要为同一业务同时启动两个新建命令。Node 与 Python 使用兼容的状态文件结构，但不要并发操作同一个状态文件或输出文件。

### Windows PowerShell 运行

Python / Node 命令本身也可直接在 PowerShell 中运行。需要使用环境变量时写 $env:MXB_VIDEO_TASK_ID。

只需要原始请求时，用 curl.exe 加 JSON 文件，避免内联中文和引号转义：

```powershell
curl.exe --fail-with-body --max-time 120 "https://openpix.cc/v1/videos" -H "Authorization: Bearer $env:MXB_API_KEY" -H "Content-Type: application/json" --data-binary "@sd-text.json"
```

此命令只创建任务，不会自动轮询。手动保存返回 ID 后按[任务章节](https://openpix.cc/docs/video-tasks.html)查询；生产接入优先使用上面的持久化脚本。

### 上传本地素材后生成

```bash
python mxb-upload.py --kind image --file reference.png
python mxb-upload.py --kind video --model dreamina-seedance-2-0-mini-260615 --file reference.mp4
python mxb-upload.py --kind audio --model SD-2.0-MZ1 --file reference.wav
```

按需要执行其中一条，复制返回 url 到该型号正确的参考字段，保存新的 JSON，再执行一次生成。上传失败就停止，不静默去掉参考素材。完整限制见[素材上传](https://openpix.cc/docs/video-reference.html)。

### 状态文件与恢复原则

| 文件/情况 | 处理 |
|---|---|
| 状态文件存在且有 task_id | 继续查询此 ID，修改请求 JSON 不会创建新任务 |
| status=submission_unknown 且没有 ID | 提交结果不明，先查控制台；不能删除文件后盲目重跑 |
| 本地等待到期 | 任务可能仍在执行，保留状态文件续查 |
| 下载失败 | 使用原 ID 重试下载，不能新建任务 |
| 确定要创建新任务 | 明确完成核查后，使用新的 --state 文件名 |
| .part 文件 | 临时下载，失败时清理；不能当完整视频交付 |

示例仅把 API Key 放在内存，不写入状态文件。错误退出不会打印原始响应、完整请求头或带签名的媒体链接。为方便排障，业务系统应安全记录任务 ID 和公开错误摘要。

### 示例的验证范围与生产补充

已对各型号 JSON 和文档中的完整生成请求做当前契约校验，并用离线响应验证提交去重、续查、失败和下载错误处理。本次文档发布没有调用收费生成接口。

客户端会检查任务身份、状态和 MP4 文件头，拒绝携带 Key 自动重定向，并设置示例下载上限 512 MiB。文件头检查不等于完整解码；生产系统还应使用媒体工具验证整个文件，加入任务队列、数据库事务、监控、随机退避和账单核对。示例状态文件用于单机单任务，不替代分布式并发控制。

### Python 完整源码

```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://openpix.cc"):
        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", "done"):
                return result
            if status in ("failed", "failure", "error", "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://openpix.cc")
    parser.add_argument("--request", type=Path, help="UTF-8 JSON request file")
    parser.add_argument("--state", type=Path, default=Path("video-task.json"))
    parser.add_argument("--task", help="Existing task ID; never submits a generation")
    parser.add_argument("--output", type=Path, default=Path("video-result.mp4"))
    parser.add_argument("--wait", type=int, default=1800, help="Polling deadline in seconds")
    args = parser.parse_args()
    if args.wait <= 0:
        raise ValueError("--wait must be positive")
    client = VideoClient(os.environ.get("MXB_API_KEY", ""), args.base)
    payload = None
    if not args.task and not args.state.exists():
        if not args.request:
            raise ValueError("New tasks require --request with a model-specific JSON file")
        payload = json.loads(args.request.read_text(encoding="utf-8-sig"))
        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)
```

### Node.js 完整源码

```javascript
// Node.js 22+. No dependency, no automatic POST retry.
// Keep this state file; an unknown submission must be checked before retrying.
import { readFile, writeFile, rename, open, unlink, mkdir } from 'node:fs/promises';
import { dirname, resolve } from 'node:path';
import { pathToFileURL } from 'node:url';
import { parseArgs } from 'node:util';

export class ApiError extends Error {
  constructor(status) { super('HTTP ' + status + '; check task and usage logs'); this.status = status; }
}
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
export class VideoClient {
  constructor(key, base = 'https://openpix.cc', transport = fetch) {
    base = base.replace(/\/$/, '');
    if (!['https://openpix.cc', 'https://api.manxiaobai.online'].includes(base)) throw new Error('Invalid API origin');
    if (!key || /[\r\n]/.test(key)) throw new Error('Set MXB_API_KEY');
    this.key = key; this.base = base; this.transport = transport;
  }
  taskPath(task) {
    if (typeof task !== 'string' || !/^[A-Za-z0-9_-]{1,200}$/.test(task)) throw new Error('Invalid task ID');
    return '/v1/videos/' + encodeURIComponent(task);
  }
  async request(path, payload, timeout = 30000) {
    const response = await this.transport(this.base + path, {
      method: payload === undefined ? 'GET' : 'POST',
      headers: { Authorization: 'Bearer ' + this.key, ...(payload === undefined ? {} : { 'Content-Type': 'application/json' }) },
      body: payload === undefined ? undefined : JSON.stringify(payload),
      redirect: 'error', signal: AbortSignal.timeout(timeout),
    });
    if (!response.ok) { await response.body?.cancel(); throw new ApiError(response.status); }
    return response;
  }
  async json(path, payload) {
    const response = await this.request(path, payload, payload === undefined ? 30000 : 120000);
    const value = await response.json();
    if (!value || typeof value !== 'object' || Array.isArray(value)) throw new Error('Unexpected response');
    return value;
  }
  async wait(task, seconds, pause = sleep, now = () => performance.now()) {
    const deadline = now() + seconds * 1000;
    let delay = 5000;
    while (now() < deadline) {
      let value;
      try { value = await this.json(this.taskPath(task)); }
      catch (error) {
        const transient = error instanceof ApiError ? [429,500,502,503,504].includes(error.status)
          : error instanceof TypeError || ['TimeoutError','AbortError'].includes(error.name);
        if (!transient) throw error;
        await pause(Math.min(delay, Math.max(0, deadline - now())));
        delay = Math.min(delay * 2, 30000); continue;
      }
      if (value.id !== task) throw new Error('Response task ID mismatch');
      const status = String(value.status || '').toLowerCase();
      if (['completed','success','succeeded','done'].includes(status)) return value;
      if (['failed','failure','error','canceled','cancelled','expired'].includes(status)) throw new Error('Task failed; check error and bill');
      if (!['queued','pending','submitted','in_progress','running','processing'].includes(status)) throw new Error('Unknown state; keep task ID');
      delay = 5000;
      await pause(Math.min(delay, Math.max(0, deadline - now())));
    }
    throw new Error('Waiting stopped; resume the saved task');
  }
  async download(task, output) {
    const temporary = output + '.part';
    let file;
    try {
      const response = await this.request(this.taskPath(task) + '/content', undefined, 180000);
      if (response.status !== 200 || !response.body) { await response.body?.cancel(); throw new Error('Unexpected download'); }
      await mkdir(dirname(resolve(output)), { recursive: true });
      file = await open(temporary, 'w');
      let size = 0, first = Buffer.alloc(0);
      for await (const chunk of response.body) {
        size += chunk.length;
        if (size > 512 * 1024 * 1024) throw new Error('Example download limit: 512 MiB');
        if (first.length < 32) first = Buffer.concat([first, Buffer.from(chunk).subarray(0, 32 - first.length)]);
        await file.writeFile(chunk);
      }
      if (!first.includes(Buffer.from('ftyp'))) throw new Error('Content is not MP4');
      await file.sync(); await file.close(); file = null;
      await rename(temporary, output);
    } catch (error) {
      await file?.close().catch(() => {});
      await unlink(temporary).catch(() => {});
      throw error;
    }
  }
}
export async function getTask(client, state, payload) {
  let saved;
  try { saved = JSON.parse(await readFile(state, 'utf8')); }
  catch (error) { if (error.code !== 'ENOENT') throw error; }
  if (saved !== undefined) {
    if (saved?.base !== client.base) throw new Error('Saved origin differs');
    if (!saved.task_id) throw new Error('Submission unknown; check site records before another POST');
    client.taskPath(saved.task_id); return saved.task_id;
  }
  if (!payload || typeof payload !== 'object' || Array.isArray(payload)) throw new Error('Provide a request JSON object');
  const handle = await open(state, 'wx');
  try {
    await handle.writeFile(JSON.stringify({ base: client.base, status: 'submission_unknown' }));
    await handle.sync();
  } finally { await handle.close(); }
  const result = await client.json('/v1/videos', payload);
  const task = result.id; client.taskPath(task);
  console.log('Accepted task:', task);
  await writeFile(state + '.tmp', JSON.stringify({ base: client.base, task_id: task }));
  await rename(state + '.tmp', state);
  return task;
}
async function main() {
  const { values } = parseArgs({ options: {
    base: { type: 'string', default: 'https://openpix.cc' },
    request: { type: 'string' }, task: { type: 'string' },
    state: { type: 'string', default: 'video-task.json' },
    output: { type: 'string', default: 'video-result.mp4' },
    wait: { type: 'string', default: '1800' },
  } });
  const seconds = Number(values.wait);
  if (!Number.isFinite(seconds) || seconds <= 0) throw new Error('--wait must be positive');
  const client = new VideoClient(process.env.MXB_API_KEY, values.base);
  let payload;
  if (!values.task && values.request) payload = JSON.parse((await readFile(values.request, 'utf8')).replace(/^\uFEFF/, ''));
  const task = values.task || await getTask(client, values.state, payload);
  client.taskPath(task);
  console.log('Querying task:', task);
  await client.wait(task, seconds);
  await client.download(task, values.output);
  console.log('Saved video:', values.output);
}
if (process.argv[1] && pathToFileURL(resolve(process.argv[1])).href === import.meta.url) {
  main().catch(error => {
    console.error('Stopped (' + error.constructor.name + '). Keep the task state and check site logs; do not auto-resubmit.');
    process.exitCode = 1;
  });
}
```

### 上传工具完整源码

```python
"""Python 3.10+: upload reference media; this script does not generate a video."""
import argparse
import base64
import json
import os
import sys
import urllib.parse
import urllib.request
from pathlib import Path

ORIGINS = ("https://openpix.cc", "https://api.manxiaobai.online")
MODELS = ("dreamina-seedance-2-0-mini-260615", "dreamina-seedance-2-0-fast-260128", "MiniMax-H3", "SD-2.0-MZ1")
MIMES = {
    ".png": "image/png", ".jpg": "image/jpeg", ".jpeg": "image/jpeg", ".webp": "image/webp",
    ".mp4": "video/mp4", ".mov": "video/quicktime",
    ".mp3": "audio/mpeg", ".wav": "audio/wav", ".m4a": "audio/mp4", ".aac": "audio/aac",
}


class NoRedirect(urllib.request.HTTPRedirectHandler):
    def redirect_request(self, req, fp, code, msg, headers, newurl):
        return None


def prepare(kind, model, path):
    mime = MIMES.get(path.suffix.lower(), "")
    if not mime.startswith(kind + "/"):
        raise ValueError("File extension does not match the selected kind")
    if kind != "image" and model not in MODELS:
        raise ValueError("Video/audio upload requires a documented model")
    if model == "SD-2.0-MZ1" and kind == "video":
        raise ValueError("This model does not accept reference video")
    if kind == "audio" and model != "MiniMax-H3" and path.suffix.lower() not in (".mp3", ".wav"):
        raise ValueError("Use MP3 or PCM WAV for this model")
    limit = 20 * 1024 * 1024 if kind == "image" else (15_000_000 if kind == "audio" else (50_000_000 if model == "MiniMax-H3" else 200_000_000))
    if not 0 < path.stat().st_size <= limit:
        raise ValueError("Empty file or exceeds this upload example's size limit")
    with path.open("rb") as stream:
        data = stream.read(limit + 1)
    if len(data) > limit:
        raise ValueError("File grew beyond the size limit")
    if kind == "image":
        data_url = "data:" + mime + ";base64," + base64.b64encode(data).decode("ascii")
        return "/v1/video-reference-images", "application/json", json.dumps({"image": data_url}).encode()
    return "/v1/video-reference-media/" + urllib.parse.quote(model, safe="") + "/" + kind, mime, data


def main():
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument("--base", choices=ORIGINS, default=ORIGINS[0])
    parser.add_argument("--kind", choices=("image", "video", "audio"), required=True)
    parser.add_argument("--model", choices=MODELS)
    parser.add_argument("--file", type=Path, required=True)
    args = parser.parse_args()
    key = os.environ.get("MXB_API_KEY", "")
    if not key or "\r" in key or "\n" in key:
        raise ValueError("Set MXB_API_KEY in your server environment")
    path, mime, data = prepare(args.kind, args.model, args.file)
    request = urllib.request.Request(args.base + path, data=data, headers={
        "Authorization": "Bearer " + key, "Content-Type": mime,
    })
    with urllib.request.build_opener(NoRedirect()).open(request, timeout=120) as response:
        result = json.load(response)
    if not isinstance(result, dict) or not isinstance(result.get("url"), str):
        raise ValueError("Unexpected upload result")
    parsed = urllib.parse.urlsplit(result["url"])
    if parsed.username or parsed.password or parsed.scheme + "://" + parsed.netloc not in ORIGINS:
        raise ValueError("Unexpected upload URL origin")
    # URL is a capability: keep this output private, never publish logs containing it.
    print(json.dumps(result, ensure_ascii=False, indent=2))


if __name__ == "__main__":
    try:
        main()
    except (OSError, ValueError) as error:
        code = getattr(error, "code", None)
        print("Upload stopped" + (" (HTTP " + str(code) + ")" if code else "") + ". Check the file and model rules; no generation was submitted.", file=sys.stderr)
        sys.exit(1)
```

---

## 文生图总览

推荐使用异步入口创建一个图片任务，再查询结果。调用前先确认完整 model 在当前 Key 的目录里，按下表选择型号专章，再填写对应参数。Seedream 的 resolution 是图片档位，不能与视频分辨率或其他图片模型字段混用。

### 各系列参数入口

| 系列 | size / 规格 | 特别注意 |
|---|---|---|
| [MJ](https://openpix.cc/docs/midjourney.html) | 比例，如16:9；quality为0.25/0.5/1/2 | n=1通常交付4张单图，可另有拼图，全部计数 |
| [Seedream](https://openpix.cc/docs/seedream.html) | resolution小写档位 + size比例 | Lite支持2k/4k，Pro为1k/2k |
| [GPT Image](https://openpix.cc/docs/gpt-image.html) | 推荐像素尺寸，质量按分组 | Adobe中质量固定medium，可请求4K；福利组有特定兼容 |
| [香蕉](https://openpix.cc/docs/banana.html) | Images像素尺寸，原生imageConfig | 两种接口结构不同 |
| [Grok图片](https://openpix.cc/docs/grok-image.html) | 从1024x1024开始 | 不承诺4K |

### 异步生成

POST https://openpix.cc/v1/images/generations/async

请求使用 Bearer Key 和 application/json；正常接纳返回 HTTP **202**，随后按[图片任务](https://openpix.cc/docs/tasks.html)查询。

| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| model | string | 是 | 当前完整公开图片模型 ID |
| prompt | string | 是 | 非空图片描述，长度上限按具体型号 |
| size | string | 建议显式 | GPT/Gemini常用像素；MJ/Seedream示例用比例，按专章填写 |
| resolution | string | 型号相关 | Seedream使用2k/4k等枚举；不要对所有图片型号统一添加 |
| n | integer | 建议固定 1 | 普通图片通常一张；MJ表示一次任务，4张单图及可选四宫格，固定按次计费 |
| quality | string 或 number | 否 | 按型号填写；GPT用字符串档位，MJ用数字，Seedream最小示例省略 |
| response_format | string | 否 | 异步建议省略，结果通常为托管 URL |
| stream | boolean | 不使用 | 图片异步不提供可靠的 SSE 终态解析 |

```bash
curl --fail-with-body --max-time 60 \
  https://openpix.cc/v1/images/generations/async \
  -H "Authorization: Bearer $MXB_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"model":"gpt-image-2.5","prompt":"木桌上的白色茶杯，柔和自然光，产品摄影","size":"1024x1024","n":1}'
```

收到任务对象即保存 task_id，不要直接读取 data[0].url。后台生成失败可能发生在 HTTP 202 之后，必须继续查询终态。

### 规格与可选参数

gpt-image-2.5-flare、gpt-image-2.5-sunburst 与普通 gpt-image-2.5 是不同完整型号。使用高质量或高规格时确认当前分组权限与报价；不能把一个型号的 high/xhigh/max 直接套给其他型号。

background、output_format、output_compression、moderation、seed、mask 等字段不是本站对全部图片型号统一保证的能力。Gemini Images 适配主要处理 prompt、size、n 与参考图；需要原生配置时看[Gemini 原生接口](https://openpix.cc/docs/gemini.html)。

异步开关中的 background:true 为布尔值。背景样式的 background:"transparent" 是字符串，两者含义不同。推荐直接用 /async 避免歧义。

### 同步生成

POST /v1/images/generations，不加异步开关。它等待结果，正常响应包含 data[].url 或 data[].b64_json。

```bash
curl --fail-with-body --max-time 600 \
  https://openpix.cc/v1/images/generations \
  -H "Authorization: Bearer $MXB_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"model":"gpt-image-2.5","prompt":"一只蓝色陶瓷茶壶，产品摄影","size":"1024x1024","n":1}'
```

同步适合能保持长连接的服务端。600 秒是客户端示例超时，不是生成时延保证；网络断线不等于服务端没有生成。业务通常优先选择异步，便于保存 ID 和恢复查询。

### 同步结果示意

```json
{
  "created": 1793203200,
  "data": [
    {"url": "https://openpix.cc/generated/example.png"}
  ]
}
```

时间与链接为示意。先检查 HTTP 和 error，再检查 data 非空。结果为 b64_json 时 Base64 解码保存；URL 则下载原图。文件扩展名不会改变真实格式。

下载图片直链不要附带 API Key。使用临时文件，验证响应类型、文件头和真实图片可解码后再保存正式结果。批量任务对每张图片分别跟踪，避免把部分成功当作全部成功。

### 相关章节

参考图编辑见[图生图](https://openpix.cc/docs/edits.html)，状态和保留期见[图片任务](https://openpix.cc/docs/tasks.html)，完整客户端见[代码下载](https://openpix.cc/docs/examples.html)，质量档位与分组费用见[分组与计费](https://openpix.cc/docs/pricing.html)。

---

## MJ / Midjourney

MJ 通过本站 Images 接口提交，使用本站任务号查询。**一次请求通常返回 4 张单图，附四宫格时共 5 张；必须保存整个 `data[]`，不要只取第一项。**

本页核对于 2026-11-02。当前三个型号均有本站真实出图记录，以下高级参数范围依据当前接口校验；不代表每个参数组合都已实际生成验证。

### 型号、分组和价格

| 完整 model | Key 分组 | 当前单价 |
|---|---|---|
| `mj-v6.1` | mj和Seedream生图专用分组 | ¤0.55 / 次 |
| `mj-v7` | 同上 | ¤0.55 / 次 |
| `mj-v8.1` | 同上 | ¤0.55 / 次 |

这是分组倍率 1 的公开报价快照，当前报价以[模型广场](https://openpix.cc/pricing)为准。`mj-v8.2`、`mj-niji7` 与其他名称未列为当前可新建型号，不要自行拼接版本。

`n=1` 表示创建一次 MJ 生成任务，不表示只交付一张。**普通用户与 SVIP 均固定 ¤0.55 / 次**：提交时预留 ¤0.55，成功交付 4 张独立图片即按整次收费；有四宫格预览时附送，不额外计费，没有四宫格也不改变价格。生成失败按原有规则退还预留费用。2026-11-02 改价前已提交的任务仍按提交时的价格结算，不追补历史订单。

### 最小请求：提交一次

下载 [mj-text.json](https://openpix.cc/docs/examples/mj-text.json)，三个型号只需更换 `model`。

```json
{
  "model": "mj-v7",
  "prompt": "A blue ceramic teapot on a wooden table, soft window light, editorial product photography",
  "size": "16:9",
  "n": 1,
  "quality": 1,
  "stylize": 100
}
```

```bash
curl --fail-with-body --max-time 60 \
  https://openpix.cc/v1/images/generations/async \
  -H "Authorization: Bearer $MXB_API_KEY" \
  -H 'Content-Type: application/json' \
  --data-binary @mj-text.json
```

API Key 设置方法见[快速上手](https://openpix.cc/docs/quickstart.html)。正常接纳返回 HTTP 202 和 `task_id`，立即保存。`/v1/image-tasks/generations` 也是可用的异步入口。不要把其他 MJ 服务的 `/mj/submit/imagine` 或 `/v1/midjourney/generations` 地址替换域名后直接使用。

### 基础参数

| 字段 | 类型 | 必填 / 默认 | 规则 |
|---|---|---|---|
| model | string | 必填 | 上表三个完整 ID |
| prompt | string | 必填 | 非空描述；建议写明主体、环境、构图、光线和风格 |
| size | string | 默认 `16:9` | 比例枚举：`1:1`、`3:2`、`2:3`、`4:3`、`3:4`、`5:4`、`4:5`、`16:9`、`9:16`、`21:9` |
| n | integer | 省略或 `1` | 不能传 4 / 5 来索取这次任务的单图数量 |
| quality | number 或数值字符串 | 可省略 | `0.25`、`0.5`、`1`、`2`；不是 GPT 的 high/max |
| image | string 或 string[] | 可省略 | 单张或多张图片地址 |
| images | string[] | 可省略 | 与 image 合计最多 5 张，URL 或完整图片 Data URL |

`size` 不能填 `1024x1024`、`2K`、`4K` 或 `auto`。MJ 没有本页承诺的固定 4K 输出档；比例影响构图，最终像素以下载图片为准。不要把 `resolution=4k` 当作能保证输出 4K 的参数。

### 风格、随机性与参考权重

以下为当前接入接受的字段。可选项默认省略，交由模型使用默认值。参数对画面的具体作用、组合有效性可能随版本变化；先从最小请求逐项增加。

| 字段 | 类型与范围 | 用途 |
|---|---|---|
| stylize | number，0–1000 | 风格化强度 |
| chaos | number，0–100 | 多样性 |
| weird | number，0–3000 | 非常规风格强度 |
| seed | number，0–4294967295，建议整数 | 随机种子；相同值不保证跨版本、跨时间逐像素复现 |
| raw | boolean | 原始风格选项 |
| hd | boolean | 细节选项，不等于已保证固定 4K |
| tile | boolean | 平铺选项 |
| negative_prompt | string | 不希望出现的元素 |
| iw | number，0–3 | 图片参考权重 |
| cref / cw | string / number 0–100 | 角色参考及权重 |
| sref / sw | string / number 0–1000 | 风格参考及权重 |
| dref / dw | string / number 0–100 | 细节参考及权重 |

布尔值使用 JSON 的 `true` / `false`，不写字符串 `"true"`。权重使用数字。`cref/sref/dref` 的参考内容应遵循对应版本支持的形式；接口接受字符串不等于任意内容都能被模型使用。

### 单张与多张参考图

参考图片通过 JSON `images` 字符串数组提交。下载 [mj-reference.json](https://openpix.cc/docs/examples/mj-reference.json)，把两个示例 URL 换成可访问的真实图片地址。

```json
{
  "model": "mj-v7",
  "prompt": "保留图一的茶壶造型，参考图二的配色，生成自然光产品摄影",
  "size": "4:3",
  "n": 1,
  "images": [
    "https://media.example.com/teapot.png",
    "https://media.example.com/palette.png"
  ],
  "iw": 1
}
```

仍提交到 `/v1/images/generations/async`。也可用 JSON `/v1/images/edits/async`，但编辑入口要求至少一张参考图。MJ 当前要求 JSON，不使用 multipart 上传；本地图片可转成 `data:image/png;base64,...` 字符串。`images:[{"image_url":"..."}]` 是其他适配常见的对象形式，**MJ 本页使用字符串数组**。

图片必须能在生成时读取；将“第几张图用于什么”写入 prompt。不要把本地 `C:\...` 路径当 URL，不要同时在 image 和 images 重复放相同素材。

### 查询、读取全部图片与下载

```bash
curl --fail-with-body --max-time 30 \
  "https://openpix.cc/v1/images/tasks/$MXB_TASK_ID" \
  -H "Authorization: Bearer $MXB_API_KEY"
```

处理 `processing`、`completed`、`failed`；完整状态见[图片任务](https://openpix.cc/docs/tasks.html)。完成后遍历 `data`，兼容 `result.data`，逐张保存 URL / Base64。不要假设第一张必定是某个单图，也不要仅凭数组下标认定四宫格；实际尺寸与画面可区分单图和拼图。

本站返回的图片为可下载结果文件，没有提供可继续调用的 MJ 按钮 ID。URL 有有效期，及时下载。下载图片不携带本站 API Key。[Python / Node.js 客户端](https://openpix.cc/docs/examples.html)支持请求文件与全部图片保存；下载中断可以用原任务号继续，不能为下载再创建一次任务。

### 当前不提供的操作与排查

| 情况 | 做法 |
|---|---|
| 想点击 U1 / V1、变体、局部重绘、扩图、单独放大 | 当前只开放新的图片生成，没有原生 MJ 后续动作接口 |
| 传入 action / task_id / custom_id / index | 这些动作字段会被拒绝；不要把查询 ID 放回新生成请求 |
| quality=high/max 或 size=4K 报错 | 改成此页数字质量与比例 size |
| 请求受理后生成失败 | 查看任务 error，202 不是完成承诺 |
| 只下载一张、账单是 ¤0.55 | 检查是否只取了 data[0]；这是整次任务价格，应保存全部单图，四宫格有则附送 |
| 长时间等待 / 查询网络出错 | 保留 ID，退避重查，不重复 POST |

中文多文字排版、精确角色复现和所有参考参数组合未作统一效果保证。需要严格文字海报时，也可比较 [Seedream](https://openpix.cc/docs/seedream.html)、[GPT Image](https://openpix.cc/docs/gpt-image.html) 与[香蕉生图](https://openpix.cc/docs/banana.html)。

---

## Seedream 5.0 Lite / Pro

Seedream 是图片模型，Seedance 是视频模型。两者名称相近，但请求接口、型号与计费不同。本站 Seedream 使用 Images JSON，推荐异步提交、查询、下载。

### 型号与规格

2026-11-01 核对，Key 选择 **mj和Seedream生图专用分组**。以下为倍率 1 的当前公开价，完整价格以[模型广场](https://openpix.cc/pricing)为准。

| model | 档位 resolution | 每次输出 | 当前价格 |
|---|---|---|---|
| `seedream-5.0-lite` | `2k` / `4k`，默认 2k | 1 张 | ¤0.07 / 张 |
| `seedream-5.0` | `1k` / `2k`，默认 1k | 1 张 | ¤0.09 / 张 |

Pro 的请求 ID 是 `seedream-5.0`，不是自行添加的 `seedream-5.0-pro`。Pro 不应按 Lite 的参数传 4k；需要 4K 时选择 Lite。本站已有 Lite 双参考 4K 和 Pro 2K 成功记录，分别交付过 3840×2160 和 2816×1584；档位不代表每个比例都使用完全相同的长边。

### Lite：4K 中文海报

```json
{
  "model": "seedream-5.0-lite",
  "prompt": "商业产品海报：蓝色陶瓷杯，米白背景。主标题「一杯好时光」，副标题「让每一天从温暖开始」，中文清楚可读，留白充足。",
  "resolution": "4k",
  "size": "16:9",
  "n": 1
}
```

下载 [seedream-lite-4k.json](https://openpix.cc/docs/examples/seedream-lite-4k.json)，执行：

```bash
curl --fail-with-body --max-time 60 \
  https://openpix.cc/v1/images/generations/async \
  -H "Authorization: Bearer $MXB_API_KEY" \
  -H 'Content-Type: application/json' \
  --data-binary @seedream-lite-4k.json
```

### Pro：2K 产品图

同一入口，下载并使用 [seedream-pro-2k.json](https://openpix.cc/docs/examples/seedream-pro-2k.json)。

```json
{
  "model": "seedream-5.0",
  "prompt": "白色桌面上的陶瓷茶壶，柔和自然光，保留釉面纹理。",
  "resolution": "2k",
  "size": "16:9",
  "n": 1
}
```

### 字段与比例

| 字段 | 类型 | 要求 |
|---|---|---|
| model | string | 两个完整 ID 之一，Key 须有权限 |
| prompt | string | 非空，说明主体、背景、布局和需出现的文字 |
| resolution | string | 建议显式使用表内小写档位 |
| size | string | 本页推荐比例写法，例如 `16:9`、`1:1`、`9:16` |
| n | integer | 固定 `1` |
| image | string | 可选，单个可访问图片 URL 或图片 Data URL |
| images | string[] | 可选，多图 URL / Data URL 数组，顺序对应 prompt 中的“图一、图二” |

比例参数可用 `auto`、`1:1`、`3:2`、`2:3`、`4:3`、`3:4`、`5:4`、`4:5`、`16:9`、`9:16`、`2:1`、`1:2`、`3:1`、`1:3`、`21:9`、`9:21`。为避免档位推断差异，当前推荐显式 `resolution` 加比例 `size`，而不是只传一个像素值。

Lite 参考图数量上限为 6，Pro 为 10；本站真实验证包含 Lite 双图，满数量组合没有逐一实测。请先以所需少量素材验证效果。GPT 的 high/xhigh/max、MJ 的数字 quality 不作为 Seedream 本页已确认参数，最小请求省略 quality。

### 多参考图

将 [seedream-reference.json](https://openpix.cc/docs/examples/seedream-reference.json) 中的示例地址替换为自己的图片，提交到与文生图相同的 `/v1/images/generations/async`。

```json
{
  "model": "seedream-5.0-lite",
  "prompt": "以图一为产品主体，图二为色彩参考，制作横幅海报，标题「慢下来，喝杯茶」。",
  "resolution": "4k",
  "size": "16:9",
  "n": 1,
  "images": [
    "https://media.example.com/product.png",
    "https://media.example.com/colors.png"
  ]
}
```

图片 URL 必须完整、可访问，不能是需要登录的页面。也可用完整 Data URL；本地文件可交给[图片 Python 客户端](https://openpix.cc/docs/examples.html)的 `--reference`，程序会转换格式并按顺序追加。此页不把 JSON 参考图成功等同于所有 multipart、遮罩和精确局部重绘能力都已验证。

### 结果与效果检查

HTTP 202 后保存 task_id，用 `GET /v1/images/tasks/{task_id}` 查询。`completed` 后读取 `data[].url` 或 `result.data[].url`；同步 `/v1/images/generations` 则直接等待 data。具体字段见[任务章节](https://openpix.cc/docs/tasks.html)。

下载后检查真实尺寸、主体一致性及文字。模型能够生成中文，不代表长文逐字排版绝无错漏；此前海报实测出现过末句重复。把需要原样出现的文字用引号分开，避免同一段要求相互矛盾，关键文字成品应人工校对。

### 常见问题

| 问题 | 处理 |
|---|---|
| 请求了 4K 却选 Pro | Pro 当前是 1k/2k，改用 Lite 的 4k |
| 只写 size 后尺寸不符合预期 | 显式传 resolution 与比例，下载解码核对实际像素 |
| 参考图没有被采用 | 检查字符串数组、URL 可访问性，并说明每张图用途 |
| 收到 429 | 控制并发，先确认本次是否已有任务，不自动重复提交收费 POST |
| HTTP 200/202 但没有图片 | 区分任务回执和完成结果，查询 error 及最终状态 |

固定按张价不使用 total_tokens 算费；同一任务有预留/退款时合并核对。批量需求采用多次独立任务，每次 n=1，避免把一个 MJ 的多图行为套用到 Seedream。

---

## GPT Image 与分组规则

同一个 GPT Image 名称在不同 Key 分组可能使用不同规格和计费。先确认分组，再选完整 model；不要只看模型名称就假定“全质量、4K、按张”都相同。本页于 2026-11-02 更新两个福利分组的 SVIP 会员价，并保留 Adobe2 全质量组及 Sunburst 全参组现价。

### 型号怎么选

| 系列 | 常见完整 ID | 质量参数 |
|---|---|---|
| GPT Image 2 | `gpt-image-2`、`gpt-image-2-1k`、`gpt-image-2-2k`、`gpt-image-2-4k` | 通常 low / medium / high，固定质量组另有规则 |
| GPT Image 2.5 Flare | `gpt-image-2.5-flare` 及目录里的 -1k/-2k/-4k | 可选档位由分组决定，不与 Sunburst 混用 |
| GPT Image 2.5 Sunburst | `gpt-image-2.5-sunburst` 及目录里的 -1k/-2k/-4k | 全参组有 xhigh / max 档及对应收费 |
| 历史无后缀 2.5 | `gpt-image-2.5`、`gpt-image-2.5-1k` | 仅按当前 Key 权限使用，不等价于两个有后缀型号 |

完整清单见[型号目录](https://openpix.cc/docs/models.html)。`max` 是字符串，`msx` 是拼写错误；不要把 high/max 当作分辨率。本文的“4K”是目标档位/规格，需要查看实际下载图片的尺寸。

### 分组差异与费用

下表按当前公开配置解释规则；具体型号权限以 Key 为准，金额以模型广场选定分组后的报价为准。

| 分组 | 重要规则 |
|---|---|
| adobe中质量专属分组 | 推荐 `gpt-image-2`，旧名 `gpt-image-2-2k` 继续兼容；固定 medium；默认 2K，可明确请求 4K；普通 / SVIP 均 ¤0.06 / 张 |
| adobe2全质量专属分组 | GPT Image 2 全质量产品；low / medium / 默认 ¤0.08，high ¤0.09 / 张；各尺寸同价，普通 / SVIP 同价 |
| adobe全参专属分组 | Flare 默认 ¤0.06、xhigh ¤0.07、max ¤0.08 / 张；Sunburst 默认 ¤0.08、xhigh / max 均 ¤0.09 / 张（普通 / SVIP 同价） |
| gpt生图相对稳定高价分组 | 当前 GPT Image 2 ¤0.08 / 张、Sunburst ¤0.14 / 张；此组不提供无后缀 2.5，也不接入 Flare |
| adobe福利1k生图分组 / GPT生图福利分组 | 当前可用的 GPT2、Flare、Sunburst 基础名及 -1k：SVIP 均为 ¤0.006 / 张；普通价 GPT2 ¤0.008、Flare / Sunburst ¤0.01 / 张。已有 2K/4K 兼容规则见下节 |
| gpt生图逆向分组 | 对已配置的 GPT2/Flare/Sunburst 按 Token 计价，当前分组倍率 1.2 |
| oai和adobe混组 | 相应 GPT2/Flare/Sunburst 按 Token，当前分组倍率 2 |
| oai生图专属分组 / gpt生图az渠道 | 依当前 Key 可用型号和 Token 分类报价，不套用按张组价格 |

2026-11-02，adobe全参专属分组的 Sunburst max 从 ¤0.10 降为 ¤0.09 / 张，xhigh 保持 ¤0.09；适用于基础名和 -1k/-2k/-4k 型号。Flare 的 xhigh / max 仍为 ¤0.07 / ¤0.08。

adobe2全质量专属分组的 `gpt-image-2`、`gpt-image-2-2k`、`gpt-image-2-4k` 均采用上述两档价格。基础名显式请求 1K / 2K / 4K 时也按同一质量档收费，4K 不另加价；例如 `gpt-image-2` + `quality:"high"` + `size:"3840x2160"` 为 ¤0.09 / 张，多张按实际计费张数累加。

质量覆盖价和固定分组价可能已经是最终售价，不重复乘倍率。表格是公开售价，不代表充值现金兑换比例。缺少质量字段不代表自动使用 max。

### 4K / max 完整请求

以下适用于有相应 Sunburst 权限和能力的分组，例如当前 `gpt生图相对稳定高价分组`；不能用于 Adobe 中质量组。

```json
{
  "model": "gpt-image-2.5-sunburst",
  "prompt": "中文新品海报：蓝色陶瓷杯，白色桌面。标题「一杯好时光」，副标题「温暖，从此刻开始」，文字工整清晰。",
  "size": "3840x2160",
  "quality": "max",
  "n": 1
}
```

下载 [gpt-sunburst-4k.json](https://openpix.cc/docs/examples/gpt-sunburst-4k.json)，提交一次：

```bash
curl --fail-with-body --max-time 60 \
  https://openpix.cc/v1/images/generations/async \
  -H "Authorization: Bearer $MXB_API_KEY" \
  -H 'Content-Type: application/json' \
  --data-binary @gpt-sunburst-4k.json
```

GPT2 需要 high 时，使用其完整型号与 `quality:"high"`，不要复制 2.5 的 max。通用客户端优先传明确像素 `size`，如 `1024x1024`、`2048x2048`、`3840x2160`；服务端按目标产品适配，具体输出以原图为准。不是所有分组都允许 `4096x4096`。

### Adobe 中质量：high 自动按 medium，支持明确请求 4K

在 `adobe中质量专属分组` 中，推荐使用不带尺寸后缀的 `gpt-image-2`，通过 size 选择 1K、2K 或 4K。旧名 `gpt-image-2-2k` 继续兼容，已有请求无需修改；两种名称均为普通 / SVIP 0.06 / 张。默认尺寸为2K，所有 quality（包括 high）统一按 medium 生成并按本组价格计费；这条规则不改变全质量分组的 high。

```json
{
  "model": "gpt-image-2-2k",
  "prompt": "白色桌面上的陶瓷茶壶，柔和自然光，产品摄影",
  "size": "2560x1440",
  "quality": "medium",
  "n": 1
}
```

使用新名称请求横版4K时，推荐明确像素：

```json
{
  "model": "gpt-image-2",
  "prompt": "清爽的产品海报，横向构图",
  "quality": "high",
  "size": "3840x2160",
  "n": 1,
  "response_format": "url"
}
```

上面的 high 会按 medium 执行，普通 / SVIP 均 ¤0.06/张。也可使用 `size:"4K", aspect_ratio:"16:9"`；或省略size，使用 `resolution/image_size:"4K"`。同时填写多个尺寸字段时，明确的size优先，应避免冲突写法。

| 请求 | 实际处理 |
|---|---|
| 未填size / auto | 默认2048×2048，medium |
| 2K及以下 | 选择符合尺寸能力的可用渠道 |
| 明确4K或超过2K能力的画布 | 保留请求尺寸，选择支持该规格的可用渠道 |
| 长边超过4096或K档位超过4 | 生成前拒绝超出范围的请求 |

4K不再被本站直接拒绝，也不会自动缩小成2K再提交。生成服务的最终像素仍以实际文件为准：4K档不保证所有比例都返回4096×4096方图。上述生成行为已按实际结果核对；蒙版和真实流式能力不因本次更新获得额外保证。

### 两个福利组：保留原型号请求高分辨率

只对 `adobe福利1k生图分组` 和 `GPT生图福利分组` 的以下系列说明：GPT Image 2、2.5 Flare、2.5 Sunburst 的基础名及 -1k 别名。可继续用原 model，`size` 传 `2K`/`4K` 或明确像素；服务端处理规格兼容。已有真实返回 2048×2048 与 4096×4096 的验证记录。

```json
{
  "model": "gpt-image-2-1k",
  "prompt": "蓝色陶瓷茶壶，白色桌面，柔和自然光",
  "size": "4K",
  "n": 1
}
```

示例 Key 需有该 -1k 型号；GPT 福利组也可按自己的目录使用基础名。该兼容不等于全站所有 -1k 模型都支持 4K，尤其不把无后缀 `gpt-image-2.5` 自动视为 Sunburst。

### 参考图与提示词格式

JSON 参考图编辑可用以下形式，提交到 `/v1/images/edits/async`。当前 AZ/OAI 相关接入已兼容 `images[].image_url`，客户端无需自行模拟文件表单。

```json
{
  "model": "gpt-image-2.5-sunburst",
  "prompt": "保留图一产品外观，使用图二的配色，制作干净的竖版海报。",
  "images": [
    {
      "image_url": "https://media.example.com/product.png"
    },
    {
      "image_url": "https://media.example.com/colors.png"
    }
  ],
  "size": "1024x1536",
  "quality": "high",
  "n": 1
}
```

把两个示例 URL 换成真实图片；也可使用完整 Data URL。`prompt` 必须是顶层非空字符串，不能只在 messages、图片对象或其他字段里写描述。详细多图、multipart、URL 和大小说明见[参考图章节](https://openpix.cc/docs/edits.html)。

蒙版字段能上传不等于已经保证精确局部重绘。各产品对透明区域、参考图数量和外部图片格式的支持有差异，不能把“全参”分组名解释为所有模型均可精确遵循 mask。

### Token 账单怎么看

按 Token 的图片组要分别看文本输入、图片输入、缓存读写和输出。基础费率及分组最终价可从 `/api/pricing` 对应 `group_token_prices` 和模型广场读取；`image_input` 不应漏掉，缓存命中的图片也可能有独立 `image_cache_read`。

示例：`oai和adobe混组` 的 GPT2/Sunburst 基础文本输入 5、图片输入 8、输出 30、文本缓存读 1.25 / 百万 Token，倍率 2 后分别为 10、16、60、2.5。Flare 的基础缓存写为 2，不能混写成缓存读 1.25。实际只对本次返回并计入的用量分类收费，不能把 total_tokens 对每一分类重复计算。

用量很少不代表质量一定差，费用高也不保证文字完全正确。请同时核对真实输出、usage 与账单。[计费章节](https://openpix.cc/docs/pricing.html)解释预留、结算与分组覆盖价。

### 结果、流式与断线

异步查询到 completed 后下载图片，检查实际像素与内容。普通 JSON/任务模式为推荐方式；`stream:true` 不能作为所有 GPT 图片接入都已支持的能力，特别不要仅凭 SSE 的 HTTP 200 当成成功出图。

生成已经开始后，关闭页面、断开连接或本地超时不表示取消。服务端可能继续完成并结算，应先查看原任务或调用记录、图库；不要自动重新提交。图片 URL 可能是本站托管地址，也可能是该产品允许的官方媒体链接，下载时都不附带本站 Key。

---

## 香蕉 / Gemini 生图

香蕉生图可以使用统一 Images 异步接口，也可以使用 Gemini 原生 `generateContent`。前者便于保存任务号与轮询；后者直接返回 `candidates[].content.parts[]`，两者使用不同请求和响应结构。

### 当前型号和单价

2026-11-01，以下为 **香蕉谷歌生图分组** 倍率 1 的公开报价快照。

| model | 当前单价 | 规格说明 |
|---|---|---|
| `gemini-3-pro-image-preview` | ¤0.05 / 张 | Pro 图片；已有 4K 目标请求实际出图记录 |
| `gemini-3.1-flash-image-preview` | ¤0.038 / 张 | Flash 图片；已有 4K 目标请求实际出图记录 |
| `gemini-nano-banana-2.1` | ¤0.045 / 张 | Banana 2.1；已有 4K 目标请求实际出图记录 |
| `gemini-3.1-flash-lite-image` | ¤0.025 / 次 | 单独的 Lite 型号；不继承前三项的 4K 能力结论 |

三个已验证型号曾在横版 4K 请求下返回 5504×3072 的原图。这是实际记录，不是所有请求固定返回该尺寸。不要把名字相近的 `gemini-3.1-flash-image`（无 preview）当成此分组的 Flash 图片型号；请保留完整 ID。

### Images：4K 多文字

```json
{
  "model": "gemini-3.1-flash-image-preview",
  "prompt": "设计一张横版中文茶饮海报，标题「茶香入夏」，副标题「清爽一杯，刚刚好」，文字准确、布局清楚。",
  "size": "3840x2160",
  "n": 1
}
```

下载 [banana-4k.json](https://openpix.cc/docs/examples/banana-4k.json)：

```bash
curl --fail-with-body --max-time 60 \
  https://openpix.cc/v1/images/generations/async \
  -H "Authorization: Bearer $MXB_API_KEY" \
  -H 'Content-Type: application/json' \
  --data-binary @banana-4k.json
```

Images 的 `size` 使用像素字符串，例如 `1024x1024`、`2048x2048`、`3840x2160`。这里不要填 MJ 风格的 `16:9`。未固定档位的图片型号会按尺寸选择图像档位，真实能力仍由目标型号决定。

`n` 建议 1，quality 先省略；GPT 的 max 不等于 Gemini 的原生质量控制。需要精确选择原生档位时，用下一节的 imageConfig。

### Gemini 原生：明确比例和档位

```bash
curl --fail-with-body --max-time 600 \
  'https://openpix.cc/v1beta/models/gemini-3.1-flash-image-preview:generateContent' \
  -H "Authorization: Bearer $MXB_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{
    "contents":[{"role":"user","parts":[{"text":"中文茶饮海报，主标题「茶香入夏」，副标题「清爽一杯，刚刚好」"}]}],
    "generationConfig":{
      "responseModalities":["IMAGE"],
      "imageConfig":{"aspectRatio":"16:9","imageSize":"4K"}
    }
  }'
```

原生档位使用大写 `1K` / `2K` / `4K`（限所选模型支持），比例写入 `aspectRatio`。原生请求不使用 Images 的 prompt、size、n 和异步开关；model 在路径中，不必重复写进 body。

### 单参考图、多参考图

Images 可在 `/v1/images/edits/async` 提交非空 prompt 与 `images:[{"image_url":"完整URL或Data URL"}]`；多张图片保持数组顺序，在提示词里写清图一/图二用途。multipart 可使用重复 image / image[] 文件字段。Gemini 图片适配单张参考大小上限 25 MiB，数量与效果按具体型号验证。

原生接口则在同一 `contents[].parts[]` 里加入 `inlineData`，其中 `data` 是纯 Base64，不能带 `data:image/...;base64,` 前缀。完整示例和响应解析见[Gemini 原生接口](https://openpix.cc/docs/gemini.html)。

### 返回值、计费和排查

| 入口 | 成功结果应如何处理 |
|---|---|
| Images 异步 | 保存 task_id，查询到 completed，遍历 data[] 下载；不能把202当成图片 |
| Images 同步 | 检查 data[] 非空，读取 url / b64_json |
| Gemini 原生 | 遍历 candidates 的 parts，Base64 解码 inlineData；若只有文本则没有得到图片 |

按张/按次报价不使用 totalTokenCount 推算图片收费；原生 usage 是用量信息，最终按当前分组的计费规则。生成失败或只有文本时保留 request_id 核查，避免自动重复收费请求。

若收到权限或余额错误，先查 Key 的模型清单。400 先区分两种参数结构；429 控制并发。中文文字成品仍需检查错字、重复与漏字。精确蒙版、任意多图数量和所有格式组合不是本页统一保证的能力。

---

## Grok Imagine 图片

当前图片型号是 `grok-imagine-image`、`grok-imagine-image-2.0`、`grok-imagine-image-quality`，位于 **grok生图分组**。名称中的 image 与 [Grok 视频](https://openpix.cc/docs/grok-video.html)的 video 不是同一种接口。

### 最小请求与完整型号

```json
{
  "model": "grok-imagine-image-2.0",
  "prompt": "一只蓝色陶瓷茶壶，简洁产品摄影，白色背景",
  "size": "1024x1024",
  "n": 1
}
```

将 [grok-image.json](https://openpix.cc/docs/examples/grok-image.json) 提交到 `POST https://openpix.cc/v1/images/generations/async`，使用 Bearer Key 与 application/json。不同型号用完整 model 切换，不能通过把 quality 填成 quality 来选择 `grok-imagine-image-quality`。

```bash
curl --fail-with-body --max-time 60 \
  https://openpix.cc/v1/images/generations/async \
  -H "Authorization: Bearer $MXB_API_KEY" \
  -H 'Content-Type: application/json' \
  --data-binary @grok-image.json
```

### 参数与规格边界

| 字段 | 要求 |
|---|---|
| model | 当前 Key 可见的三个完整 ID 之一 |
| prompt | 非空字符串 |
| size | 最小示例 1024x1024；使用像素尺寸 |
| n | 推荐 1 |
| quality | 首次接入省略；不套用 GPT2.5 的 max |

已有对应型号的 1K/2K 请求成功记录，4K 请求存在明确拒绝记录；本页不将 Grok 图片标成统一支持 4K。参考图、mask、透明背景和种子行为需按所用型号确认，不从视频能力或其他图片系列类推。

### 查询与价格

保存 task_id，用 `GET /v1/images/tasks/{task_id}` 查询，完成后遍历 data[]；同步路径 `/v1/images/generations` 直接等待图片。完整错误与恢复流程见[图片任务](https://openpix.cc/docs/tasks.html)。

2026-11-01 公开报价：上述三个型号均为 ¤0.045 / 张，grok生图分组倍率 1。当前报价与权限以[模型广场](https://openpix.cc/pricing)及 Key 目录为准。不要把 Grok 语言的 Token 费率或视频按次价格套在图片上。

---

## 图生图与参考图

推荐 `POST /v1/images/edits/async`。同一接口接收参考图与提示词，创建任务后继续使用统一图片任务查询路径。

### multipart 文件上传

```bash
curl --fail-with-body --max-time 120 \
  https://openpix.cc/v1/images/edits/async \
  -H "Authorization: Bearer $MXB_API_KEY" \
  -F 'model=gpt-image-2.5' \
  -F 'prompt=保持茶壶形状与颜色，将背景改为浅绿色' \
  -F 'size=1024x1024' \
  -F 'n=1' \
  -F 'image=@reference.png;type=image/png'
```

在命令执行目录放置真实 `reference.png`；不要把 JPEG 文件只改后缀伪装成 PNG。curl 自动设置 multipart Content-Type 和 boundary。

| 字段 | 形式 | 说明 |
|---|---|---|
| model | 文本 | 完整模型 ID |
| prompt | 文本 | 描述要保留与修改的部分 |
| image | 文件 | 推荐的单参考图字段 |
| size | 文本 | 按模型选择的请求尺寸 |
| n | 文本 `1` | 普通图片建议1；MJ使用JSON、n=1仍返回多张 |
| quality | 文本，可选 | 只在已确认模型/渠道支持时使用 |

Gemini multipart 支持 PNG、JPEG、WebP、BMP 的类型识别，单张参考图大小上限为 25 MiB；渠道可能有更小限制。总 HTTP 请求体也受网关限制，JSON Base64 会扩大体积。GPT 上传能力随渠道不同，建议优先用单张 PNG/JPEG 验证。

### JSON 参考图

GPT 图片的 AZ/OAI 相关接入现已兼容 JSON 的 image/images、HTTP(S) URL、Data URL 与 b64_json，并有双参考图实测；Gemini 也有 JSON 参考图成功记录。具体支持仍按型号和分组。MJ/Seedream 使用字符串数组，见各自专章。

```json
{
  "model": "gemini-3.1-flash-image-preview",
  "prompt": "保留主体，把背景改成干净的白色",
  "images": [
    {"image_url": "data:image/png;base64,替换为完整图片的Base64内容"}
  ],
  "size": "1024x1024",
  "n": 1
}
```

`images[].image_url` 是完整 Data URL，不能包含文件路径或省略的 `...`。要避免手工拼接，使用[完整代码](https://openpix.cc/docs/examples.html)中的 `--reference reference.png`。

Gemini 还支持由服务端读取公开 HTTP/HTTPS 图片 URL；私有地址、无法解析的域名、要求 Cookie 的图片和过多重定向会被拒绝。图片 URL 必须在生成期间可访问。参考图上传或读取失败时，应停止并修复输入，不能默默改成纯文生图。

### 多参考图与遮罩

Gemini 可构造多参考图输入，multipart 可重复 `image`/`image[]` 字段。不同模型/渠道的数量与行为没有统一承诺；本手册的最小稳定示例使用单参考图。

`mask`、精确局部重绘、透明区域语义和多图顺序依赖具体渠道能力。当前不能把它们列为所有模型统一支持的编辑能力；有此需求时先对目标模型与分组做小规模验证。

### 同步与错误

将路径改为 `/v1/images/edits` 且不加异步开关即为同步请求。异步编辑失败也可能发生在 HTTP 202 之后，因此必须轮询到终态，检查 `error.message` 和图片结果，而不是只检查上传成功。

### JSON 与 multipart：两张图完整对照

GPT 的 JSON 编辑可下载 [gpt-reference.json](https://openpix.cc/docs/examples/gpt-reference.json)。prompt 是顶层非空字符串，images 每项包含 image_url。替换两张素材地址后，POST `/v1/images/edits/async`。不要用空字符串、数组或 messages 代替 prompt。

本地文件用 multipart 时：

```bash
curl --fail-with-body --max-time 120 \
  https://openpix.cc/v1/images/edits/async \
  -H "Authorization: Bearer $MXB_API_KEY" \
  -F 'model=gpt-image-2.5-sunburst' \
  -F 'prompt=保留图一产品造型，采用图二配色，制作竖版海报' \
  -F 'size=1024x1536' -F 'quality=high' -F 'n=1' \
  -F 'image[]=@product.png;type=image/png' \
  -F 'image[]=@palette.png;type=image/png'
```

curl会设置带boundary的Content-Type，不能另写 application/json；prompt使用普通表单文本，不要把它作为文件部分上传。单图用image，多图用有序image[]。

AZ/OAI这一JSON转换路径最多16张、单图50 MiB、总表单128 MiB，支持PNG/JPEG/WebP；这些是该路径上限，不能当作所有其他型号都接受这么多或这么大的图片。Gemini单张25 MiB、MJ最多5张、Seedream Lite/Pro分别6/10张，格式也不同。接近总请求上限时还要考虑Base64膨胀与网关限制。

### 出现 prompt is required 怎么查

1. 确认原始请求体的顶层prompt是非空字符串，使用JSON UTF-8或正确multipart文本字段。
2. 检查Content-Type与实际body一致，multipart的boundary不能丢失。
3. 检查多层代理或SDK是否把字段改成messages/input，或者把POST转发成了空body。
4. 参考图必须是完整可读取的图片地址；`https:///...`、省略号、需登录页面、仅文件路径都无效。
5. 仍失败时提供本站request_id、时间、完整型号、去除Key与私有素材的请求结构，保留原任务核查。

不要因为错误提到prompt就删除参考图改成纯文生图。也不要把第三方工具的“转换成官方格式”提示当成所有分组必须改格式；优先使用本站已说明的请求结构。

---

## 图片任务与轮询

可靠流程是：**提交一次 → 保存 task_id → 查询同一任务 → 校验图片 → 下载保存**。轮询重试不应变成重新提交。

### 哪些方式会创建异步任务

| 提交方式 | 正常接纳 HTTP | 说明 |
|---|---|---|
| `/v1/images/generations/async` | 202 | 推荐文生图 |
| `/v1/images/edits/async` | 202 | 推荐图生图 |
| `/v1/image-tasks/generations`、`/edits` | 202 | 兼容入口 |
| 普通 Images 路径 + `background:true` | 200 | JSON 必须是布尔 true；multipart 为文本 true |
| 普通 Images 路径 + `task_type:"async"` | 200 | 兼容开关 |
| 普通 Images 路径 + `Prefer: respond-async` | 200 | HTTP 请求头开关 |

普通路径返回 200 时，也可能是任务对象。应识别 `task_id`，不能仅凭状态码把响应当成图片。

`background:"auto"/"opaque"/"transparent"` 是背景相关字符串，不是异步开关。JSON 字符串 `"true"` 也不等于布尔 true。关闭异步时省略兼容开关；要同时表达背景字符串和异步，请使用 `/async` 路径，背景能力仍取决于渠道。

### 查询入口与归属

推荐 `GET /v1/images/tasks/{task_id}`。以下是同一任务的查询别名：

```text
/v1/image-tasks/{task_id}
/v1/images/generations/{task_id}
/v1/images/edits/{task_id}
/v1/tasks/{task_id}
```

必须携带创建任务时的**同一 API Key**。缺少 Key、Key 不匹配、任务不存在或已清理，都可能返回 404。不要仅凭 404 断定模型故障。

### 响应字段

| 字段 | 类型 | 说明 |
|---|---|---|
| id / task_id | string | 本站任务 ID，优先保存 task_id |
| object | string | 图片任务通常为 `image.generation.task` |
| status | string | 对外状态，见下表 |
| state | string | 兼容状态，如 queued/running/succeeded/failed |
| provider_status | string | 另一套兼容状态，如 in_progress/completed |
| poll_url / poll_path | string | 查询地址/路径；不要向不可信外域转发 Key |
| poll_after_ms | number | 建议轮询间隔，当前默认 3000 毫秒 |
| created_at / updated_at | number | Unix 秒时间戳，部分响应还有 created |
| completed_at / expires_at | number | 终态时间/过期时间，可能未出现 |
| data | array | 成功后的图片列表，应检查非空 |
| result | object 或其他值 | 兼容结果；正常图片结果通常包含 result.data |
| request_id | string，可选 | 请求关联 ID，不保证所有阶段都有 |
| billing | object，可选 | 网关推断的账务提示，不代替账单 |
| error | object，可选 | 通常包含 message |
| submission_uncertain | boolean，可选 | true 表示结果未知，应核对而非自动重提 |

### 状态处理

| status | 含义 | 客户端动作 |
|---|---|---|
| processing | 排队或生成中 | 按间隔 GET 查询 |
| completed | 任务层报告完成 | 检查 data/result.data 非空、图片能读取后才视为业务成功 |
| failed | 失败或提交不确定 | 停止轮询，读取错误及 submission_uncertain |
| cancelled | 取消终态兼容值 | 停止轮询；不表示当前开放了取消 API |
| expired | 过期终态兼容值 | 停止轮询；过期任务也可能直接 404 |

兼容旧客户端时可同时识别 state 的 `succeeded`，但不要漏掉正式对外的 `completed`。未知状态不能无限等待，应受客户端总截止时间控制。

### 完成与失败的结构示意

```json
{
  "task_id": "imgtask_example",
  "status": "completed",
  "state": "succeeded",
  "data": [{"url":"https://openpix.cc/generated/example.png"}],
  "result": {"data":[{"url":"https://openpix.cc/generated/example.png"}]},
  "billing": {"status":"charged"}
}
```

```json
{
  "task_id": "imgtask_example",
  "status": "failed",
  "state": "failed",
  "error": {"message":"本次请求失败，请结合 request_id 核对原因"}
}
```

示例中的任务、链接与错误文字均为结构示意。HTTP 200 的查询响应可以承载失败任务，不能只检查 HTTP 状态。

### 重试、超时与保存

- 当前没有可依赖的 `Idempotency-Key` 去重保证，重复提交可能产生多个任务和多次扣费。
- 只对网络中断、429 或临时 5xx 的 GET 查询做有界退避；遵循 Retry-After，避免高频轮询。
- 提交失败且没有任务 ID 时，记录时间与错误，先核对调用记录。不要把“没收到响应”等同于“没创建任务”。
- 客户端等待到期时保存任务 ID，之后继续查同一个任务；等待超时不是取消操作。
- 当前终态任务保留设置为约 2 小时，图片也属于临时托管。及时下载，不把任务表和临时 URL 当作永久存储。
- 在途任务不保证跨服务重启自动恢复；长时间 processing 应携带 ID 联系支持。当前没有公开取消接口。

### 多图、4K 拒绝与断开后的结算

- MJ 的一次任务返回4张单图，可附拼图。遍历全部 data[] 或 result.data[]；只读取第一张会造成结果遗漏，且不改变按实际图片数计费。
- Adobe中质量组的超规格请求会在生成前失败。显式异步入口可能先返回202，再查询到failed；这一尺寸拦截不扣生成费。详情见[GPT分组规则](https://openpix.cc/docs/gpt-image.html)。
- 已经开始处理的合法生成不会因用户关闭页面或本地超时自动取消；可能继续完成、保留图片并结算。先查询原任务、账号内的图片历史及调用记录。
- 普通图片任务和临时托管通常约2小时，官方签名链接也有自己的时效；以实际expires_at及链接状态为准，及时下载。图库有记录不代表过期图片仍可永久取回。

---

## Gemini 原生接口

需要直接使用 Gemini 的 contents/parts、原生图片输入或原生响应时，使用本章接口。希望统一任务 ID 与异步轮询时，使用[Images 接口](https://openpix.cc/docs/images.html)。两者不能混用响应解析方式。

### 原生文生图

`POST /v1beta/models/{model}:generateContent`

本站可使用同一 Bearer Key 鉴权。URL 中填写有权限的公开模型 ID；路径中的模型决定路由，body 不需要再填写 model。

```bash
curl --fail-with-body --max-time 600 \
  'https://openpix.cc/v1beta/models/gemini-3.1-flash-image-preview:generateContent' \
  -H "Authorization: Bearer $MXB_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{
    "contents": [{"role":"user","parts":[{"text":"一只蓝色陶瓷茶壶，产品摄影"}]}],
    "generationConfig": {
      "responseModalities": ["IMAGE"],
      "imageConfig": {"aspectRatio":"1:1","imageSize":"1K"}
    }
  }'
```

原生接口直接等待结果，不会因添加 Images 的 `background:true` 而返回本站图片任务 ID。当前没有可用于替代此接口的本站 `/v1beta/interactions` 接入流程。

### 字段与规格

| 字段 | 类型 | 说明 |
|---|---|---|
| contents | array | 至少一组内容 |
| contents[].role | string | 示例为 user |
| contents[].parts | array | 文本和图片输入 |
| parts[].text | string | 提示词 |
| parts[].inlineData.mimeType | string | 如 image/png |
| parts[].inlineData.data | string | 纯 Base64 内容，不带 data: 前缀 |
| generationConfig.responseModalities | array | 生图示例使用 IMAGE |
| generationConfig.imageConfig.aspectRatio | string | 如 1:1、16:9；按模型能力 |
| generationConfig.imageConfig.imageSize | string | 1K/2K/4K 等目标档位，须确认当前型号支持及 Key 权限 |

当前 Pro/Flash 公开型号使用无档位后缀的名称。原生路径不经过 Images 的尺寸适配，应显式填写该型号支持的 imageSize；不要为模型自行拼接目录中不存在的 -1k/-2k/-4k。

### 原生参考图

在同一个 parts 数组加入图片：

```json
{
  "contents": [{
    "role": "user",
    "parts": [
      {"text":"保留茶壶主体，把背景改成浅绿色"},
      {"inlineData":{"mimeType":"image/png","data":"替换为完整图片的纯Base64内容"}}
    ]
  }],
  "generationConfig": {
    "responseModalities": ["IMAGE"],
    "imageConfig": {"aspectRatio":"1:1","imageSize":"1K"}
  }
}
```

参考图应为真实可解码图片，MIME 必须对应其实际格式。原生路径与 Images 的上传限制及参数适配不完全相同，不能把一条路径的行为套给另一条。

### 解析结果

遍历 `candidates[].content.parts[]`，寻找 `inlineData`（兼容客户端也可识别 `inline_data`），读取 mimeType 和 data，Base64 解码后保存。不要把原生响应解析成 OpenAI 的 `data[0].url`。

没有图片时，检查返回的 `error`、候选 `finishReason`、文本 parts 和 `promptFeedback`。HTTP 200 但没有图片不是可下载的成功结果。不要因无图自动重复发送收费 POST。

### AS Gemini 语言与视频理解

原生generateContent也用于语言及视频理解，但必须换成有权限的语言型号。MP4传入inlineData.mimeType=video/mp4，data是整文件纯Base64；不要放到生图imageConfig里。五个已验证型号、完整工具往返、视频脚本及1.8倍率见[AS Gemini专章](https://openpix.cc/docs/as-gemini.html)。

---

## 图片代码与下载

Python 3.10+ / Node.js 22+，无需第三方SDK。读取服务端环境变量 `MXB_API_KEY`，提交一次、保存任务号、轮询、**保存全部返回图片**。MJ一次4/5张也会完整保存。

下载：[Python](https://openpix.cc/docs/examples/mxb-image.py) · [Node.js](https://openpix.cc/docs/examples/mxb-image.mjs) · [AS Gemini工具/视频理解Python](https://openpix.cc/docs/examples/mxb-gemini.py) · [完整Markdown](https://openpix.cc/docs/manxiaobai-api-docs.md)。Key设置见[快速上手](https://openpix.cc/docs/quickstart.html)，视频生成另见[视频代码](https://openpix.cc/docs/video-examples.html)。

### 可下载的请求文件

| 目的 | JSON | 接口与准备 |
|---|---|---|
| MJ文生图 | [mj-text.json](https://openpix.cc/docs/examples/mj-text.json) | MJ专用组；比例size、数字quality |
| MJ双参考 | [mj-reference.json](https://openpix.cc/docs/examples/mj-reference.json) | 替换两个素材URL |
| Seedream Lite 4K | [seedream-lite-4k.json](https://openpix.cc/docs/examples/seedream-lite-4k.json) | resolution=4k |
| Seedream Pro 2K | [seedream-pro-2k.json](https://openpix.cc/docs/examples/seedream-pro-2k.json) | resolution=2k |
| Seedream双参考 | [seedream-reference.json](https://openpix.cc/docs/examples/seedream-reference.json) | 替换两个素材URL |
| Sunburst 4K/max | [gpt-sunburst-4k.json](https://openpix.cc/docs/examples/gpt-sunburst-4k.json) | 需要相应型号与规格权限 |
| Adobe中质量 | [adobe-medium-2k.json](https://openpix.cc/docs/examples/adobe-medium-2k.json) | 固定medium，默认2K；4K写法见GPT图片章节 |
| GPT双参考 | [gpt-reference.json](https://openpix.cc/docs/examples/gpt-reference.json) | 替换URL，编辑入口 |
| 香蕉4K | [banana-4k.json](https://openpix.cc/docs/examples/banana-4k.json) | Flash图片完整preview后缀 |
| Grok图片 | [grok-image.json](https://openpix.cc/docs/examples/grok-image.json) | 图片型号，非视频 |

示例JSON不含Key。文件中 `media.example.com` 是素材占位，不能直接用于生成；换成自己的可读取URL，或删掉占位images后通过下面的本地参考参数传图。

### Python：MJ完整生成与全部下载

把Python与对应JSON下载到同一工作目录，在已设置MXB_API_KEY的终端运行：

```bash
python mxb-image.py --request mj-text.json --output-dir mj-run-01
```

结果包括 submission.json（提交状态/任务号）、result.json（最终响应）以及 image-01.png 等实际图片文件。扩展名依据文件头选择，不把JPEG仅改名为PNG。每次新生成使用一个新的输出目录，避免误重复提交。

继续查询已有任务、恢复下载（不会重新生成）：

```bash
python mxb-image.py --task "$MXB_TASK_ID" --output-dir mj-run-01
```

本地多参考图片，使用原文生图JSON加两个素材，不需要先手工上传：

```bash
python mxb-image.py --request seedream-lite-4k.json \
  --reference product.png --reference palette.png --output-dir seedream-run-01
```

`--reference` 按顺序追加。MJ/Seedream编码为字符串数组并走生成入口；GPT/Gemini编码为image_url对象并自动走编辑入口。需要明确编辑路径时用 `--edit`，但目标模型必须支持。

不读取文件时也可用基本参数：

```bash
python mxb-image.py --model gemini-3.1-flash-image-preview \
  --size 3840x2160 --prompt "横版中文茶饮海报，标题茶香入夏" --output-dir banana-run-01
```

使用 `--request` 时，以JSON文件中的model/prompt/size/quality等为准；不要再用其他基本参数期待覆盖文件。等待时限由 `--timeout` 控制，默认1800秒，超时不取消服务端任务。

### Node.js：请求文件与续查

```bash
# Bash；已设置MXB_API_KEY
unset MXB_TASK_ID
export MXB_REQUEST='mj-text.json'
export MXB_OUTPUT_DIR='mj-node-run-01'
node mxb-image.mjs

# 用真实任务号续查，不发起新生成
export MXB_TASK_ID='替换为真实任务号'
node mxb-image.mjs
```

可选变量：MXB_MODEL / MXB_PROMPT / MXB_SIZE（没有MXB_REQUEST时使用）；MXB_REFERENCE单图；MXB_REFERENCES为JSON字符串数组（多图时使用，例如 `["product.png","palette.png"]`）；MXB_EDIT=true强制编辑。输出同样保存全部图片。

### Windows PowerShell 7

```powershell
$secureKey = Read-Host 'API Key' -AsSecureString
$env:MXB_API_KEY = [System.Net.NetworkCredential]::new('', $secureKey).Password
python .\mxb-image.py --request .\mj-text.json --output-dir .\mj-run-01
```

Node.js对应写法：

```powershell
Remove-Item Env:MXB_TASK_ID -ErrorAction SilentlyContinue
$env:MXB_REQUEST = '.\mj-text.json'
$env:MXB_OUTPUT_DIR = '.\mj-node-run-01'
node .\mxb-image.mjs
```

以上两种方式任选一种；两者都执行就会创建两次任务并分别计费。不要把Bash的反斜杠续行直接贴入PowerShell。

### 失败、恢复与示例边界

提交前先创建submission.json。如果该文件已存在，脚本拒绝新POST，避免把一次提交失败当成未受理重复收费。先检查其中的task_id和调用记录；需要新作品时用新的输出目录。提交超时且没有任务号应人工核对，脚本不擅自重试。

查询可对临时GET错误做有界退避；状态失败、身份不符、未知状态或结果为空会停止。下载失败时已经保存的任务号与result.json还在，使用原任务续查/重新下载，不重新生成。有效期见[图片任务](https://openpix.cc/docs/tasks.html)。

示例每张参考最多25MiB、下载图片最多64MiB，这些是客户端保守上限，不是所有模型的能力上限。代码检查Content-Type和常见文件头，生产系统仍应使用图像解码库验证完整像素，按业务配置存储和大小限制。结果URL下载不携带API Key，API鉴权请求不跟随重定向。

代码与模型参数按当前实现和既有实际出图记录核对，发布时进行离线流程测试。本次文档更新没有新发起付费生成，不把离线测试当成所有模型/参数组合的新实测。

### Python完整源码

```python
#!/usr/bin/env python3
"""Python 3.10+, standard library. Submit once, resume, save ALL returned images."""
import argparse
import base64
import json
import mimetypes
import os
import re
import time
import urllib.error
import urllib.parse
import urllib.request
from pathlib import Path

ORIGIN = 'https://openpix.cc'
LIMIT = 64 * 1024 * 1024  # Client download bound, not the API specification.


class NoRedirect(urllib.request.HTTPRedirectHandler):
    def redirect_request(self, req, fp, code, msg, headers, newurl):
        return None


class APIError(RuntimeError):
    def __init__(self, code, message, retry_after=None):
        super().__init__(f'HTTP {code}: {message}')
        self.code, self.retry_after = code, retry_after


def request(key, path, payload=None, timeout=30):
    if not path.startswith('/v1/'):
        raise ValueError('Expected a fixed /v1/ path')
    req = urllib.request.Request(ORIGIN + path,
        data=None if payload is None else json.dumps(payload, ensure_ascii=False).encode('utf8'),
        headers={'Authorization': 'Bearer ' + key, 'Content-Type': 'application/json'},
        method='GET' if payload is None else 'POST')
    try:
        with urllib.request.build_opener(NoRedirect()).open(req, timeout=timeout) as response:
            result = json.load(response)
    except urllib.error.HTTPError as error:
        raise APIError(error.code, error.read(2048).decode('utf8', 'replace'), error.headers.get('Retry-After')) from error
    if not isinstance(result, dict) or (result.get('error') and not (result.get('status') or result.get('state'))):
        raise RuntimeError('API returned an error or invalid JSON object')
    return result


def prepare(body, references=(), force_edit=False):
    if not isinstance(body, dict) or not isinstance(body.get('model'), str):
        raise ValueError('Request must contain model')
    if not isinstance(body.get('prompt'), str) or not body['prompt'].strip():
        raise ValueError('prompt must be a nonempty string')
    body = dict(body)
    string_refs = body['model'].startswith(('mj-', 'seedream-'))
    if references:
        images = list(body.get('images', []))
        for name in references:
            file = Path(name)
            mime = mimetypes.guess_type(file.name)[0]
            if mime not in ('image/png', 'image/jpeg', 'image/webp'):
                raise ValueError('Local references must be PNG/JPEG/WebP')
            if not 0 < file.stat().st_size <= 25 * 1024 * 1024:
                raise ValueError('Example accepts each reference up to 25 MiB; model limits may be lower')
            url = 'data:' + mime + ';base64,' + base64.b64encode(file.read_bytes()).decode('ascii')
            images.append(url if string_refs else {'image_url': url})
        body['images'] = images
    edit = force_edit or (bool(body.get('images') or body.get('image')) and not string_refs)
    return body, '/v1/images/' + ('edits' if edit else 'generations') + '/async'


def task_id(value):
    if not isinstance(value, str) or not re.fullmatch(r'[A-Za-z0-9_-]+', value):
        raise ValueError('Invalid task ID; check logs before another POST')
    return value


def wait(key, identifier, seconds=1800):
    identifier = task_id(identifier)
    deadline, delay = time.monotonic() + seconds, 3
    while time.monotonic() < deadline:
        try:
            task = request(key, '/v1/images/tasks/' + identifier)
        except APIError as error:
            if error.code not in (429, 500, 502, 503, 504):
                raise
            try:
                delay = min(30, max(1, float(error.retry_after)))
            except (ValueError, TypeError):
                delay = min(30, delay * 1.5)
            time.sleep(delay)
            continue
        except (urllib.error.URLError, TimeoutError):
            time.sleep(delay)
            delay = min(30, delay * 1.5)
            continue
        returned = task.get('task_id') or task.get('id')
        if returned and returned != identifier:
            raise RuntimeError('Task ID mismatch')
        if task.get('submission_uncertain'):
            raise RuntimeError('Submission uncertain; inspect original task and billing, do not resubmit')
        status = str(task.get('status') or task.get('state') or '').lower()
        if status in ('completed', 'succeeded', 'success'):
            nested = task.get('result')
            data = task.get('data') or (nested.get('data') if isinstance(nested, dict) else None)
            if not isinstance(data, list) or not data or not all(isinstance(x, dict) for x in data):
                raise RuntimeError('Completed task has no valid images')
            return data, task
        if status in ('failed', 'failure', 'error', 'cancelled', 'canceled', 'expired'):
            raise RuntimeError('Task failed: ' + json.dumps(task.get('error'), ensure_ascii=False))
        if status not in ('processing', 'queued', 'running', 'pending', 'in_progress'):
            raise RuntimeError('Unknown task status: ' + status)
        value = task.get('poll_after_ms', 3000)
        delay = min(30, max(1, value / 1000)) if isinstance(value, (int, float)) else 3
        time.sleep(delay)
    raise TimeoutError('Resume using --task ' + identifier + '. Server task is not cancelled.')


def image_extension(data):
    if data.startswith(b'\x89PNG\r\n\x1a\n'): return '.png'
    if data.startswith(b'\xff\xd8\xff'): return '.jpg'
    if data[:4] == b'RIFF' and data[8:12] == b'WEBP': return '.webp'
    if data[:6] in (b'GIF87a', b'GIF89a'): return '.gif'
    raise RuntimeError('Invalid or unsupported image signature; no output file replaced')


def save_images(items, directory):
    directory = Path(directory)
    directory.mkdir(parents=True, exist_ok=True)
    saved = []
    for i, item in enumerate(items, 1):
        if item.get('url'):
            url = urllib.parse.urlsplit(item['url'])
            if url.scheme not in ('https', 'http') or url.username or url.password or not url.hostname:
                raise ValueError('Invalid image result URL')
            # Separate request: no API Key sent to result URLs or redirects.
            with urllib.request.urlopen(item['url'], timeout=120) as response:
                if not response.headers.get('Content-Type', '').lower().startswith('image/'):
                    raise RuntimeError('Result URL did not return an image')
                data = response.read(LIMIT + 1)
        elif item.get('b64_json'):
            if len(item['b64_json']) > (LIMIT + 2) // 3 * 4:
                raise RuntimeError('Image exceeds client limit')
            data = base64.b64decode(item['b64_json'], validate=True)
        else:
            raise RuntimeError('Missing image URL/Base64')
        if not data or len(data) > LIMIT:
            raise RuntimeError('Image empty or exceeds client limit')
        target = directory / f'image-{i:02d}{image_extension(data)}'
        temp = target.with_suffix(target.suffix + '.part')
        temp.write_bytes(data)
        os.replace(temp, target)
        saved.append(str(target))
    return saved


def main():
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument('--request', help='UTF-8 JSON request; preserves model-specific fields')
    parser.add_argument('--model', default='gpt-image-2.5')
    parser.add_argument('--prompt')
    parser.add_argument('--size', default='1024x1024')
    parser.add_argument('--reference', action='append', default=[])
    parser.add_argument('--edit', action='store_true')
    parser.add_argument('--task', help='Resume; sends no generation POST')
    parser.add_argument('--timeout', type=int, default=1800)
    parser.add_argument('--output-dir', default='images')
    args = parser.parse_args()
    key = os.environ.get('MXB_API_KEY')
    if not key: parser.error('Set MXB_API_KEY')
    if args.timeout <= 0: parser.error('--timeout must be positive')
    directory = Path(args.output_dir)
    directory.mkdir(parents=True, exist_ok=True)
    identifier = args.task
    if not identifier:
        body = json.loads(Path(args.request).read_text(encoding='utf-8-sig')) if args.request else {
            'model': args.model, 'prompt': args.prompt, 'size': args.size, 'n': 1}
        body, path = prepare(body, args.reference, args.edit)
        state_file = directory / 'submission.json'
        state = {'state': 'submitting', 'created_at': int(time.time()), 'model': body['model'], 'path': path}
        # Exclusive state file prevents accidentally repeating a billable POST in the same directory.
        with state_file.open('x', encoding='utf8') as stream:
            json.dump(state, stream, ensure_ascii=False, indent=2)
        try:
            created = request(key, path, body, timeout=60)  # Exactly one POST.
            identifier = task_id(created.get('task_id') or created.get('id'))
        except Exception:
            state['state'] = 'submission_uncertain'
            state_file.write_text(json.dumps(state, indent=2), encoding='utf8')
            raise RuntimeError('Submission not retried. Inspect calling logs before creating another task.')
        state.update(state='accepted', task_id=identifier)
        state_file.write_text(json.dumps(state, indent=2), encoding='utf8')
        print('Task:', identifier, flush=True)
    data, final = wait(key, identifier, args.timeout)
    (directory / 'result.json').write_text(json.dumps(final, ensure_ascii=False, indent=2), encoding='utf8')
    for name in save_images(data, directory): print('Saved:', name)


if __name__ == '__main__': main()
```

### Node.js完整源码

```javascript
// Node.js 22+. Submit once; resume with MXB_TASK_ID; save ALL returned images.
import {readFile,writeFile,mkdir,rename,stat} from 'node:fs/promises';
import {extname,join} from 'node:path';
import {pathToFileURL} from 'node:url';
const origin='https://openpix.cc',limit=64*1024*1024;
const pause=ms=>new Promise(resolve=>setTimeout(resolve,ms));
export class APIError extends Error {
  constructor(status,body,retryAfter){super(`HTTP ${status}: ${body.slice(0,2048)}`);this.status=status;this.retryAfter=retryAfter;}
}
export async function request(key,path,body,timeout=30000){
  if(!path.startsWith('/v1/'))throw new Error('Expected fixed /v1/ path');
  const r=await fetch(origin+path,{method:body===undefined?'GET':'POST',redirect:'error',
    headers:{Authorization:`Bearer ${key}`,'Content-Type':'application/json'},
    body:body===undefined?undefined:JSON.stringify(body),signal:AbortSignal.timeout(timeout)});
  if(!r.ok)throw new APIError(r.status,await r.text(),r.headers.get('retry-after'));
  const value=await r.json();
  if(!value||typeof value!=='object'||Array.isArray(value)||(value.error&&!(value.status||value.state)))throw new Error('Invalid API response');
  return value;
}
export async function prepare(input,references=[],forceEdit=false){
  if(!input||typeof input.model!=='string'||typeof input.prompt!=='string'||!input.prompt.trim())throw new Error('model and nonempty prompt required');
  const body={...input},stringRefs=/^(mj-|seedream-)/.test(body.model);
  if(references.length){
    body.images=[...(body.images||[])];
    for(const file of references){
      const mime={'.png':'image/png','.jpg':'image/jpeg','.jpeg':'image/jpeg','.webp':'image/webp'}[extname(file).toLowerCase()];
      const {size}=await stat(file);
      if(!mime||size<=0||size>25*1024*1024)throw new Error('Local reference must be PNG/JPEG/WebP up to 25 MiB; model limits may be lower');
      const url=`data:${mime};base64,${(await readFile(file)).toString('base64')}`;
      body.images.push(stringRefs?url:{image_url:url});
    }
  }
  const edit=forceEdit||(!stringRefs&&Boolean(body.image||body.images?.length));
  return {body,path:`/v1/images/${edit?'edits':'generations'}/async`};
}
export function taskId(id){if(typeof id!=='string'||!/^[A-Za-z0-9_-]+$/.test(id))throw new Error('Invalid task ID');return id;}
export async function wait(key,id,timeout=1800000){
  taskId(id);const deadline=Date.now()+timeout;let delay=3000;
  while(Date.now()<deadline){
    let t;
    try{t=await request(key,'/v1/images/tasks/'+id);}
    catch(e){
      if(e instanceof APIError&&![429,500,502,503,504].includes(e.status))throw e;
      if(!(e instanceof APIError)&&!['TypeError','TimeoutError','AbortError'].includes(e.name))throw e;
      const retry=e.retryAfter==null?NaN:Number(e.retryAfter)*1000;
      delay=Number.isFinite(retry)?Math.max(1000,Math.min(30000,retry)):Math.min(30000,delay*1.5);
      await pause(delay);continue;
    }
    if((t.task_id||t.id)&&(t.task_id||t.id)!==id)throw new Error('Task ID mismatch');
    if(t.submission_uncertain)throw new Error('Submission uncertain; inspect original task before another POST');
    const status=String(t.status||t.state||'').toLowerCase();
    if(['completed','succeeded','success'].includes(status)){
      const data=t.data?.length?t.data:t.result?.data;
      if(!Array.isArray(data)||!data.length||!data.every(x=>x&&typeof x==='object'))throw new Error('No valid images');
      return {data,task:t};
    }
    if(['failed','failure','error','cancelled','canceled','expired'].includes(status))throw new Error(`Task failed: ${JSON.stringify(t.error)}`);
    if(!['processing','queued','running','pending','in_progress'].includes(status))throw new Error('Unknown task status: '+status);
    delay=typeof t.poll_after_ms==='number'?Math.max(1000,Math.min(30000,t.poll_after_ms)):3000;await pause(delay);
  }
  throw new Error(`Wait ended; resume with MXB_TASK_ID=${id}. Server task is not cancelled.`);
}
export function imageExtension(b){
  if(b.subarray(0,8).equals(Buffer.from([137,80,78,71,13,10,26,10])))return '.png';
  if(b.subarray(0,3).equals(Buffer.from([255,216,255])))return '.jpg';
  if(b.toString('ascii',0,4)==='RIFF'&&b.toString('ascii',8,12)==='WEBP')return '.webp';
  if(['GIF87a','GIF89a'].includes(b.toString('ascii',0,6)))return '.gif';
  throw new Error('Invalid image signature');
}
export async function saveImages(items,directory){
  await mkdir(directory,{recursive:true});const names=[];
  for(const [i,item] of items.entries()){
    let bytes;
    if(item.url){
      const url=new URL(item.url);
      if(!['https:','http:'].includes(url.protocol)||url.username||url.password)throw new Error('Invalid image URL');
      const r=await fetch(url,{signal:AbortSignal.timeout(120000)}); // No API credentials.
      if(!r.ok||!r.headers.get('content-type')?.startsWith('image/'))throw new Error('Download is not an image');
      const chunks=[];let length=0;
      for await(const chunk of r.body){length+=chunk.length;if(length>limit)throw new Error('Image exceeds client limit');chunks.push(chunk);}
      bytes=Buffer.concat(chunks);
    }else if(item.b64_json){
      if(item.b64_json.length>Math.ceil(limit/3)*4)throw new Error('Image exceeds client limit');
      bytes=Buffer.from(item.b64_json,'base64');
    }else throw new Error('Missing URL/Base64');
    if(!bytes.length||bytes.length>limit)throw new Error('Empty or oversized image');
    const name=join(directory,`image-${String(i+1).padStart(2,'0')}${imageExtension(bytes)}`);
    await writeFile(name+'.part',bytes);await rename(name+'.part',name);names.push(name);
  }
  return names;
}
async function main(){
  const key=process.env.MXB_API_KEY;if(!key)throw new Error('Set MXB_API_KEY');
  const directory=process.env.MXB_OUTPUT_DIR||'images';await mkdir(directory,{recursive:true});
  let id=process.env.MXB_TASK_ID;
  if(!id){
    const input=process.env.MXB_REQUEST?JSON.parse((await readFile(process.env.MXB_REQUEST,'utf8')).replace(/^\uFEFF/,'')):{
      model:process.env.MXB_MODEL||'gpt-image-2.5',prompt:process.env.MXB_PROMPT,size:process.env.MXB_SIZE||'1024x1024',n:1};
    const refs=process.env.MXB_REFERENCES?JSON.parse(process.env.MXB_REFERENCES):(process.env.MXB_REFERENCE?[process.env.MXB_REFERENCE]:[]);
    const {body,path}=await prepare(input,refs,process.env.MXB_EDIT==='true');
    const stateFile=join(directory,'submission.json'),state={state:'submitting',model:body.model,path,created_at:Date.now()};
    await writeFile(stateFile,JSON.stringify(state,null,2),{flag:'wx'});
    try{const created=await request(key,path,body,60000);id=taskId(created.task_id||created.id);}
    catch(e){await writeFile(stateFile,JSON.stringify({...state,state:'submission_uncertain'},null,2));throw new Error('Submission not retried; inspect calling logs before another POST. '+e.message);}
    await writeFile(stateFile,JSON.stringify({...state,state:'accepted',task_id:id},null,2));console.log('Task:',id);
  }
  const {data,task}=await wait(key,id);await writeFile(join(directory,'result.json'),JSON.stringify(task,null,2));
  for(const name of await saveImages(data,directory))console.log('Saved:',name);
}
if(process.argv[1]&&import.meta.url===pathToFileURL(process.argv[1]).href)main().catch(e=>{console.error(e.message);process.exitCode=1;});
```

### AS Gemini完整源码

使用方法与计费见[AS Gemini专章](https://openpix.cc/docs/as-gemini.html)。tools模式通常产生两次模型调用，video模式上传本地MP4进行理解。

```python
#!/usr/bin/env python3
"""Python 3.10+, standard library. AS Gemini tool roundtrip or native MP4 understanding."""
import argparse
import base64
import json
import os
import re
import urllib.request
from pathlib import Path


class NoRedirect(urllib.request.HTTPRedirectHandler):
    def redirect_request(self, req, fp, code, msg, headers, newurl): return None


def post(key, path, body):
    req = urllib.request.Request('https://openpix.cc' + path,
        data=json.dumps(body, ensure_ascii=False).encode('utf8'),
        headers={'Authorization': 'Bearer ' + key, 'Content-Type': 'application/json'}, method='POST')
    # Billable POST, deliberately no automatic retry.
    with urllib.request.build_opener(NoRedirect()).open(req, timeout=180) as response:
        value = json.load(response)
    if value.get('error'): raise RuntimeError(json.dumps(value['error'], ensure_ascii=False))
    return value


def run_tools(key, model):
    tools = [{'type': 'function', 'function': {
        'name': 'lookup_stock', 'description': '查询指定商品和仓库的库存',
        'parameters': {'type': 'object', 'properties': {'sku': {'type': 'string'}, 'warehouse': {'type': 'string'}},
                       'required': ['sku', 'warehouse']}}}]
    messages = [{'role': 'user', 'content': '请查询 TEA-01 在上海仓的库存，告诉我能否发货。'}]
    first = post(key, '/v1/chat/completions', {'model': model, 'messages': messages, 'tools': tools, 'tool_choice': 'auto', 'stream': False})
    if not first.get('choices'): raise RuntimeError('No assistant choice')
    assistant = first['choices'][0]['message']
    calls = assistant.get('tool_calls') or []
    if not calls: raise RuntimeError('No tool call; inspect answer instead of assuming tool success')
    messages.append(assistant)  # Keep COMPLETE assistant message and tool metadata.
    for call in calls:
        function = call['function']
        if function['name'] != 'lookup_stock' or not call.get('id'): raise RuntimeError('Unexpected tool')
        args = json.loads(function['arguments'])
        if not isinstance(args, dict) or not all(isinstance(args.get(k), str) for k in ('sku', 'warehouse')):
            raise RuntimeError('Invalid tool arguments')
        # Fixture only: replace this explicitly allowed function with your application lookup.
        result = {'sku': args['sku'], 'warehouse': args['warehouse'],
                  'stock': 17 if args['sku'] == 'TEA-01' and args['warehouse'] in ('上海', '上海仓') else 0,
                  'source': 'demo_fixture'}
        messages.append({'role': 'tool', 'tool_call_id': call['id'], 'content': json.dumps(result, ensure_ascii=False)})
    second = post(key, '/v1/chat/completions', {'model': model, 'messages': messages, 'tools': tools, 'tool_choice': 'auto', 'stream': False})
    print(json.dumps({'first_usage': first.get('usage'), 'second_response': second}, ensure_ascii=False, indent=2))


def video_body(file, prompt):
    path = Path(file)
    # Conservative sample limit, not the service's maximum.
    if path.suffix.lower() != '.mp4' or not 0 < path.stat().st_size <= 16 * 1024 * 1024:
        raise ValueError('Example accepts MP4 up to 16 MiB; start with a short clip')
    raw = path.read_bytes()
    if len(raw) < 12 or raw[4:8] != b'ftyp': raise ValueError('Expected MP4 ftyp signature')
    return {'contents': [{'role': 'user', 'parts': [
        {'inlineData': {'mimeType': 'video/mp4', 'data': base64.b64encode(raw).decode('ascii')}}, {'text': prompt}]}]}


def main():
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument('mode', choices=['tools', 'video'])
    parser.add_argument('--model', default='gemini-3.8-flash')
    parser.add_argument('--file')
    parser.add_argument('--prompt', default='按时间顺序描述视频中的主体、动作和文字，不猜测不可见内容。')
    args = parser.parse_args();key = os.environ.get('MXB_API_KEY')
    if not key: parser.error('Set MXB_API_KEY')
    if not re.fullmatch(r'[A-Za-z0-9._-]+', args.model): parser.error('Invalid model ID')
    if args.mode == 'tools': run_tools(key, args.model)
    else:
        if not args.file: parser.error('--file is required for video')
        response = post(key, '/v1beta/models/' + args.model + ':generateContent', video_body(args.file, args.prompt))
        texts = [p['text'] for c in response.get('candidates', []) for p in c.get('content', {}).get('parts', []) if isinstance(p.get('text'), str)]
        if not texts: raise RuntimeError('No text; inspect finishReason/promptFeedback before another POST')
        print('\n'.join(texts));print(json.dumps({'usageMetadata': response.get('usageMetadata')}, ensure_ascii=False, indent=2))


if __name__ == '__main__': main()
```

---

## 语言模型接口

语言模型按所选完整型号和分组调用。最小接入使用 Chat Completions；需要 Responses 时，先确认目标型号支持。两种协议的请求与输出结构不同，不能共用同一个 JSON 解析模板。

### 调用前检查

GET /v1/models 确认 Key 可用的完整模型 ID。模型名由本站目录定义，不自行删改后缀。图片和视频型号即使在目录中出现，也不能据此当作普通对话模型使用。

本页最小示例使用当前目录中的 gpt-5.5；Key 必须有对应语言模型分组权限。高级能力、上下文上限、推理参数、工具调用和多模态支持按具体型号，不在所有模型间统一承诺。

### Chat Completions

POST /v1/chat/completions

```bash
curl --fail-with-body --max-time 180 \
  https://openpix.cc/v1/chat/completions \
  -H "Authorization: Bearer $MXB_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{
    "model":"gpt-5.5",
    "messages":[{"role":"user","content":"用三句话介绍产品摄影的布光方法。"}],
    "stream":false
  }'
```

| 字段 | 类型 | 说明 |
|---|---|---|
| model | string，必填 | 当前 Key 可用的完整语言模型 ID |
| messages | array，必填 | 有序消息数组，至少含本次请求内容 |
| messages[].role | string | 最小示例 user；system、assistant、tool 等角色按具体模型和协议 |
| messages[].content | string 或协议允许的内容数组 | 纯文本先使用字符串；图片/音频等内容块需要单独确认支持 |
| stream | boolean | false 为完整 JSON；true 为 SSE |
| temperature / top_p | number，可选 | 型号相关，初次接入省略，避免模型不支持而报错 |
| max_tokens / max_completion_tokens | integer，可选 | 适用字段按型号，不能同时盲目传入两个版本 |
| tools / tool_choice | object/array 等，可选 | 目标模型明确支持后按协议使用，不视为平台统一能力 |

非流式响应结构示意：

```json
{
  "id": "chatcmpl_example",
  "object": "chat.completion",
  "model": "gpt-5.5",
  "choices": [
    {
      "index": 0,
      "message": {"role": "assistant", "content": "示例回答"},
      "finish_reason": "stop"
    }
  ],
  "usage": {"prompt_tokens": 20, "completion_tokens": 40, "total_tokens": 60}
}
```

ID、内容和用量均为示意。不要默认 choices 永远非空；先处理 error。存在工具调用时，message.content 可能为空，应该按 tool_calls 分支处理，而不是自动重试。

finish_reason=length 通常表示输出达到限制；stop 表示本次正常停止；工具或其他终止原因按实际响应处理。输出截断不意味着原请求没计费。

### 多轮对话

Chat Completions 的普通请求由调用方传入需要的历史消息。仅重复提交相同用户标识，不会自动带上本地聊天记录。保存必要的 user / assistant 消息，并控制上下文长度。

传入的历史内容也会占用输入用量。长对话应按业务需求裁剪或总结；不能无限追加然后假定输入费用固定。不要向模型发送与当前业务无关的凭据或私有日志。

### Responses

POST /v1/responses

```bash
curl --fail-with-body --max-time 180 \
  https://openpix.cc/v1/responses \
  -H "Authorization: Bearer $MXB_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"model":"gpt-5.5","input":"给一张茶壶产品图写一句标题。","stream":false}'
```

| 字段 | 类型 | 说明 |
|---|---|---|
| model | string，必填 | 具有 Responses 支持与权限的目标型号 |
| input | string 或协议输入数组 | 最小文本请求直接使用字符串 |
| instructions | string，可选 | 模型支持时使用的指令，不是 messages 数组 |
| stream | boolean，可选 | true 使用 Responses 事件流 |
| max_output_tokens | integer，可选 | 支持时限制输出，不使用 Chat 的 max_tokens 替代 |
| previous_response_id / tools 等 | 可选 | 不构成本站所有模型统一可用的会话与工具承诺，确认支持后再用 |

原始 HTTP JSON 应遍历 output 数组中的 message 项，再读取 content 中 type=output_text 的 text。不要假定顶层必然有 SDK 提供的 output_text 便捷属性。

```javascript
const texts = [];
for (const item of response.output || []) {
  if (item.type !== 'message') continue;
  for (const part of item.content || []) {
    if (part.type === 'output_text' && typeof part.text === 'string') texts.push(part.text);
  }
}
const answer = texts.join('\n');
```

以上是响应解析片段，response 需要替换成你已接收的真实 JSON。还要处理 status、error 和 incomplete_details 等实际返回字段；不能把空文本一律视为成功。

### SSE 流式文本

模型支持时设置 stream:true，使用 curl 的 --no-buffer 观察事件：

```bash
curl --no-buffer --fail-with-body --max-time 180 \
  https://openpix.cc/v1/chat/completions \
  -H "Authorization: Bearer $MXB_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"model":"gpt-5.5","messages":[{"role":"user","content":"写一句产品介绍"}],"stream":true}'
```

客户端必须按 SSE 的事件边界解析，不能把一个网络 chunk 当成一段完整 JSON。一个事件可能跨多个 chunk，同一个 chunk 也可能包含多个事件；使用连续的 UTF-8 解码器处理跨块字符。

- Chat Completions 常见 data: JSON，从 choices[].delta.content 累积文本，以 [DONE] 或相应结束逻辑收尾。工具增量需要单独拼接。
- Responses 使用事件 type，例如文本增量和完成事件；不要拿 Chat 的 choices 解析器硬套。
- 连接建立后仍可能出现错误事件。HTTP 200 不保证整个流已完整结束。
- 中途断线时保留已收内容并标记不完整，不自动重发完整生成请求。
- 流中的 usage 可能缺省或只在结束时出现，不要按已显示字符数推算最终费用。

### 参数错误与重试

400 一般先检查字段适配、上下文长度和请求结构；401/403 检查 Key 和权限；429 根据响应提示控制并发；5xx 或连接中断时先判断是否已有输出或请求记录。

语言生成同样可能产生“提交已受理但客户端没拿到完整响应”的情况。不要让 SDK、代理或队列无条件自动重试收费 POST。记录本站 request_id（若有）、型号、时间和错误摘要，避免记录完整 Key。

### 费用与边界

输入、输出、缓存以及长上下文阶梯可能分别计价。某些型号有 service_tier 等影响价格的参数，只有明确需要且当前支持时再设置。完整计费说明见[分组与计费](https://openpix.cc/docs/pricing.html)。

本章提供当前本站入口和协议说明，不代表本次对全部语言型号的高级能力重新执行了付费测试。音频、Embedding、Realtime 等未在本手册列出的业务，不应仅根据某个通用 SDK 存在相应方法就认定本站已提供。

### 新上型号、工具与计费专章

[语言型号与阶梯费用](https://openpix.cc/docs/language-pricing.html)补全GPT Pro六个型号、Grok Heavy三型号及上下文阈值、缓存、Astra优先档。已验证的AS Gemini工具调用与原生视频理解见[AS Gemini](https://openpix.cc/docs/as-gemini.html)，提供可下载的完整Python示例。

---

## AS Gemini 工具与视频理解

使用 **as gemini分组** 的 Key。本页重点说明已验证的五个语言型号、Function Calling 往返、原生 MP4 理解，以及当前 1.8 倍率的 Token 计费。

### 型号与已验证能力

| 完整 model | Chat 工具往返 | Gemini 原生 MP4 理解 |
|---|---|---|
| `gemini-3-flash-preview` | 已验证 | 已验证短视频 |
| `gemini-3.1-pro-preview` | 已验证 | 已验证短视频 |
| `gemini-3.5-flash` | 已验证 | 已验证短视频 |
| `gemini-3.8-flash` | 已验证，另测过流式工具 | 已验证短视频与顺序反转对照 |
| `gemini-4-argon` | 已验证 | 已验证短视频 |

上述为 2026-10-30 的本站实际调用、2026-11-01 的目录核对；测试包括工具结果回传后正确引用，以及视频颜色、文字、动作、先后顺序。不是只检查 HTTP 200。短片验证不等于已经验证长电影、音轨识别或最大文件上限。

同组目录另有 `gemini-3-pro-preview` 和 `gemini-3.1-flash-image`，**不在本页五型号能力与 Token 费率承诺内**。后者与香蕉组带 `-preview` 的图片型号不同；缺少已确认报价不能当作免费，接入前单独确认。

### 普通对话

```bash
curl --fail-with-body --max-time 180 \
  https://openpix.cc/v1/chat/completions \
  -H "Authorization: Bearer $MXB_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"model":"gemini-3.8-flash","messages":[{"role":"user","content":"用三句话说明如何拍好产品图。"}],"stream":false}'
```

响应读取 choices[].message；原生文本也可用 `/v1beta/models/{model}:generateContent` 和 contents/parts。两个入口都使用本站 Key。

### 工具调用：两轮都要传什么

第一轮把函数名称、说明、JSON 参数结构放在 tools。`tool_choice:"auto"` 让模型决定是否调用。

```json
{
  "model": "gemini-3.8-flash",
  "messages": [{"role":"user","content":"请查询 TEA-01 在上海仓的库存，并告诉我能否发货。"}],
  "tools": [{
    "type": "function",
    "function": {
      "name": "lookup_stock",
      "description": "查询指定商品在指定仓库的库存",
      "parameters": {
        "type": "object",
        "properties": {"sku":{"type":"string"},"warehouse":{"type":"string"}},
        "required": ["sku","warehouse"]
      }
    }
  }],
  "tool_choice": "auto",
  "stream": false
}
```

读取 `choices[0].message.tool_calls`，JSON 解析 `function.arguments`，在你自己的程序执行获准函数。然后**保留第一轮完整 assistant 消息**，追加 `role:"tool"`，`tool_call_id` 必须等于对应调用的真实 id：

```json
{
  "role": "tool",
  "tool_call_id": "替换为第一轮真实调用id",
  "content": "{\"sku\":\"TEA-01\",\"warehouse\":\"上海\",\"stock\":17}"
}
```

再携完整 messages、同一 model 和必要 tools 发第二次 Chat 请求，模型才能据此回答。工具结果中的 content 是 JSON 字符串，不是把对象直接当 content。一次产生多个 tool_calls 时，应逐一回传对应 id，不丢弃助手消息或只回传最后一个。

可下载并运行 [mxb-gemini.py](https://openpix.cc/docs/examples/mxb-gemini.py) 的 tools 模式，演示固定的虚构库存查询；它不会访问真实库存或执行任意模型生成代码：

```bash
python mxb-gemini.py tools --model gemini-3.8-flash
```

本例每次工具往返通常有两次收费模型请求。这里的 Function Calling 是调用方执行函数，不代表模型自动拥有浏览器、联网搜索或代码执行器。流式工具参数可能分片返回，需要按调用 index/id 拼接 arguments，得到完整 JSON 后再执行。

### 视频理解：使用原生 inlineData

当前推荐 **Gemini 原生路径**。`video_url` 形式的 OpenAI 兼容请求曾返回200但答错画面，不能把200作为视频已被模型读取的证据。

```json
{
  "contents": [{
    "role": "user",
    "parts": [
      {"inlineData":{"mimeType":"video/mp4","data":"替换为整个MP4文件的纯Base64"}},
      {"text":"按时间顺序描述视频中的主体、动作和出现的文字，不要猜测不可见内容。"}
    ]
  }]
}
```

POST 到 `https://openpix.cc/v1beta/models/gemini-3.8-flash:generateContent`。这是发送视频内容让语言模型理解，不是创建视频任务；不使用 `/v1/videos`，没有生成视频用的 seconds/size 参数。

避免手工拼 Base64，可运行完整示例：

```bash
python mxb-gemini.py video --model gemini-3.8-flash --file clip.mp4 \
  --prompt "逐段描述视频内容，包括文字和动作顺序。"
```

示例读取本地 MP4 并编码，不预先抽帧；原生返回从 candidates[].content.parts[].text 读取。`inlineData.data` 不能放文件路径、HTTP URL 或 Data URL 前缀。先用短小视频验证你的实际场景，文件经 Base64 会增大约三分之一；本页不声明通用最大时长/文件量。

### 当前费率与上下文档位

以下是 **已经乘 1.8** 的普通本组价格快照，单位 ¤ / 百万 Token；不再乘一次 1.8。账户另有专属价格时以自己的账单为准。

| 型号 / 完整输入长度 | 文本输入 | 输出（含计费思考） | 缓存读取 | 缓存写入 |
|---|---:|---:|---:|---:|
| 3 Flash Preview | 0.90 | 5.40 | 0.09 | 1.80 |
| 3.1 Pro Preview，≤200000 | 3.60 | 21.60 | 0.36 | 0 |
| 3.1 Pro Preview，>200000 | 7.20 | 32.40 | 0.72 | 0 |
| 3.5 Flash | 2.70 | 16.20 | 0.27 | 0 |
| 3.8 Flash | 1.35 | 6.75 | 0.135 | 0 |
| 4 Argon | 3.60 | 18.00 | 0.18 | 3.60 |

Pro 的阈值按完整输入上下文（包含缓存部分）判断，超过后**整次请求**使用长上下文费率，不是只对超出的 Token 加价。缓存命中不能把长请求降回短档。工具定义、历史消息、工具结果和视频内容都会影响输入；不要按 prompt 字符数估算 Token。

例如 3.8 Flash 无缓存、输入1000 Token、计费输出200 Token，费用为 `(1000×1.35 + 200×6.75)/1000000 = ¤0.0027`。有缓存时各计价项需按账单分类，不把缓存 Token 与普通输入重复累加。

### 错误与能力边界

| 情况 | 核查 |
|---|---|
| tool_calls 存在但 content 为空 | 进入工具分支，不当成空回答重发 |
| 第二轮不能引用结果 | 检查完整 assistant 消息、tool_call_id、role=tool 以及 content 类型 |
| 视频只返回泛泛回答 | 检查是否走原生 inlineData，并用已知片段核实实际理解 |
| 用量大于可见输出文字 | 检查思考用量、历史与工具上下文，不能仅凭字数算费 |
| 客户端超时 | 记录 request_id 和时间，先查调用记录，不盲目重发收费 POST |

其他语言模型的普通 Chat / Responses 结构见[语言接口](https://openpix.cc/docs/text.html)；不要把本页实测结果外推到另一分组的同名模型。

---

## 语言型号与阶梯费用

本页说明 GPT Pro、Grok Heavy、谷歌语言与 AS Gemini 的选型和费用。公开 ID 是本站请求名称；名称相近不构成其他平台同名产品能力或参数完全相同的保证。

### 分组与型号

| 分组 | 当前型号 | 入口 |
|---|---|---|
| gptpro语言模型分组 | gpt-5.5、gpt-5.6、gpt-5.6-sol、gpt-6-sol、gpt-6.1-sol、gpt-6-astra | Chat Completions；Responses 按具体型号支持确认 |
| grokheavy速刷语言 | grok-4.5、grok-4.6、grok-4.7 | Chat Completions |
| 谷歌语言模型 | gemini-3.5-flash、gemini-3.6-flash、gemini-3.7-flash、gemini-3.8-flash | Chat Completions / Gemini 原生 |
| as gemini分组 | 五个已验证语言型号及另两个需单独确认的目录项 | 详见 [AS Gemini](https://openpix.cc/docs/as-gemini.html) |

不要将 `gpt-5.6` 自动改名为 `gpt-5.6-sol`；两者是独立公开条目。也不要把 Grok 图片、视频型号放到普通语言请求中。

### 调用与返回

完整普通/流式请求、Responses 解析、工具消息与错误处理见[语言模型接口](https://openpix.cc/docs/text.html)。例如：

```json
{
  "model": "gpt-6.1-sol",
  "messages": [{"role":"user","content":"为陶瓷茶杯设计三个简短的中文广告标题。"}],
  "stream": false
}
```

POST `/v1/chat/completions`，使用本站 Bearer Key。将 model 换成有权限的其他语言型号即可进行基础对话，但推理参数、采样参数、工具、多模态与 Responses 不能直接当成全型号共通能力。优先省略不必要的扩展字段。

### GPT Pro：当前普通分组费率

2026-11-01 公开配置快照，单位 ¤ / 百万 Token，**下表已乘分组倍率 0.2**。缓存写 5m/1h 仅在实际计费响应含相应类别时参与，不意味着所有请求自动创建缓存。

| model / 完整输入长度 | 输入 | 输出 | 缓存读 | 缓存写 5m / 1h |
|---|---:|---:|---:|---|
| gpt-5.5 | 1 | 6 | 0.011 | 以当前报价及账单为准 |
| gpt-5.6、gpt-5.6-sol，≤272000 | 1 | 6 | 0.1 | 1.25 / 未单列 |
| 同上，>272000 | 2 | 9 | 0.2 | 2.5 / 未单列 |
| gpt-6-sol，≤250000 | 0.4 | 2 | 0.05 | 未单列 |
| gpt-6-sol，>250000 | 0.8 | 3 | 0.09 | 未单列 |
| gpt-6.1-sol，≤250000 | 0.4 | 2 | 0.022 | 0.5 / 0.5 |
| gpt-6.1-sol，>250000 | 0.8 | 3 | 0.044 | 1 / 1 |
| gpt-6-astra，≤272000 | 2 | 10 | 0.2 | 2.5 / 未单列 |
| gpt-6-astra，>272000 | 4 | 15 | 0.4 | 5 / 未单列 |

Astra 显式 `service_tier:"priority"` 在对应上下文档费率上再乘2；不需要优先档时省略该字段。该加价不推广到其他型号。费用规则中的阈值也不等于模型最大上下文容量。

### Grok Heavy：200K 阈值

以下 **已乘分组倍率 0.13**，单位相同。这里的短档条件是 `<200000`，因此恰好200000属于长档，与上表的“≤”边界不同。

| model / 完整输入长度 | 输入 | 输出 | 缓存读 |
|---|---:|---:|---:|
| grok-4.5，<200000 | 0.26 | 0.78 | 0.039 |
| grok-4.5，≥200000 | 0.52 | 1.56 | 0.078 |
| grok-4.6 / grok-4.7，<200000 | 0.26 | 0.78 | 0.065 |
| grok-4.6 / grok-4.7，≥200000 | 0.52 | 1.56 | 0.13 |

### 谷歌语言模型组：与AS组分开看

以下是2026-11-01公开配置，已乘本组0.35倍率，单位¤/百万Token。它与AS Gemini的1.8倍率不是同一组价格，尤其3.5 Flash不能混套。

| model | 输入 | 输出 | 缓存读 |
|---|---:|---:|---:|
| gemini-3.5-flash | 0.175 | 1.05 | 0.0175 |
| gemini-3.6-flash | 0.2625 | 1.3125 | 0.02625 |
| gemini-3.7-flash | 0.2625 | 1.3125 | 0.02625 |
| gemini-3.8-flash | 0.2625 | 1.3125 | 0.02625 |

此组可使用语言章节的普通Chat请求，替换完整model；Gemini原生文本使用contents/parts。AS组的工具和视频实测不能直接作为这一组的同名型号验收，生产接入按自己的目标Key验证。这里未提供统一缓存写入或长上下文分档保证。

### 用量核对方法

1. 记录本次 Key 的分组、完整 model、影响计费的 service_tier 等字段与 request_id。
2. 取同一次请求的 usage；普通文本、缓存读写、输出等按日志实际分类核对。缓存 Token 不应既作为普通输入又重复作为缓存输入收一次。
3. 用包含缓存的完整输入判断上下文档。跨阈值是整次请求换档，不是分段累进。
4. 已经是分组最终价的报价不要再次乘倍率。账户专属价与公开普通价可能不同。
5. 思考用量可能计入输出。total_tokens 不是“全部 Token 都按输出单价计费”，可见文字长度也不是实际计费输出长度。

AS Gemini 的当前1.8倍费率、200K边界与工具/视频例子见[专章](https://openpix.cc/docs/as-gemini.html)。谷歌语言组与 AS Gemini 即使有同名型号，也不能相互套用分组费率和能力结论。

动态价格仍以[模型广场](https://openpix.cc/pricing)和本次最终账单为准；公开 `/api/pricing` 可供程序读取配置，不执行其中的表达式字符串。接入程序应按官方返回字段处理费用，而不是在前端猜测或复制一张永久不更新的价表。

---

## 分组与计费

**先确认 Key 的分组，再看所选型号的计费方式。** 同一模型可以出现在多个分组中，报价不同；请求体里的 group 不会替你改变 Key 的分组。

当前报价见[模型广场](https://openpix.cc/pricing)，实际消费和退款见[调用记录](https://openpix.cc/usage-logs)。

### 计价单位与分组倍率

本站以 **¤** 表示站内标准额度，不等于人民币符号 ¥。充值金额、账户到账额度和调用消耗是不同概念，以[钱包](https://openpix.cc/wallet)的实际方案为准。

价格展示要区分“基础单价”和“已乘分组倍率的单价”。从模型广场选定分组后读取的最终报价，不应再重复乘一次倍率。最低价只对具有相应权限并选择了对应分组的 Key 有意义。

### 三类视频费用怎么计算

以下为 **2026-10-29、分组倍率 1** 的公开报价快照。不是永久价格承诺。

| 型号 | 方式 | 单位报价 | 例子 |
|---|---|---|---|
| grok-imagine-video-6s | 按次 | ¤0.08 / 次 | 一个 6 秒任务 ¤0.08 |
| grok-imagine-video-1.5-6s | 按次 | ¤0.10 / 次 | 一个 6 秒任务 ¤0.10 |
| grok-video-1.5 | 按次 | ¤0.85 / 次 | 按调用计价，不按 seconds 直接乘 |
| MiniMax-H3 | 按秒 | ¤0.06 / 请求秒数 | seconds=5：¤0.30 |
| SD-2.0-MZ1 | 按秒 | ¤0.16 / 请求秒数 | seconds=5：¤0.80 |
| Seedance Mini | 视频 Token | 无视频参考 ¤12.65 / 百万；有视频参考 ¤7.70 / 百万 | 实际计费用量 × 对应单价 / 1,000,000 |
| Seedance Fast | 视频 Token | 无视频参考 ¤26.455 / 百万；有视频参考 ¤15.73 / 百万 | 同上 |

SD-2.0-MZ1 当前位于公开分组“seedance低价渠道，质量自测”。其他新视频型号主要位于“视频分组”；grok-video-1.5 也在 default。Key 必须有对应权限。

#### 视频 Token 不是提示词字数

Mini/Fast 的视频 Token 与输出视频规格、实际用量有关，不能把中文字符数当作费用依据，也不能把 5 秒视频固定当作某个 Token 数。图片或音频参考不等于“有视频参考”；只有实际视频参考才对应视频参考分支。

例如计费用量恰为 40,594 Token、Mini 无视频参考、倍率 1，则费用示例为 40,594 × 12.65 / 1,000,000 = **¤0.5135141**。这只是算术示例，不保证每个 5 秒任务使用该用量。响应 usage 和同一任务的最终账单一起核对。

#### 按秒不等于按播放文件的小数时长

H3 和 SD-2.0-MZ1 使用有效请求秒数计算当前按秒价格。生成文件实际时长可能存在编码小数差异，不应拿播放器显示的 5.03 秒重新计算请求费用。也不能把参考视频的 duration 当作输出 seconds。

### 图片与语言费用

| 类型 | 阅读方式 |
|---|---|
| 普通固定图片单价 | 按完整 model、分组、输出数量核对 |
| 图片质量档位 | 查看当前型号 quality 对应价格；high/xhigh/max 可能不同 |
| 语言 Token | 输入与输出分别计算，缓存和阶梯规则按当前型号 |
| 动态计费 | 读取公开规则及适用参数，不能只看一个 model_price |

例如 gpt-image-2.5-flare 与 gpt-image-2.5-sunburst 是不同型号；不能把一个型号的 quality 或报价套给另一个。基础、1K、2K、4K 的档位是否同价也取决于具体分组。

语言费用示意：若输入单价 ¤A / 百万 Token，输出单价 ¤B / 百万 Token，分别用了 I 和 O，则基础计算为 I×A/1,000,000 + O×B/1,000,000。有缓存、长上下文阶梯或其他计费项时，应按实际规则分项计算，不能简单把 total_tokens 全乘输出单价。

### 如何读公开价格接口

GET /api/pricing 无需 API Key，返回公开模型与报价。主要字段如下；字段可能按型号缺省。

| 字段 | 用途 |
|---|---|
| data[].model_name | 完整公开模型 ID |
| data[].enable_groups | 当前报价涉及的可用分组 |
| group_ratio | 分组倍率映射 |
| data[].quota_type | 基础计价类型；1 通常为固定按次，0 不能一概当作 Token |
| data[].model_price | 固定基础价格，动态计费时不能孤立解读 |
| data[].model_ratio / completion_ratio | Token 计价相关比例，不等于界面最终价格 |
| data[].billing_expr | 存在时需要按动态计费规则理解 |
| data[].billing_usage_schema | 计费输入字段描述，不是完整生成能力清单 |

特别是 **quota_type=0 + 动态表达式** 也可能是按秒视频。不能只凭 quota_type 把 SD-2.0-MZ1 标成 Token 计费。计费 schema 的通用分辨率枚举也不能用来推断所有型号接受 1080p/4K；真实参数以专属型号章节为准。

公开目录不等于 Key 的可调用目录。自己的权限用 GET /v1/models 查询。不要在第三方页面执行返回的表达式字符串作为 JavaScript。

### 预留、结算和退款

视频提交时可能先预留额度，完成后按实际规则结算，差额可能表现为补扣或退款。请将同一任务关联的记录合计查看；初始预留不一定是最终消费。

HTTP 202/200 只表示接口阶段的结果。失败、客户端超时、下载失败或者关闭页面，都不能单独证明没有扣费或已经退款。出现异常时保留任务 ID 和 request_id，查看原任务及调用记录，避免通过重复生成“验证退款”。

### 费用不一致时依次检查

1. Key 当时实际选择的分组及倍率。
2. 完整 model、quality、seconds、是否含视频参考等计价字段。
3. 报价单位：每次、每秒还是每百万 Token。
4. 是否重复提交，是否存在预留/补扣/退款多条记录。
5. 比较的是同一时刻报价，还是历史截图。
6. 是否把站内额度直接误写成人民币，或重复乘了分组倍率。

### MJ 与新图片分组的特别规则

- MJ固定¤0.55/次，普通用户和SVIP同价。n=1创建一次任务，成功提供4张独立图片；四宫格预览有则附送，是否附带拼图均不改变价格。提交时预留整次费用，生成失败按原有规则退款。见[MJ专章](https://openpix.cc/docs/midjourney.html)。
- Seedream Lite / Pro在专用组当前分别¤0.07 / ¤0.09每张，规格与参考图见[Seedream](https://openpix.cc/docs/seedream.html)。
- 图片Token组与按张组是不同口径，文本、图片输入、缓存及输出分类不能遗漏或重复计算。GPT全质量组还可能按quality定价，见[GPT图片](https://openpix.cc/docs/gpt-image.html)。
- Adobe中质量的high统一按medium及本组单价计费，支持明确4K。仅超出4K范围的尺寸在生成前拒绝；已开始生成后的超时、断线另按任务最终账单核对。

### 公开报价的覆盖关系

`group_token_prices` 包含分组专用的input/output/cache_read/cache_write/image_input/image_cache_read与可选long_context；这些基础费率按有效分组倍率计价。它可以覆盖基础model_price，不能因为quota_type=1就忽略分组Token价。

`group_model_prices` 是固定分组售价覆盖，`group_quality_prices` 是分组质量档售价；选中覆盖价后不要机械地再乘一次分组倍率。`billing_plugin_variants` 提供MJ等任务的动态报价，不能把其model_price=0理解为免费。

程序接入应展示型号+分组+实际计价方式。语言长上下文阈值、优先档及算例见[语言费用](https://openpix.cc/docs/language-pricing.html)与[AS Gemini](https://openpix.cc/docs/as-gemini.html)。普通公开倍率与用户专属倍率可能不同，历史账单依请求当时生效的规则核对。

---

## 错误处理与排查

先分清两层错误：提交/查询的 HTTP 错误，以及 HTTP 200 查询结果里的失败任务。两者都要检查。

### HTTP 状态速查

| 状态/现象 | 常见原因 | 正确处理 |
|---|---|---|
| 400 | 字段、尺寸、MIME 或模型参数不匹配 | 按 error.message 修正；不要原样循环重试 |
| 401 | Key 缺失、无效、过期 | 检查 Bearer 格式、令牌状态与有效期 |
| 403 或权限相关错误 | 分组/模型权限或令牌限制 | 对照 Key 的分组与 `/v1/models` |
| 余额/额度不足 | 账户余额或令牌额度不足 | 核对钱包及令牌额度；不要只按固定 HTTP 402 判断 |
| 404 查询图片任务 | ID 错误、Key 不同、任务清理 | 使用原 Key 与推荐查询路径，核对任务时间 |
| 413 | 上传体积超过入口限制 | 缩小图片/请求；Base64 会增加传输体积 |
| 429 | 速率、并发、队列或资源限制 | GET 按 Retry-After 退避；提交结果不明时不要自动重发 |
| 500/502/503/504 | 服务暂时异常 | 有 task_id 则继续核对该任务；无 ID 则先查记录 |
| HTTP 200 但 status=failed | 任务执行失败 | 读取 error.message，停止正常轮询 |
| HTTP 200/completed 但没有图片 | 空结果、格式不兼容或生成异常 | 视为业务失败，保存 ID 与响应摘要反馈 |
| 浏览器 CORS 报错 | 第三方网页直接跨域访问 | 改为从自己的后端调用，不在前端暴露 Key |

不同模型和渠道的错误封装可能不同。通用读取顺序为 `error.message`、`message`，并保留 HTTP 状态；不能只匹配某一条中文错误文本。

### 常见具体问题

**参数不支持**：按当前完整型号删除不支持的字段。Mini/Fast 不接收 1080p/4K，SD-2.0-MZ1 不支持关闭音频或参考视频，H3 不使用 generate_audio 开关。不能把另一型号的请求原样复制过来。

**模型不存在 / 无权访问 / 无可用渠道**：区分拼写、分组权限和实时渠道可用性。先查 `/v1/models`，再查模型状态页。目录列出模型不代表某时刻一定有可用容量。

**拿到 task_id 但客户端一直转圈**：检查是否只识别 succeeded 而漏掉 completed，是否只读取 result 而忽略 data，以及是否把毫秒误当成秒。未知状态和 GET 错误必须有截止时间。

**返回图片不是请求的像素尺寸**：size 是请求目标，有些渠道会归一、取整或使用固定档位。应读取原图像素，不能仅用响应回显的 size 判断规格兑现。

**账单看起来重复**：检查客户端/SDK/反向代理是否自动重试 POST，以及超时后是否手动再次提交。同一个 Idempotency-Key 当前也不能保证去重。

### 反馈时提供什么

```text
接口路径：
请求时间与时区：
模型完整 ID：
分组名称：
HTTP 状态：
task_id：
request_id（若有）：
error.message：
是否曾重复提交：
脱敏后的必要参数（不包含完整图片 Base64）：
```

排查时提供以上脱敏信息。不要发送完整 Key、Cookie、未脱敏 Authorization 或包含凭证的日志。

### 视频特有错误

素材上传的 415/422 检查 MIME、编码、时长与像素；视频 /content 的 400 先查任务是否完成，410 检查资源是否失效。下载失败只重试原任务下载，不能重新生成。完整对照见[视频任务与下载](https://openpix.cc/docs/video-tasks.html)和[素材上传](https://openpix.cc/docs/video-reference.html)。

### 新图片与工具请求的常见问题

| 错误/现象 | 检查与处理 |
|---|---|
| prompt is required / non-empty string | 顶层prompt类型、Content-Type与body一致性、多层转发是否丢字段；见[参考图](https://openpix.cc/docs/edits.html) |
| MJ size不支持 | 使用比例枚举，不传像素或4K；quality用数值 |
| Adobe中质量尺寸失败 | 本组已支持明确4K；检查是否超出长边4096/4K范围，参考GPT图片章节 |
| Seedream Pro请求4k | 选Lite 4k，Pro只使用1k/2k |
| 返回5张但只看见1张 | 遍历全部data[]，完整客户端下载全部图片 |
| AS Gemini视频HTTP200但理解错误 | 使用已验证的原生MP4 inlineData，不用状态码替代内容检查 |
| 工具调用后不回答 | 回传role=tool、正确tool_call_id并保留完整assistant消息 |
| 页面关闭后仍有消费 | 合法生成可能继续完成并结算，查原任务和图库，不重复生成 |

---

## 限制与接入建议

本站提供统一入口，但不同模型并不因此具备相同能力。新增接入应先选择型号，再按相应章节构造请求。

### 兼容边界

| 能力 | 边界与做法 |
|---|---|
| 视频参数 | Grok、Mini/Fast、SD、H3 的时长、分辨率、素材格式分别校验，不能共用一个固定参数模板 |
| 视频参考图 | 普通参考图、首帧和首尾帧语义不同，遵守型号的互斥规则 |
| 视频计费 | 按次、按秒、视频 Token 分别计算 |
| 图片异步 stream=true | 不提供可靠的图片 SSE 终态解析，使用非流式任务结果 |
| 异步 b64_json | 结果通常转为托管 URL，客户端兼容少数 Base64 结果 |
| 多图 n>1 | 部分失败与计费更复杂，示例固定 n=1，分别跟踪任务 |
| 幂等提交 | 没有全型号统一可依赖的 Idempotency-Key 保证，在业务端去重 |
| completed | 仍需校验结果非空、下载成功、媒体可解码 |
| 任务取消 / 回调 | 没有本手册承诺的统一取消 API 或统一 webhook，使用有界轮询 |
| 任务保留 | 结果有保留期，及时转存；图片任务不保证跨重启恢复 |
| 浏览器接入 | Key 放自己的后端，不在网页源码或浏览器存储中分发 |
| 历史型号 | 当前目录未列出的旧型号不作为新接入默认值 |

### 超时、重试和业务去重

区分提交连接超时、一次 GET 超时、客户端总等待期限。图片示例提交 60 秒，视频示例提交 120 秒，查询 30 秒，总等待 30 分钟；这些是客户端配置，不是服务 SLA。

提交前先持久化自己的业务唯一 ID 和“正在提交”状态。收到 task_id 立即记录。POST 超时且没有 ID 时标记“提交结果未知”，先核查记录；不要让 SDK、反向代理、作业队列自动重放生成请求。

只对 GET 的网络故障、429 和临时 5xx 做有界退避。使用原 ID 恢复等待，绝不因为下载失败重新生成。批量任务从小并发开始，加入轮询抖动，按 429 调整。

### 凭据和结果下载

- 查询任务只使用固定本站 HTTPS origin 与合法 task_id，不能信任任意 poll_url 然后向其转发 Key。
- 视频 /content 入口需要 Key；图片托管直链与外部媒体 URL 不携带本站 Key。
- 禁止携带 Key 自动跨站重定向。原始响应中的临时签名 URL 也不写进公共日志。
- 下载到临时文件，校验 HTTP、文件头、大小与真实解码，再原子替换正式结果。
- 生产系统记录公开型号、参数摘要、任务 ID、实际媒体规格、哈希与费用，避免仅保存页面截图。

### SDK 和工作流适配

普通 OpenAI Images 客户端通常期待同步 data[]。本站 /async 的任务对象需要你实现“创建、保存 ID、轮询、终态、下载”，只修改 Base URL 不会自动完成这些步骤。

视频使用本手册明确给出的 JSON 与原始 HTTP 路径；第三方 SDK 的专有对象不一定与所选型号参数一致。禁用会自动重发生成 POST 的重试配置。若工具不支持任务对象，可自行增加服务端任务队列和轮询组件。

### 上线前检查

用当前 Key 确认目录和分组；校验参数；对业务提交去重；模拟失败、结果未知、GET 暂时失败和下载中断；确认日志脱敏；配置临时资源转存与账单核对。

先在自己的业务中选少量目标规格验证实际生成，再逐步放量。本手册的契约校验不能替代对全部提示词、画面、规格的生成质量验收。

### 当前图片接入差异

MJ只接受本页约定JSON，size用比例、quality用数值，n=1返回多张，原生MJ后续按钮未开放。Seedream用resolution档位加size比例，Lite与Pro上限不同。GPT/Gemini常用像素size；Adobe中质量统一medium，支持明确4K并按可用规格分流，不自动降成2K。参见[MJ](https://openpix.cc/docs/midjourney.html)、[Seedream](https://openpix.cc/docs/seedream.html)、[GPT图片](https://openpix.cc/docs/gpt-image.html)、[香蕉](https://openpix.cc/docs/banana.html)。

不能仅凭模型广场显示某种通用endpoint标签，断定所有SDK参数或原生操作都能调用。优先使用对应专章的已确认入口。

---

