mjMJ Image API开发者接入文档

MJ Image API

使用 API Key 异步提交图片任务,再查询结果。图片结果返回 URL,不返回 Base64。提交成功只表示已受理,不表示图片已经可以下载。

文档核对日期:2026-11-14。请求契约以当前服务为准;上游模型、套餐和资源可用性仍可能变化。以下参数和功能“实测”主要指 2026-11-13 的生产接口验收,不是性能承诺或所有参数组合的穷举。

图片接入流程:POST /api/submit → 保存 job_id 与 job.media_url → GET job.media_url 轮询 → 从 images 取得 URL 并下载。接口请求携带 API Key,下载返回的 URL 时不携带 API Key。上传参考图的 file_b64 是输入字段,与返回结果的 URL 形式不同。
当前能力边界:
  • 历史归属:已记录归属的任务按 Key 限制;历史未归属任务目前可能被其他有效 Key 读取。尚不能承诺严格的历史作品隔离,这是当前实现缺陷。
  • 四周扩图:本轮 v8.2 + zoomFactor=2 虽返回任务 ID,观察约 17 分 35 秒仍未取得结果。改画幅 zoomFactor=100 已完成,二者不能混为一谈。
  • 区域重绘:inpaint 本轮已产出四张结果;遮罩区域保持、边界和保真效果尚未完整验收。

上游公开图片可能通过浏览器直接访问,但这不替代本服务下载接口的验收,也不能把服务 API Key 发送给第三方图片域名。此页只描述对外接入;未列入清单的路径、额外字段或任意动作类型不构成稳定公开承诺。

快速开始

1. 准备 API Key 和运行环境

联系服务提供方取得以 mj_live_ 开头的 API Key。保存在调用程序的环境变量中,不写入网页前端、URL 或公开仓库。本页示例 Key 全是占位值,执行前必须替换。

# Bash:后续 curl 示例使用 Bash 的换行和引号语法
export MJ_API_KEY='mj_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'

# 安装 Python 示例所需依赖
python -m pip install requests
# PowerShell:设置环境变量;不要直接复制 Bash 的反斜杠换行
$env:MJ_API_KEY = 'mj_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
# 使用 curl 时可显式调用 curl.exe,避免旧版 PowerShell 的同名命令别名

2. 检查连接和读取权限

curl --fail-with-body {BASE}/health \
  -H "Authorization: Bearer $MJ_API_KEY"
{"service":"mj-lite-service","version":"0.2","ok":true,"accounts":1,"docs":"/docs"}

ok=true 说明这次服务请求和认证成功,不保证生成容量、上游登录状态、套餐能力或图片下载都正常。仅有 submit 权限的 Key 不能通过需要 read 的健康检查。

3. 提交任务

curl --fail-with-body -X POST {BASE}/api/submit \
  -H "Authorization: Bearer $MJ_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"mode":"relaxed","action":{"t":"imagine","prompt":"a ceramic teapot on a wooden table --ar 3:2 --v 8.2"}}'

收到 job_id 和 job.media_url 后,任务进入异步处理;提交接口不会等待图片完成。此请求会创建真实任务并使用上游资源,不要把网络超时后的重发当作安全重试。没有固定完成时间,设置合理的等待窗。

4. 查询图片结果

# 用真实响应中的 job.media_url 替换下方路径;建议每 10–20 秒查询一次
curl --fail-with-body {BASE}/api/media/ee4c507f-521a-4ce0-888c-16aa71df6f63 \
  -H "Authorization: Bearer $MJ_API_KEY"

当 archive_status=ready,整组图片的 URL 均已可用;处理中也可先下载 images 中 status=ready 的图片。详见结果字段与状态。不必先等 /api/gallery 列表出现,再来查询结果。

5. 使用返回的 URL 下载

# 替换为本次响应中的 images[0].download_url;这次请求不要附加 API Key
curl --fail-with-body 'https://images.example.com/result-0.png?token=replace-with-current-response' -o result-0.png

download_url 返回图片文件;thumb_url 和 preview_url 用于预览。URL 有有效期,到期后重新查询,使用新的完整 URL。

公开能力清单

“已产出”表示记录和结果图片均观察到;“可传”仅表示接口能表达该请求;“未测”不等于不支持。

方法路径用途 / 权限本轮状态
POST/api/submit生成与二次操作 / submit多种动作已产出;扩图组合仍异常;视频本轮未测
POST/api/uploadBase64 图片上传 / submitPNG 上传成功,返回 short_url
POST/api/maskPNG 遮罩上传 / submit成功返回 mask_url,随后 inpaint 已产出
GET/api/media/{jobId}图片结果状态、元数据与下载 URL / read与生成记录分开;只有 ready 的结果附下载地址
POST/api/media/{jobId}/archive历史结果准备或失败重试 / submit只允许自己的任务;不重新生成
GET/api/jobs最近任务原始记录 / read查询通过;没有单任务终态查询语义
GET/api/gallery最近作品卡片 / read查询通过;历史隔离有前述例外
GET/api/thumb/{jobId}/{index}原始图片 / 缩略图 / 视频封面 / read图片就绪返回 302 下载 URL;未就绪返回 202,视频范围另述
GET/api/image/{jobId}/{index}图片下载 / read结果 ready 后 302 到原始 PNG 的 URL;推荐直接使用 media 接口返回的 download_url
GET/api/credits共享 GPU 额度快照 / read查询通过,不是 Key 剩余请求次数
GET/api/queue共享队列计数 / read查询通过,不是某个任务的状态
GET/health连通性与认证 / read通过,不代表全链路健康

认证与权限

Authorization: Bearer mj_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

认证方案名不区分大小写,示例统一使用 Bearer;它与 Key 之间保留一个空格。Key 本身区分大小写。请求头名称遵循 HTTP 的不区分大小写规则。

权限允许的公开操作注意
read健康、列表、额度、队列、取图不包含生成任务
submit提交任务、图片上传、遮罩上传不自动附带 read;若需查询结果,申请同时具备两项权限的 Key

已记录归属的任务应只允许对应 Key 读取;历史未归属任务目前存在放行例外。不要把本页当作历史数据已严格隔离的证明。提交响应当前也可能带额外账号身份字段:不应依赖或继续传播这些字段,响应脱敏尚未统一。

配额、并发和数据大小

限制当前行为
Key 每日次数按已认证的 HTTP 请求计数,不是只按生成次数。列表轮询、健康检查、取图和部分被拒绝的已认证请求也会计入。限额由服务方配置,0 表示不限。
每日重置UTC 00:00 切换日期;北京时间为 08:00。不是每次首次调用后滚动 24 小时。
图片速率图片接口还有独立的短时间速率限制,可能返回 429 与 JSON retry_after 秒数。它与每日次数不是同一种限制,实际值由服务配置决定。
并发由所用上游账号、套餐和当前队列决定,不承诺固定并发数。收到任务 ID 后仍可能排队。不要用并发轮询代替并发生成。
GPU 额度fast 会使用快速资源;relaxed 受套餐、队列和上游规则约束。Key 请求次数、GPU 时间和队列长度是三种不同的量。
图片上传大小建议原始图片不超过 18 MiB。当前应用解码后上限为 20 MiB,但请求体上限为 25 MiB;Base64 膨胀后无法保证 20 MiB 文件能通过入口。
大小换算1 MiB = 1,048,576 字节。Base64 长度为 4 × ceil(原始字节数 / 3),另加 JSON、文件名和 data URL 前缀。原始文件接近 18.75 MiB 时就可能超出当前请求体上限。
上传失败413 可能来自请求入口,也可能来自应用,响应可能不是 JSON。不要只检查解码后的文件大小。

轮询建议从 10~20 秒间隔开始,并按 Key 的请求预算调整。例如每 5 秒轮询 10 分钟就约需 120 次请求,尚未计算提交和下载;每日上限较低时会先触发 429。不要为了等待结果反复提交同一任务。

任务生命周期、超时与重试

  1. 提交未确认:请求超时或连接断开,不确定上游是否已经创建任务。不要立即重新 POST。
  2. 已受理:HTTP 200 且有 job.job_id,只证明本次受理响应成功,不证明生成完成。
  3. 列表可见:在 /api/jobs 或 /api/gallery 找到对应 ID,证明已有结果记录;仍需检查结果数量和图片可读性。
  4. 图片已就绪:GET job.media_url 的 archive_status=ready 表示整组结果 URL 可用;处理中 images 中 status=ready 的图片可先下载。
  5. 下载已验证:请求 download_url 成功,Content-Type 为图片且内容可解码,文件长度与 sha256 校验通过,才说明这次文件获取成功。
  6. 观察窗内未确认:列表和队列都找不到任务时,状态仍可能未知。队列为 0 不等于成功,也不能据此编造 failed 或 completed。

上面的名称是接入方观察阶段,不是服务已实现的一组状态枚举。当前没有可靠的按任务 ID 查询终态、进度百分比、开始时间、完成时间或失败原因的独立公开接口;也没有 webhook / 回调。

幂等与取消:当前没有公开的幂等键或任务取消接口。相同 POST 重发可能创建新任务。本页没有承诺请求超时自动回滚或取消上游工作。若需要去重,由调用方保存请求、受理响应和任务 ID;提交结果未知时联系服务方核对,而不是盲重试。

建议对 GET 使用有限的等待窗;明确的认证和权限错误立即处理,429 按实际原因等待,短暂网络失败可在总截止时间内重试。不要把所有 403/404 都当成“稍后重试”。任务的外部观察耗时包含排队、生成、记录传播、图片就绪和轮询,不能当作纯 GPU 时间。

POST /api/submit:公共提交契约

POST/api/submit
需要 submit 权限。统一使用外层 mode 和 action;下面每个标为 JSON 的请求示例都是完整请求体。
字段类型 / 是否必填取值、默认与含义
modestring / 否默认 relaxed。公开约定值只有 fast、relaxed;不要传 null、空字符串或其他大小写。它是公共速度选项,不是文生图专属字段。
actionobject / 是包含下方某个动作的完整字段,至少有字符串 t。不要把此页未列出的任意 t 当成已支持功能。
{"mode":"relaxed","action":{"t":"imagine","prompt":"a ceramic teapot on a wooden table --ar 3:2 --v 8.2"}}
{"ok":true,"job":{"job_id":"ee4c507f-521a-4ce0-888c-16aa71df6f63","media_url":"/api/media/ee4c507f-521a-4ce0-888c-16aa71df6f63","job_type":"v8-2_diffusion","event_type":"diffusion","is_queued":false,"softban":false,"flags":{"mode":"relaxed","visibility":"public"},"meta":{"width":1344,"height":896,"batch_size":4,"parent_id":null,"parent_grid":null}},"attempts":[]}
响应字段说明
ok这次提交返回成功;不是完成状态。
job.job_id用于关联、轮询和后续动作的任务 ID。示例为 UUID;不要将它当 API Key。
job.media_url本任务结果查询路径,例如 /api/media/{jobId}。请求时携带自己的 API Key。结果响应直接包含 archive_status、images,不需要先查作品列表。
job.job_type / event_type上游实际选择的模型或动作信息。用于核对,不把当前列表当作永久完整枚举。
job.flags可用于核对实际受理的 mode 与可见性。缺失字段不应自行补造成上游已确认的事实。
job.meta受理时的预计尺寸、结果张数和父任务。最终以结果记录和实际资源为准。
attempts可能出现的此前尝试与错误,不保证只有空数组;失败响应也可能用这个字段。字段可能带额外信息,不要依赖其未列出的内容。

快速 / 慢速适用与验证矩阵

下列所有动作都可在公共请求层传 mode="fast" 或 mode="relaxed";能表达不等于上游一定接受或完成。套餐、模型、源任务和当前资源仍会约束实际执行。

功能fastrelaxed2026-11-13 证据边界
普通 imagine已产出已产出一次快速约 14.47 秒;一次 v8.2 慢速约 42.62 秒,不是固定 SLA 或相同工作负载的严格性能比较
URL、多图、风格、角色、指令式提示词接口可传,未单独实测已产出不能把普通快速文生图成功推广为所有参考组合已验证
upscale接口可传,未单独实测两档已产出本轮只覆盖 v8.2 源图的细腻 / 创意放大
vary接口可传,未单独实测两档已产出轻微 / 强烈变体均有四张结果
reroll接口可传,未单独实测已产出整组重绘
remix接口可传,未单独实测已产出选择一张源图,不代表只输出一张
outpaint接口可传,未单独实测100 改画幅完成;2 扩图未确认完成必须区分两个请求组合
inpaint接口可传,未单独实测已产出遮罩编辑效果尚未完整验收
video历史记录可用;本轮未测上游套餐限制;本轮未测历史 standard 套餐拒绝 relaxed 视频,需 fast;完整当前兼容性未重新核验
只使用外层 mode 控制速度。不要在 prompt 写 --fast、--relax、--turbo:旧验证记录发现它们会改变账号默认速度,影响后续任务,并出现外层 mode 与账号状态不一致。遇到这种情况联系服务方核对,不在循环中反复改速度参数。

八种动作与完整请求

公共规则:id 是源任务 ID;index 是源任务结果的零基索引,必须满足 0 ≤ index < 源结果张数。普通四张结果是 0、1、2、3,放大后只有一张时只能取 0。输入选一张、输出一张是两件不同的事,最终读取 batch_size 和资源数组长度。

1. imagine:生成

字段规格
t必填 string,固定 imagine
prompt必填 string,文字、图片 URL 与兼容参数。没有已公开的稳定最大字符数;过长文本可能被上游拒绝,不保证任意长度。
{"mode":"fast","action":{"t":"imagine","prompt":"a ceramic teapot on a wooden table --ar 3:2 --v 8.2"}}

普通生成本轮输出四张;不同模型或后续动作的张数必须以实际结果为准。显式写版本更利于复现,省略版本时由上游当前默认决定,不承诺永久默认 v8.2。

2. upscale:细腻 / 创意放大

字段规格
t固定 upscale
id / index必填 string / integer,选择一张已有结果
type必填 string,必须匹配源模型,不能只写 subtle 或 creative
源模型细腻 type创意 type证据
v8.2v8r2_2x_subtlev8r2_2x_creative本轮均产出一张,1344×896 到 2688×1792 的尺寸记录
v8.1完整当前枚举未重新确认完整当前枚举未重新确认不要直接把 v8.2 前缀套到 v8.1
v7v7_2x_subtlev7_2x_creative历史记录,本轮未测
v6.1v6r1_2x_subtlev6r1_2x_creative历史记录,本轮未测
v6 / niji 6 / niji 7本页未确认本页未确认不凭模型相似推断前缀
{"mode":"relaxed","action":{"t":"upscale","type":"v8r2_2x_subtle","id":"ee4c507f-521a-4ce0-888c-16aa71df6f63","index":0}}

创意放大将 type 改为 v8r2_2x_creative。模型不匹配可能被上游拒绝,历史错误如 v7 2x upscale is not supported by this model。本轮两档均已产出,不代表所有模型枚举已经覆盖。

3. vary:变体

字段规格
t固定 vary
id / index必填,选择源结果
strong必填 boolean,false 为轻微,true 为强烈,不使用字符串 true 或 false
v8_1_hd / v8_2_hd普通用法显式传 null;历史已识别 true,但高清变体分支本轮未测,不承诺 true 适用于所有源模型
{"mode":"relaxed","action":{"t":"vary","strong":false,"v8_1_hd":null,"v8_2_hd":null,"id":"ee4c507f-521a-4ce0-888c-16aa71df6f63","index":0}}

强烈变体只把 strong 改为 true。本轮每次输出四张新结果,继承和归一化后的参数应再查最终 full_command 或 command。

4. reroll:整组重绘

字段规格
t固定 reroll
id必填源任务 ID
newPrompt本页示例必填完整提示词与参数,不依赖空值继承
index不需要;这是对整组重新生成
{"mode":"relaxed","action":{"t":"reroll","newPrompt":"a ceramic teapot on a wooden table --ar 3:2 --v 8.2","id":"ee4c507f-521a-4ce0-888c-16aa71df6f63"}}

5. remix:改词重生

字段规格
t固定 remix
id / index必填,指定一张源结果
newPrompt必填 string,写完整新描述及需要的参数
strongboolean,true / false;本轮实测 true,false 分支未单独重新验证
{"mode":"relaxed","action":{"t":"remix","strong":true,"newPrompt":"a copper teapot on a pale marble table --ar 3:2 --v 8.2","id":"ee4c507f-521a-4ce0-888c-16aa71df6f63","index":0}}

本轮选一张源图后输出了四张新图。不要把“单张源输入”解释为“固定单张输出”。上游某些编辑结果可能限制后续 remix;本轮没有覆盖全部父任务类型。

6. outpaint:扩图 / 改画幅

字段规格
t固定 outpaint
id / index必填源任务及索引
newPrompt必填完整描述;改画幅时加目标 --ar
zoomFactorinteger。历史曾观察 1、2、3、4、10、100 被受理;这不是这些值都已完成的保证。传 1.5 的历史错误为 Malformed request。完整稳定值域未确认。
{"mode":"relaxed","action":{"t":"outpaint","newPrompt":"a ceramic teapot on a wooden table --ar 9:16","zoomFactor":100,"id":"ee4c507f-521a-4ce0-888c-16aa71df6f63","index":0}}

改画幅已产出:本轮 100 + --ar 9:16 得到 816×1456 的尺寸记录。尺寸会按模型约束调整,不保证像素比完全精确。

{"mode":"relaxed","action":{"t":"outpaint","newPrompt":"a ceramic teapot on a wooden table","zoomFactor":2,"id":"ee4c507f-521a-4ce0-888c-16aa71df6f63","index":0}}
上面四周扩图组合当前异常。v8.2 源图返回了 job_id,但约 17 分 35 秒内没有观察到结果;队列和历史里也未找到它。不能把 200 写成扩图已通过,也不能推断其他 zoomFactor 或模型一定成功。不建议把这个组合用于必须完成的流程。

7. inpaint:区域重绘

先上传与源图对应的 PNG 遮罩取得 mask_url,再提交这个动作。两个请求都需要 submit 权限。不能把 Base64 直接当成 maskUrl。

字段规格
t固定 inpaint
newPrompt必填 string,编辑指令
maskUrl必填 string,使用 /api/mask 实际返回的 mask_url;注意大小写不同
id / index对应上传遮罩时的 job_id / image_num
{"mode":"relaxed","action":{"t":"inpaint","newPrompt":"replace the teapot with a copper kettle","maskUrl":"https://storage.example.com/replace-with-uploaded-mask.png","id":"ee4c507f-521a-4ce0-888c-16aa71df6f63","index":0}}

示例 maskUrl 是占位地址,必须替换为真实上传响应,不能原样使用。本轮 v8.2 源图、1344×896 遮罩、慢速模式约 84.58 秒后观察到四张结果。旧的“此动作完全不可用”判断与这次结果不符;本轮只确认产出,未完整确认透明/颜色语义和区域保真。其他模型、快速模式和更多遮罩条件未测。

8. video:图生视频与延长

本轮未测视频。下面保留历史接入记录,并明确未验证的边界。不要把它与本轮图片验收混成一次全量通过。

字段规格
t固定 video
videoType四个已记录值见下表;不包含已确认可用的首尾帧或循环类型
parentJobobject,job_id 是源图或源视频 ID,image_num 是有效零基索引
animateMode历史接口只接受 auto;不是 low 或 high
newPromptstring,可为空;运动幅度可写 --motion low 或 --motion high
stitch普通示例传 null;其他取值未公开确认
videoType用途
vid_1.1_i2v_480图片生成 480 视频,值里没有 p
vid_1.1_i2v_720图片生成 720 视频,套餐和资源需满足条件
vid_1.1_i2v_extend_480延长 480 源视频
vid_1.1_i2v_extend_720延长 720 源视频
{"mode":"fast","action":{"t":"video","videoType":"vid_1.1_i2v_480","stitch":null,"newPrompt":"--motion low","parentJob":{"job_id":"ee4c507f-521a-4ce0-888c-16aa71df6f63","image_num":0},"animateMode":"auto"}}
{"mode":"fast","action":{"t":"video","videoType":"vid_1.1_i2v_extend_480","stitch":null,"newPrompt":"--motion high","parentJob":{"job_id":"ef5afc90-d951-48ae-92f1-a2c487d62914","image_num":0},"animateMode":"auto"}}

第二个示例的 parentJob 必须是真实源视频,不可照抄示例 UUID。历史 standard 套餐的 relaxed 视频被拒,原错误为 Relax mode for video is limited to Pro and Mega plans。完整当前套餐支持情况、延长次数和最长时长未重新确认。

视频的父任务已带图,不要再在 newPrompt 放图片 URL;历史错误为 Can't use an Image Prompt with a parent job。视频 turbo 的历史错误为 Turbo mode isn't supported for video jobs。首尾双帧、循环与额外视频张数未列为当前已验证能力。

提示词参数:范围、默认与证据

参数写在 prompt 或该动作的 newPrompt 内。不能同时用 --v 和 --niji。未指定的参数可能取上游默认或父任务参数,本服务没有为下表每个参数设固定默认。保留最终 command / full_command 来核对归一化结果。

参数 / 别名取值与边界说明 / 验证
--ar / --aspect正数宽:高。常用:1:1、5:4、4:3、3:2、16:9、2:1、21:9、4:5、3:4、2:3、9:16、1:2、9:21历史上限约 14:1 至 1:14,15:1、100:1 曾钳制;HD 历史上限约 4:1 至 1:4,超限可能报错。实际按模型分辨率调整。本轮 3:2、2:3、9:16 已观察。
--v / --version本页确认模型值:8.2、8.1、7、6.1、6本轮均有生成记录。7 常归一化为 7.0;省略时取上游当前默认,不承诺服务永久固定模型。
--niji6、7本轮两者均产出,与 --v 二选一。
--hd / --sd无值开关;不要同时传两者v8.2 的 hd 已观察 2048×2048,sd 为标准模式;v8.1 本轮测了 sd,hd 仅有历史记录。其他模型没有本轮成功证据。
--raw无值开关减少默认风格干预;本轮 v8.2、v6.1 请求保留,其他版本按矩阵。
--tile无值开关本轮 v8.2 与 hd 组合产出 2048×2048 图。接缝质量没有做像素级验收,其他组合未全测。
--stylize / --s历史参考范围 0–1000本轮测了 250、150;缺省跟随上游。不是把整个范围和所有模型都重新穷举过。
--chaos / --c历史参考范围 0–100本轮测了 15、10,控制结果变化程度。
--weird / --w历史参考范围 0–3000本轮 v8.2 的 100 保留并产出;各模型有效范围未全测。
--quality / --qv7 历史值 1、2、4;v6.1 历史值 0.5、1、2本轮 v7 测 2、4,v6.1 测 0.5。v8.1/8.2 历史记录会静默丢弃,不是 v8 质量开关,v8 使用 hd。默认 1 曾从命令中省略。
--stop历史参考范围 10–100本轮仅 v6.1 的 80 完成;v7/v8 历史记录为不兼容。其他模型未重新核验。
--exp完整稳定值域未确认;本轮值 10v7 的 10 与 quality 4 一起产出。旧记录也识别 v8,但不能把“整数”或一个成功值说成无限范围。
--no排除词字符串,逗号或上游支持的分隔方式如 --no text, watermark。本轮与其他参数组合产出;不是保证所有被排除元素绝对消失。
--seed推荐使用 0–4294967295 的整数本轮测了 271828、123。旧记录观察 0、4294967295、-1 被受理,不代表负数是推荐用法或任意大整数都有效;同 seed 也不保证跨模型或跨动作像素相同。
--sref一个或多个公开图片 URL,用空格分隔本轮单图与两图成功。统一最大数量未确认,不把某个界面限制当 API 限制。
--sw历史参考范围 0–1000本轮 200 成功,控制风格参考强度。缺省由上游决定。
--sv历史解析过整数 1–7,但每个模型只支持子集本轮 v8.2 只确认 --sv 6 成功。旧记录 v8.2 拒绝 1–5、7 为 Unsupported Style Reference version,0、8 为 Invalid value。其他版本当前子集未重新确认。
--iw历史参考范围 0–3本轮 v7 的 1.5 与单图、多图、组合参考均成功;不能推断所有版本使用同一范围。
--cref本轮确认单张公开图片 URLv6.1 与 niji 6 成功;数量上限与其他版本条件未全测。
--cw历史参考范围 0–100本轮值 100。历史语义为 0 更偏脸部、100 包含更多外观;本轮使用物体素材,不构成人脸一致性验收。
--oref本轮确认单张公开图片 URLv7 的 Omni 参考已产出;旧记录 v8.1/8.2、niji 7 不兼容。不能把 v7 写法原样套到所有版本。
--ow旧记录参考范围 0–1000,本轮值 400与 oref 搭配;完整当前边界、默认和语义未重新核验。
--public / --stealth无值开关,不同时传public 本轮保留且记录为公开。stealth 历史需 Pro / Mega;当前 standard 测试条件不支持成功路径。API 数据归属与 MJ 公共可见性不是同一概念。
--profile / --p上游真实个性化代码或受支持的用户标识依赖先在上游配置该模型的个性化档案,本轮未提供成功路径所需代码。不从已脱敏的作品响应里假设能取得用户标识,联系服务提供方确认。
--motionlow、high视频提示词参数的历史记录,本轮未测;与 animateMode 字段不是同一个参数。

完整模型列表与验证范围

表中“未测”不表示不支持。“历史”不是本轮重新验证;对完整合法值域尚未确认的参数,保留未知,不编造最大值。

能力v8.2v8.1v7v6.1v6niji 6niji 7
基础生成已测已测已测已测已测已测已测
fast 普通生成已测未测未测未测未测未测未测
quality历史静默丢弃历史静默丢弃2、4 已测0.5 已测未测未测未测
stop历史拒绝历史拒绝历史拒绝80 已测未测未测未测
hd / sd均已测sd 已测、hd 历史历史不适用历史不适用未测未测未测
图片 URL + iw指令式图片输入已测未测单图、两图已测未测未测未测未测
sref / sw已测未测已测未测未测未测未测
sv 当前支持子集6 已测未确认未确认未确认未确认未确认未确认
cref / cw旧记录不兼容旧记录不兼容使用 oref已测未测已测旧记录不兼容
oref / ow旧记录不兼容旧记录不兼容已测旧记录不兼容未测未测旧记录不兼容
图片 + 指令式提示词已产出,不证明独立 Edit Model 路由旧记录未单独确认未测未测未测未测

组合、继承与不支持项

组合 / 参数记录与边界
垫图 + sref + oref + arv7 本轮已产出;URL 输入会被上游转换为 s.mj.run 短链,这是正常归一化,不是图片被丢弃。
oref + quality历史观察 quality 被静默剥离,任务仍可能产出;不要把受理当成质量参数生效。
hd + tilev8.2 本轮产出 2048×2048;接缝效果未完整验收。
hd + 超宽 ar历史超过 4:1 可能报错,例如 wider than 4:1 are not supported with --hd。
v 与 niji二选一,不要同时使用。
后续动作的 newPrompt可能继承或归一化父任务参数;要复现时显式给需要的参数,再核对结果 command。prompt 有参数不等于上游实际执行了全部参数。
--edit旧网页请求观察会被静默剥离,不是本服务已确认的 Edit Model 开关。图片 + 指令式提示词仍使用 imagine,不据此承诺独立模型路由、固定最多四图或结果不能 remix。
--repeat / --r旧网页接口记录为 Unrecognized parameter(s)。要多次生成可由调用方逐次提交,但每次都创建独立任务。
--bsv7/v8 历史记录不接受自定义批量。不能用它承诺任意输出张数。
--loop / --end视频 loop 历史报 Unrecognized parameter(s),end 历史被静默剥离。本轮未重新测试,不能宣称循环或首尾帧已支持。
--moodboard旧记录不接受该命名参数;不把任意情绪板 ID 当 profile code。其他情绪板接入方式未在本页提供稳定契约。
Pan、Describe、Blend、Retexture没有在当前公开清单中确认可用的独立请求流程;不能因上游存在相近界面就宣称本服务已经实现。

参考图:直接 URL 与上传后引用

支持直接传图片 URL,不必先转 Base64。只有本地文件、调用方内存图片或需要自行读取的受限图片,才先上传换短链。服务不提供“给 /api/upload 一个 url 并代下载”的能力。

用途URL 放置方式本轮证据
构图 / 内容垫图放在 action.prompt 最前面、文字之前;多个地址用空格分隔v7 单图、两图、组合参考已产出
风格参考--sref 后放一个或多个 URL,再配 --swv8.2 单图、v7 多图已产出
Omni 参考v7 用 --oref URL 和 --ow单图、组合已产出
旧版角色参考v6.1 / niji 6 用 --cref URL 和 --cw两种模型已产出

本轮参考素材为测试物体图片,不代表已量化验收真人面部一致性。单图、多图的全量数量上限未确认;本页已测的是一张和两张,不保证任意张数。

直接 URL 的八组完整请求

以下 JSON 均发送到 POST /api/submit,带 Authorization 和 Content-Type。images.example.com 是示例域名,必须换成 MJ 能读取的真实图片地址。没有单独的 image_url 或 reference_urls 生成字段。

1. 单张垫图

{"mode":"relaxed","action":{"t":"imagine","prompt":"https://images.example.com/reference.jpg a teapot in a sunlit studio --iw 1.5 --ar 3:2 --v 7"}}

2. 多张垫图

{"mode":"relaxed","action":{"t":"imagine","prompt":"https://images.example.com/subject.jpg https://images.example.com/scene.jpg a collection of teapots on a shelf --iw 1.5 --v 7"}}

3. 单张风格参考

{"mode":"relaxed","action":{"t":"imagine","prompt":"a paper lantern in a rainy alley --sref https://images.example.com/style.jpg --sw 200 --sv 6 --v 8.2"}}

4. 多张风格参考

{"mode":"relaxed","action":{"t":"imagine","prompt":"a paper lantern in a rainy alley --sref https://images.example.com/style-a.jpg https://images.example.com/style-b.jpg --sw 200 --v 7"}}

5. v7 Omni 参考

{"mode":"relaxed","action":{"t":"imagine","prompt":"a teapot displayed in a cozy cafe --oref https://images.example.com/character.jpg --ow 400 --v 7"}}

6. v6.1 角色参考

{"mode":"relaxed","action":{"t":"imagine","prompt":"a ceramic teapot on a kitchen counter --cref https://images.example.com/character.jpg --cw 100 --v 6.1"}}

niji 6 写法是把 --v 6.1 换成 --niji 6,不能同时保留两种版本参数。

7. 垫图 + 风格 + Omni

{"mode":"relaxed","action":{"t":"imagine","prompt":"https://images.example.com/composition.jpg a teapot in a cozy cafe --iw 1.5 --sref https://images.example.com/style.jpg --sw 200 --oref https://images.example.com/character.jpg --ow 400 --ar 3:2 --v 7"}}

8. v8.2 图片 + 指令式提示词

{"mode":"relaxed","action":{"t":"imagine","prompt":"https://images.example.com/reference.jpg make this into a watercolor painting --sref https://images.example.com/style.jpg --sw 200 --raw --v 8.2"}}

最后一例是 imagine 的图片输入与指令,并没有 --edit 参数或独立编辑模型字段。本轮有产出,但不从这个样本推断所有编辑模型的数量、宽高比继承或 remix 限制。

URL 要求与失败排查

检查项规则
可达性必须让 MJ 能读取。你的浏览器能打开不代表上游能打开;localhost、局域网地址和私有存储不适合直接传。
内容应直接返回图片文件,不是相册、网盘分享页、登录页或验证页。推荐 HTTPS;后缀为 jpg 不足以证明响应是图片。
认证本服务不会向图片站附加你的 Cookie、Authorization 或自定义请求头。受限图片需由调用方先下载再上传,或提供不依赖这些请求头的有效链接。
有有效期的 URL保留完整查询参数,并让链接在排队和上游读取期间有效。不保证任意图片托管服务的链接都被接受。
空格URL 自身空格编码为 %20;多个图片 URL 之间使用普通空格。JSON 里的 & 保持普通字符,不转成字符串 &amp;。
本服务图片 API需要 API Key 的 /api/image 和 /api/thumb 地址不能直接当作 MJ 可读的公开图链,也不要把 API Key 拼进 URL。
Base64不要把 Base64 或 data:image/png;base64 前缀直接塞进 prompt。先传 file_b64,取得 short_url 再引用。
正常归一化第三方 URL 在最终命令里变为 s.mj.run 短链不代表失败。需检查最终参数及真实结果,不能仅看 200。

POST /api/upload:图片上传

POST/api/upload
需要 submit 权限。接收 JSON Base64,不接收 url / image_url 代下载,也不把 raw 二进制上传作为本页支持契约。
字段规格
file_b64必填 string,纯 Base64 或 data:image/png;base64 前缀。示例为可校验的 512×512 PNG;本轮只验证 PNG,其他格式由上游决定。
filename可选 string,默认 image.png;建议使用简短的普通文件名及与内容一致的后缀,不提供本地路径。
{"filename":"reference.png","file_b64":"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"}

上例包含完整测试素材,不是省略片段。将 JSON 作为请求体发送到 /api/upload;真实业务可替换为自己的 Base64,并遵守前述 18 MiB 建议上限。不要手工修改编码中间字符。

{"short_url":"https://s.mj.run/21IY3X09bSU","usage":{"image_prompt":"把 short_url 放在 prompt 最前面","style_ref":"在 prompt 末尾加 --sref https://s.mj.run/21IY3X09bSU","character_ref":"使用兼容版本的 --cref 或 --oref"}}

响应短链用于之后的提交,上传本身不创建生成任务。字段 usage 是帮助文案,不是必须照抄的完整提示词;上传返回的真实 short_url 优先于示例地址。

import base64
import os
import requests

BASE = "{BASE}"
with open("reference.png", "rb") as source:
    image_b64 = base64.b64encode(source.read()).decode("ascii")
response = requests.post(
    BASE + "/api/upload",
    headers={"Authorization": "Bearer " + os.environ["MJ_API_KEY"]},
    json={"filename": "reference.png", "file_b64": image_b64},
    timeout=60,
    allow_redirects=False,
)
if response.status_code != 200:
    raise RuntimeError("Upload failed: HTTP " + str(response.status_code) + " " + response.text[:300])
print(response.json()["short_url"])

POST /api/mask:上传区域遮罩

POST/api/mask
需要 submit 权限,返回之后提交 inpaint 所需的 mask_url。遮罩上传与生成是两个请求。
字段规格
mask_b64必填 string,PNG 的纯 Base64;本轮没有使用 data URL 前缀。不要套用 file_b64 字段名。
job_id必填 string,对应已有源图任务。
image_numinteger,缺省 0;应满足 0 ≤ image_num < 源结果张数,负数或越界不属于本页有效用法。

遮罩应与所选源图实际像素尺寸一致。本服务原样转发,不保证帮你缩放、反转或修正遮罩。当前没有已独立确认的完整颜色/透明度语义;推荐使用上游编辑器的有效遮罩或由接入方先验收。下面是本轮产出的同类演示:白色不透明底,中央矩形透明;不据此承诺透明处必然代表重绘或保留。

# 依赖:python -m pip install requests pillow
# 必须替换为你真实源任务的 ID、索引和实际尺寸
import base64
from io import BytesIO
import os
import requests
from PIL import Image, ImageDraw

BASE = "{BASE}"
JOB_ID = "ee4c507f-521a-4ce0-888c-16aa71df6f63"
IMAGE_NUM = 0
WIDTH, HEIGHT = 1344, 896
HEAD = {"Authorization": "Bearer " + os.environ["MJ_API_KEY"]}
mask = Image.new("RGBA", (WIDTH, HEIGHT), (255, 255, 255, 255))
ImageDraw.Draw(mask).rectangle(
    (WIDTH // 3, HEIGHT // 3, WIDTH * 2 // 3, HEIGHT * 2 // 3),
    fill=(255, 255, 255, 0),
)
stream = BytesIO()
mask.save(stream, "PNG")
upload = requests.post(
    BASE + "/api/mask", headers=HEAD, timeout=60, allow_redirects=False,
    json={"job_id": JOB_ID, "image_num": IMAGE_NUM,
          "mask_b64": base64.b64encode(stream.getvalue()).decode("ascii")},
)
if upload.status_code != 200:
    raise RuntimeError("Mask upload failed: HTTP " + str(upload.status_code))
mask_url = upload.json()["mask_url"]
created = requests.post(
    BASE + "/api/submit", headers=HEAD, timeout=60, allow_redirects=False,
    json={"mode": "relaxed", "action": {
        "t": "inpaint", "newPrompt": "replace the teapot with a copper kettle",
        "maskUrl": mask_url, "id": JOB_ID, "index": IMAGE_NUM,
    }},
)
if created.status_code != 200:
    raise RuntimeError("Inpaint submission failed: HTTP " + str(created.status_code))
print(created.json()["job"]["job_id"])
{"mask_url":"https://storage.example.com/uploaded-mask.png"}

响应可能含额外字段,接入只需要 mask_url。上面的返回地址也是形状示例,不是真实可用遮罩。本轮上传成功约 0.60 秒;不同模型、图片尺寸和遮罩语义不能仅凭这一次请求外推。

查询图片结果与下载 URL

生成结果以 URL 返回,不返回 Base64。提交任务后,使用响应中的 job.media_url 查询结果。新图片任务会自动准备结果,调用方不需要额外发起准备请求。

GET/api/media/{jobId}
需要 read 权限,携带本服务 API Key。只允许读取自己的任务;结果未登记返回 404,结果服务暂不可用返回 503。
字段 / 状态含义
archive_status结果准备状态,保留实际响应字段名。pending:等待处理;processing:处理中;retry:等待自动重试;ready:全部图片可下载;failed:本次结果准备失败;unsupported:当前不支持获取该类型结果。这是结果可用性状态,不是生成进度百分比。
images结果图片数组。每张有零基 index 和 status;只有 status=ready 的条目才有 download_url、thumb_url、preview_url、尺寸、字节数、content_type 和 sha256。
download_url原始 PNG 图片的下载 URL。请求这个 URL 时不要带本服务的 Authorization。使用响应中的完整地址,不修改路径或查询参数。
thumb_url / preview_url384px / 640px WebP 预览 URL,按边界保持比例缩放;下载原始图片使用 download_url。
url_expires_atURL 到期时间,Unix 秒。当前约 15 分钟有效;过期后重新查询本接口取得新 URL,不把一次返回的 URL 当永久地址。
error / attempts错误说明和尝试次数。failed 表示结果准备失败,不一定表示生成失败;retry 不会创建新生成任务。
{"job_id":"ee4c507f-521a-4ce0-888c-16aa71df6f63","archive_status":"processing","attempts":1,"error":null,"url_expires_at":1794582000,"images":[{"index":0,"status":"ready","content_type":"image/png","width":1344,"height":896,"bytes":1376205,"sha256":"replace-with-response-checksum","download_url":"https://images.example.com/result-0.png?token=replace-with-current-response","thumb_url":"https://images.example.com/result-0-small.webp?token=replace-with-current-response","preview_url":"https://images.example.com/result-0-large.webp?token=replace-with-current-response"},{"index":1,"status":"pending"},{"index":2,"status":"pending"},{"index":3,"status":"pending"}]}

这是响应形状示例,地址、校验值和时间都需以真实响应为准。上例第一张已可下载,其余仍在准备中;如果需要整组图片,等待 archive_status=ready。图片结果不包含 file_b64、image_base64 或其他 Base64 文件内容。

URL 使用边界:拿到完整 URL 的人在有效期内可读取文件,请勿公开分享。撤销 API Key 会阻止再次查询结果,但此前取得的 URL 在到期前可能仍可使用。
POST/api/media/{jobId}/archive
需要 submit 权限,JSON 请求体为 {}。仅用于历史任务初始化结果,或对 failed / retry 的结果重新安排处理。新任务无需调用。全部结果就绪返回 200,否则返回 202;不会重新生成图片。
curl --fail-with-body -X POST {BASE}/api/media/ee4c507f-521a-4ce0-888c-16aa71df6f63/archive \
  -H "Authorization: Bearer $MJ_API_KEY" \
  -H "Content-Type: application/json" -d '{}'

历史任务需能在最近结果窗口找到,否则返回 404。只允许处理自己的任务,不允许通过猜测 ID 读取其他人的图片。已就绪的图片会保留;请求超时后先查询状态,不盲目重新提交生成任务。

当前图片结果准备支持每张不超过 64MiB。视频不适用这组图片结果流程,视频接入范围见单独小节。

GET /api/jobs:原始任务记录列表

GET/api/jobs?page_size=100
需要 read 权限。查询最近记录,本轮用于关联 job_id;不是逐任务状态查询。
参数规格
page_sizeinteger,默认 60,当前代码收敛到 1–100。仅表示拉取最近窗口,没有 cursor、offset 或翻页保证。
{"data":[{"id":"ee4c507f-521a-4ce0-888c-16aa71df6f63","job_type":"v8-2_diffusion","event_type":"diffusion","full_command":"a ceramic teapot on a wooden table --ar 3:2 --v 8.2","batch_size":4,"width":1344,"height":896,"enqueue_time":"2026-11-13T14:44:20.115853+00:00","parent_id":null,"parent_grid":null}],"scoped_to_key":true}

列表可附加 media_archive,其字段与 /api/media/{jobId} 的独立响应相同,包含图片可用状态及 URL。新任务优先使用 job.media_url 查询,不依赖列表窗口。

此接口每次只查询一个来源的最近记录,不聚合所有生成来源;跨来源查结果优先使用 /api/gallery,并仍注意窗口限制。依赖字段限于示例中的任务身份、命令、类型、结果数量、尺寸和父任务关系。响应当前可能有额外上游字段,不将其全部提升为稳定公开契约,也不要传播额外身份信息。没有可靠的 completed_at、started_at 或终态错误字段,enqueue_time 不能替代 GPU 执行起止时间。

GET/api/gallery?page_size=30
需要 read 权限。按记录时间倒序返回作品卡片,带当前接口的相对取图路径。
参数规格
page_sizeinteger,默认 30,当前每个来源拉取窗口收敛到 1–60;聚合多个来源时总数量可能超过该值。没有 cursor 或完整历史翻页。
{"items":[{"id":"ee4c507f-521a-4ce0-888c-16aa71df6f63","prompt":"a ceramic teapot on a wooden table","command":"a ceramic teapot on a wooden table --ar 3:2 --v 8.2","job_type":"v8-2_diffusion","event_type":"diffusion","is_video":false,"batch_size":4,"width":1344,"height":896,"enqueued_at":"2026-11-13T14:44:20.115853+00:00","thumbs":["/api/thumb/ee4c507f-521a-4ce0-888c-16aa71df6f63/0?size=384","/api/thumb/ee4c507f-521a-4ce0-888c-16aa71df6f63/1?size=384","/api/thumb/ee4c507f-521a-4ce0-888c-16aa71df6f63/2?size=384","/api/thumb/ee4c507f-521a-4ce0-888c-16aa71df6f63/3?size=384"],"fulls":["/api/thumb/ee4c507f-521a-4ce0-888c-16aa71df6f63/0?size=640","/api/thumb/ee4c507f-521a-4ce0-888c-16aa71df6f63/1?size=640","/api/thumb/ee4c507f-521a-4ce0-888c-16aa71df6f63/2?size=640","/api/thumb/ee4c507f-521a-4ce0-888c-16aa71df6f63/3?size=640"]}],"account_count":1,"accounts":[],"errors":[],"scoped_to_key":true}

卡片可附加 media_archive,包含 archive_status 与 images 中的结果 URL。新任务直接用 job.media_url 查询即可;历史任务未登记结果时可请求 /api/media/{jobId}/archive。原有 thumbs / fulls 路径就绪后返回 302 下载地址,未就绪返回 202 JSON,不能无条件保存为图片。

thumbs / fulls 是相对路径,调用程序需要拼接 API 基地址并带自己的 API Key。索引与数组顺序一致;不要固定循环四次。is_video 为 true 时是视频结果,封面路径的尺寸可用性需单独判断。

有限窗口先拉取、再按归属过滤。返回少不一定是任务少;高吞吐或较旧任务可能落到窗口外。没有找到 ID 不等于未生成或失败。scoped_to_key=true 也不能消除前述历史未归属放行例外。卡片没有对外提供可依赖的账号身份字段,不能用它寻找个人 user ID。

图片 URL、尺寸与下载

推荐从 /api/media/{jobId} 的 images 取得 URL:download_url 用于原始 PNG,thumb_url 与 preview_url 用于预览。查询接口返回 JSON 和 URL,不返回图片 Base64;对下载 URL 的请求才返回图片文件。

GET/api/thumb/{jobId}/{index}?size=384
需要 read 权限;kind=image(缺省)请求图片,kind=video 请求视频封面。index 为有效零基结果序号,不能超过实际张数。
GET/api/image/{jobId}/{index}?size=original
需要 read 权限,只用于图片。图片就绪后返回 HTTP 302,Location 是可下载 URL;尚未就绪返回 202 JSON。推荐使用 media 接口以 JSON 取得 URL。
size图片结果
128按 128px 边界保持比例生成预览,实际尺寸受原图宽高比影响。
384按 384px 边界保持比例生成预览,对应 thumb_url 的预览规格。
640按 640px 边界保持比例生成预览,对应 preview_url 的预览规格。
original原始 PNG,与已就绪结果的 download_url 对应,实际尺寸以 width / height 和文件内容为准。

不要传任意数值期待动态缩放。size 描述图片规格;视频封面并非每个尺寸都有,本轮观察 640px 封面可读,部分 384px 封面返回 404。

# 查看响应的状态和 Location,不将 JSON 或跳转响应保存为图片
curl --fail-with-body -D - -o /dev/null "{BASE}/api/image/ee4c507f-521a-4ce0-888c-16aa71df6f63/0?size=original" \
  -H "Authorization: Bearer $MJ_API_KEY"

如果返回 302,用不带 API Key 的独立请求访问 Location;如果返回 202,按 JSON 状态继续查询。不要把 API Key 自动附加到返回的下载 URL。下载成功应为 HTTP 200 和正确的 Content-Type,保存文件后可用 bytes 与 sha256 验证完整性。

视频结果范围

视频当前不适用 /api/media 的图片结果流程,会显示 unsupported。历史已知的视频 URL 形状如下,需替换真实任务 ID 和有效序号;本轮没有重新验收视频生成或完整下载,不宣称它与图片流程具有相同保障。

https://cdn.midjourney.com/video/ee4c507f-521a-4ce0-888c-16aa71df6f63/0.mp4
https://cdn.midjourney.com/video/ee4c507f-521a-4ce0-888c-16aa71df6f63/0_640_N.webp?frame=last

访问这些地址也不要发送本服务 API Key。若返回 403,检查 URL 有效性与当前可用性并联系服务方,不假设所有 403 都能靠等待解决。

GET /api/credits:共享 GPU 额度快照

GET/api/credits
需要 read 权限。返回服务当前共享生成资源,不代表这把 Key 的剩余请求次数或独占额度。
{"account_count":1,"relax_ms_total":-8481557,"fast_available_ms_total":44940000,"accounts":[{"plan":"standard","relax_ms":-8481557,"fast_available_ms":44940000}]}
字段类型与说明
account_countnumber,快照中可汇总的资源数量,不是你的 Key 任务数
fast_available_ms_totalnumber,快速余额毫秒;除以 60000 得分钟,除以 3600000 得小时
relax_ms_totalnumber,慢速资源结余,可为负;不能据此精确换算某任务何时开始
accounts可能包含套餐和余额条目,不依赖数组顺序或把它当作可操作身份列表

数据是快照,当前可能缓存约 45 秒;任务完成后立即查询不保证计费已经最终结算。提交、列表查询和下载产生的 Key 请求次数与这些 GPU 毫秒不是同一指标。没有公开的单 Key 剩余次数查询,需联系服务方确认配置。

GET /api/queue:共享队列计数

GET/api/queue
需要 read 权限,返回共享生成资源的队列汇总,不按这把 Key 单独过滤。
{"running":2,"waiting":1,"account_count":1,"accounts":[]}

running / waiting 是计数,不是任务 ID 列表;快照可能有缓存。0 / 0 不能证明你的任务完成,也不能证明任务失败。不要用它计算单个任务精确排队时间或 GPU 时间。

GET /health:连通性检查

GET/health
需要 read 权限。确认本次服务连通和 Key 认证,不保证上游、额度和图片全链路可用。
{"service":"mj-lite-service","version":"0.2","ok":true,"accounts":1,"docs":"/docs"}

错误、恢复与响应结构

先看 HTTP 状态,再检查 Content-Type。应用错误通常有字符串 error;hint、detail、attempts、retry_after 都可能缺省,不能假设每次同形。请求入口或其他上游失败还可能返回 HTML 或纯文本。

HTTP可能来源处理建议
200请求成功;提交动作只是已受理保存任务 ID,继续核对列表和资源
202结果已登记,但图片尚未全部就绪读取 JSON 状态并有限轮询,不能把正文当图片
302已鉴权图片接口返回下载地址不要向下载 URL 发送 API Key;优先从 media JSON 取得 download_url 后独立下载
304图片缓存条件请求命中复用已有正文,不把空响应当新图片
400JSON、字段或动作格式有误检查 error 和请求体;不反复重发原样请求
401API Key 缺失/无效,或上游登录状态失效被转发根据 error 区分,不把上游身份问题都当作要换 API Key
403缺权限、Key 已撤销/过期、作品归属拒绝,或上游图片访问验证若 error 为图片获取失败,联系服务方排查取图;不要盲目更换有效 Key
404路径、任务、索引或尺寸不存在;图片尚未就绪;任务没有产出核对字段及实际资源,不默认所有 404 只需等一会儿
413请求体或解码后图片超过限制缩小原文件,计算 Base64 和 JSON 总大小;响应可能不是 JSON
429Key 每日次数、图片短时限流或上游限制按 error 和可选 retry_after 区分。每日限额等 UTC 日期切换,短时限流按秒等待;GET 轮询也占次数
500未预期的服务错误保留时间、路径及响应,联系服务方;不要自动重发可能已受理的 POST
502上游拒绝、参数/模型冲突、上游访问失败,或多次尝试均失败检查 error 与 attempts,不把所有 502 都当短暂网络错误
503没有可用生成容量或服务暂不可用停止继续提交,查询恢复情况;不要用高频重试加重队列
{"error":"请求没有 submit 权限","hint":"联系服务提供方确认权限"}
{"error":"图片获取失败 (403)","detail":null}
{"error":"图片请求太频繁","retry_after":60}

retry_after 是 JSON 里的秒数示例,不承诺所有错误都返回 HTTP Retry-After 头。当前没有稳定统一的机器错误 code 枚举,本页不虚构一组与实现不符的代码。

可运行的 Python 与 Node.js 示例

示例需要自己的 API Key,运行入口会真实提交任务,默认使用 relaxed。提交后直接按任务 ID 等待结果 URL,不需要先轮询作品列表;取得 URL 后用不含 API Key 的独立请求下载。结果为 URL,不含 Base64。失败会明确报错,不自动重发 POST。

Python 3.10+:requests

先安装 requests,设置 MJ_API_KEY,保存为 client.py。主流程为 submit → wait_for_result → download_image;wait_for_record 是按列表查看记录的可选辅助函数,不是获取结果 URL 的前置条件。

import os
import time
from pathlib import Path
import requests

BASE = "{BASE}"
KEY = os.environ.get("MJ_API_KEY", "").strip()
if not KEY:
    raise RuntimeError("Set MJ_API_KEY before running")
HEAD = {"Authorization": "Bearer " + KEY}


def api_request(method, path, body=None, timeout_seconds=30):
    response = requests.request(
        method, BASE + path, headers=HEAD, json=body,
        timeout=timeout_seconds, allow_redirects=False,
    )
    if not 200 <= response.status_code < 300:
        try:
            message = response.json().get("error", response.text[:200])
        except ValueError:
            message = response.text[:200]
        raise RuntimeError("HTTP " + str(response.status_code) + ": " + str(message))
    return response


def submit(prompt, mode="relaxed"):
    response = api_request("POST", "/api/submit", {
        "mode": mode, "action": {"t": "imagine", "prompt": prompt},
    })
    return response.json()["job"]["job_id"]


def wait_for_record(job_id, timeout_seconds=600):
    deadline = time.monotonic() + timeout_seconds
    while True:
        remaining = deadline - time.monotonic()
        if remaining <= 0:
            raise TimeoutError("Observation deadline reached; task completion is unknown: " + job_id)
        response = api_request("GET", "/api/gallery?page_size=60", timeout_seconds=min(20, remaining))
        for record in response.json().get("items", []):
            if record.get("id") == job_id:
                return record
        time.sleep(min(10, max(0, deadline - time.monotonic())))


def wait_for_result(job_id, timeout_seconds=600):
    deadline = time.monotonic() + timeout_seconds
    while time.monotonic() < deadline:
        remaining = deadline - time.monotonic()
        payload = api_request("GET", "/api/media/" + job_id, timeout_seconds=max(0.1, min(20, remaining))).json()
        if payload["archive_status"] == "ready":
            return payload
        if payload["archive_status"] in ("failed", "unsupported"):
            raise RuntimeError("Result preparation failed: " + str(payload.get("error")))
        time.sleep(min(10, max(0, deadline - time.monotonic())))
    raise TimeoutError("Result observation deadline reached: " + job_id)


def download_image(job_id, index, path):
    manifest = api_request("GET", "/api/media/" + job_id).json()
    image = next((item for item in manifest["images"] if item["index"] == index and item["status"] == "ready"), None)
    if image is None:
        raise RuntimeError("Image result is not ready")
    # Do not send the service API Key to the returned download URL.
    response = requests.request("GET", image["download_url"], timeout=60, allow_redirects=False)
    if response.status_code != 200 or not response.headers.get("Content-Type", "").lower().startswith("image/"):
        raise RuntimeError("Image download failed: HTTP " + str(response.status_code))
    digest = __import__("hashlib").sha256(response.content).hexdigest()
    if len(response.content) != image["bytes"] or digest != image["sha256"]:
        raise RuntimeError("Image file integrity check failed")
    Path(path).write_bytes(response.content)
    return path


if __name__ == "__main__":
    job_id = submit("a ceramic teapot on a wooden table --ar 3:2 --v 8.2", mode="relaxed")
    print("Accepted:", job_id)
    result = wait_for_result(job_id)
    print("Result URLs ready:", len(result["images"]))
    download_image(job_id, 0, "out.png")
    print("Downloaded out.png")

Node.js 18+:内置 fetch,保存为 client.cjs

使用 CommonJS 的 .cjs 后缀运行 node client.cjs;不需要第三方请求库。每次请求使用 AbortSignal.timeout,总轮询也有截止时间。BASE 只在一处配置,不把模板变量混成错误 URL。

const { writeFileSync } = require("node:fs");
const BASE = "{BASE}";
const KEY = (process.env.MJ_API_KEY || "").trim();
if (!KEY) throw new Error("Set MJ_API_KEY before running");

async function apiRequest(method, path, body, timeoutMs = 20000) {
  const response = await fetch(BASE + path, {
    method,
    headers: {
      Authorization: "Bearer " + KEY,
      "Content-Type": "application/json",
    },
    body: body === undefined ? undefined : JSON.stringify(body),
    redirect: "error",
    signal: AbortSignal.timeout(timeoutMs),
  });
  if (!response.ok) {
    const text = await response.text();
    let message = text.slice(0, 200);
    try { message = JSON.parse(text).error || message; } catch { }
    throw new Error("HTTP " + response.status + ": " + message);
  }
  return response;
}

async function submit(prompt, mode = "relaxed") {
  const response = await apiRequest("POST", "/api/submit", {
    mode, action: { t: "imagine", prompt },
  });
  return (await response.json()).job.job_id;
}

async function waitForRecord(jobId, timeoutMs = 600000) {
  const deadline = Date.now() + timeoutMs;
  while (Date.now() < deadline) {
    const remaining = deadline - Date.now();
    if (remaining <= 0) break;
    const response = await apiRequest("GET", "/api/gallery?page_size=60", undefined, Math.max(1, Math.min(20000, remaining)));
    const data = await response.json();
    const found = (data.items || []).find(record => record.id === jobId);
    if (found) return found;
    await new Promise(resolve => setTimeout(resolve, Math.min(10000, Math.max(0, deadline - Date.now()))));
  }
  throw new Error("Observation deadline reached; task completion is unknown: " + jobId);
}

async function waitForResult(jobId, timeoutMs = 600000) {
  const deadline = Date.now() + timeoutMs;
  while (Date.now() < deadline) {
    const remaining = Math.max(1, Math.min(20000, deadline - Date.now()));
    const response = await apiRequest("GET", "/api/media/" + jobId, undefined, remaining);
    const manifest = await response.json();
    if (manifest.archive_status === "ready") return manifest;
    if (["failed", "unsupported"].includes(manifest.archive_status)) throw new Error("Result preparation failed: " + manifest.error);
    await new Promise(resolve => setTimeout(resolve, Math.min(10000, Math.max(0, deadline - Date.now()))));
  }
  throw new Error("Result observation deadline reached: " + jobId);
}

async function downloadImage(jobId, index, filename) {
  const manifest = await (await apiRequest("GET", "/api/media/" + jobId)).json();
  const image = manifest.images.find(item => item.index === index && item.status === "ready");
  if (!image) throw new Error("Image result is not ready");
  // Do not send the service API Key to the returned download URL.
  const response = await fetch(image.download_url, { signal: AbortSignal.timeout(60000), redirect: "error" });
  if (!response.ok || !(response.headers.get("content-type") || "").toLowerCase().startsWith("image/")) {
    throw new Error("Image download failed: HTTP " + response.status);
  }
  const bytes = new Uint8Array(await response.arrayBuffer());
  const digest = require("node:crypto").createHash("sha256").update(bytes).digest("hex");
  if (bytes.byteLength !== image.bytes || digest !== image.sha256) throw new Error("Image file integrity check failed");
  writeFileSync(filename, bytes);
  return filename;
}

async function run() {
  const jobId = await submit("a ceramic teapot on a wooden table --ar 3:2 --v 8.2", "relaxed");
  console.log("Accepted:", jobId);
  const result = await waitForResult(jobId);
  console.log("Result URLs ready:", result.images.length);
  await downloadImage(jobId, 0, "out.png");
  console.log("Downloaded out.png");
}

if (require.main === module) {
  run().catch(error => { console.error(error.message); process.exitCode = 1; });
}

常见问题与接入验收

快速模式是不是只有文生图?

不是,mode 是公共提交字段。但本轮快速模式只验证了普通文生图;其他动作的 fast 分支明确标为未单独实测,不把“可传”写成“已完成”。

为什么提交 200 后一直没有结果?

可能仍在处理、落到列表窗口外、上游未产出或状态无法查询。保存任务 ID、提交时间和响应,使用有限等待窗。队列 0 不构成成功证明;不要自动重新提交导致重复任务。

为什么图片 URL 返回 403,但 API Key 能查询结果?

查询权限和下载 URL 的有效性需分别检查。先重新查询结果,使用最新 download_url 再下载,且不要附加 API Key。如果最新 URL 仍被拒绝,保留任务 ID、时间和响应状态联系服务方,不反复提交生成任务。

结果是否返回 Base64?

不返回。生成结果通过 images 中的 download_url、thumb_url、preview_url 提供。参考图上传支持 file_b64,这是输入方式,不代表生成输出也是 Base64。

如何获取原始分辨率的图片?

使用 images 中已就绪结果的 download_url,按 content_type、width、height、bytes、sha256 核对文件。预览 URL 用于展示,不能代替原图。URL 失效后重新查询即可。

为什么新 Key 还能看到部分旧作品?

历史未归属任务当前放行,不符合严格隔离承诺。接入方应知悉此限制;有明确归属的新任务仍按 Key 判断。不能仅因 scoped_to_key=true 就推断所有历史数据已隔离。

如何知道还有多少调用次数?

/api/credits 给 GPU 毫秒,不给单 Key 剩余次数。每日次数、权限、到期时间联系服务方确认。轮询和取图也计数,UTC 00:00 重置。

批量生成怎么做?

当前没有公开的一次多任务事务或批量幂等接口。由调用方逐条提交并保存每个 job_id;先使用较低并发,按资源与请求预算调节,失败时不要盲重发全部。每个请求都是独立任务和独立结果。

这份文档是否宣称全部参数已实测?

没有。它覆盖当前列出的公开能力,并把已产出、历史记录、条件限制、未确认和当前异常分开。个人化成功路径、stealth 成功路径、视频新一轮生成、所有模型交叉参数、所有数值边界、人物一致性、区域保真都没有被本轮完整验收。

接入验收至少分别核对:认证、权限、受理 ID、实际参数、结果张数、资源可读、尺寸格式、超时恢复和数据范围。功能变更、上游版本或套餐变化后,相关状态需要重新确认,不能只靠文档存在某个参数就宣布可用。