Midjourney(MJ)API 调用
MJ 使用异步任务流程:提交 Imagine,保存任务 ID,再轮询任务状态,成功后读取 imageUrl。
当前为 dev 预览,生产环境尚未开放。 本页示例使用 https://teamorouter-dev.teamolab.com,需要该开发环境的 API Key 和余额。生产环境的 Key、余额与 dev 分开,不能混用。
接口地址与鉴权
Base URL 为 https://teamorouter-dev.teamolab.com,不带 /v1。请求使用通用 MJ 格式,不能通过 OpenAI Images SDK 的 images.generate() 调用。
| 方法 | 路径 | 用途 |
|---|---|---|
| POST | /mj/submit/imagine |
提交 Imagine 任务 |
| GET | /mj/task/{task_id}/fetch |
查询任务状态及结果 |
两个接口都需要 TeamoRouter API Key:
Authorization: Bearer <TEAMO_API_KEY>
Content-Type: application/json
下方示例从环境变量读取 Key,请在自己的服务端保存凭据。
提交 Imagine
export TEAMO_API_BASE="https://teamorouter-dev.teamolab.com"
export TEAMO_API_KEY="<你的 dev API Key>"
curl --max-time 610 -D mj-submit-headers.txt \
"$TEAMO_API_BASE/mj/submit/imagine" \
-H "Authorization: Bearer $TEAMO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"botType": "MID_JOURNEY",
"prompt": "A tiny blue sailboat on a calm lake, watercolor illustration --ar 1:1",
"state": "my-mj-request-001"
}'
| 参数 | 必填 | 说明 |
|---|---|---|
prompt |
是 | 非空的图片描述,可带 --ar 等普通 MJ 参数。 |
botType |
否 | 省略或填写 MID_JOURNEY;当前不支持 NIJI_JOURNEY。 |
model |
否 | 省略即可;若填写,仅接受 mj_imagine 或 mj_imagine-1k。 |
state |
否 | 调用方自定义的关联标记,不是幂等键,不能防止重复提交。 |
受理响应示例:
{
"code": 1,
"description": "提交成功",
"result": "1320098173412546"
}
必须同时检查 HTTP 成功、code 为数字 1(已提交)或 22(已排队),以及 result 为有效任务 ID。HTTP 200 本身不代表受理成功;其他业务码应查看 description。result 可能是字符串或整数,保存为字符串后用于查询。任务 ID 由字母、数字、_ 或 - 组成,最长 128 个字符。
保存响应头 x-request-id,用于账单查询和问题排查。如果响应头包含 X-Teamo-Task-Lookup: pending,保留已返回的任务 ID,稍后查询;暂时的 404 不代表提交失败,不能因此再次提交。
查询任务与取得图片
将 TASK_ID 替换为提交响应的 result:
curl "$TEAMO_API_BASE/mj/task/TASK_ID/fetch" \
-H "Authorization: Bearer $TEAMO_API_KEY"
建议每 10 秒查询一次。响应直接是 MJ task 对象,没有额外的 data 包装。成功示例(节选):
{
"id": "1320098173412546",
"status": "SUCCESS",
"progress": "100%",
"imageUrl": "https://example.com/mj-result.png",
"failReason": ""
}
status |
处理方式 |
|---|---|
空字符串、NOT_START、SUBMITTED、IN_PROGRESS 等非终态 |
保留任务 ID,继续轮询;0% 不代表失败。 |
SUCCESS |
停止轮询,读取 imageUrl 并及时保存图片。 |
FAILURE、CANCEL |
停止轮询,查看 failReason。 |
结果中还可能包含 imageUrls、buttons 等上游字段。返回 buttons 不表示当前已支持执行放大、变体等 Action。图片 URL 的域名和有效期取决于上游,不保证永久有效。
任务查询按用户隔离,同一用户可使用其其他有效 Key 查询。任务查询记录从首次登记起保留 7 天,重复查询不延长保留时间;这不等于图片保留 7 天。任务不存在、已过期或不属于当前用户时都返回 404。
Python 完整示例
使用 Python 标准库,无需 OpenAI SDK。示例只提交一次,并在等待超时后保留任务 ID;再次查询应使用这个 ID,不能重跑提交步骤。
import json
import os
import re
import time
import urllib.error
import urllib.request
base = os.environ.get("TEAMO_API_BASE", "https://teamorouter-dev.teamolab.com").rstrip("/")
key = os.environ["TEAMO_API_KEY"]
def request(path, body=None):
req = urllib.request.Request(
base + path,
data=json.dumps(body).encode() if body is not None else None,
headers={
"Authorization": "Bearer " + key,
"Content-Type": "application/json",
},
)
try:
with urllib.request.urlopen(req, timeout=610 if body is not None else 60) as response:
return json.load(response), response.headers
except urllib.error.HTTPError as error:
request_id = error.headers.get("x-request-id", "unknown")
detail = error.read().decode("utf-8", errors="replace")
raise RuntimeError(f"HTTP {error.code}; request_id={request_id}; {detail}") from error
# 只提交一次;网络异常或 unknown 结果也不要自动重试此调用。
submitted, headers = request("/mj/submit/imagine", {
"botType": "MID_JOURNEY",
"prompt": "A tiny blue sailboat on a calm lake --ar 1:1",
"state": "my-mj-request-001",
})
task_id = str(submitted.get("result") or "")
if submitted.get("code") not in (1, 22) or not re.fullmatch(r"[A-Za-z0-9_-]{1,128}", task_id):
raise RuntimeError(submitted)
print("Save task_id:", task_id, "request_id:", headers.get("x-request-id"), flush=True)
deadline = time.monotonic() + 900
while time.monotonic() < deadline:
time.sleep(10)
task, _ = request(f"/mj/task/{task_id}/fetch")
status = task.get("status", "")
print(status, task.get("progress"))
if status == "SUCCESS":
print(task["imageUrl"])
break
if status in ("FAILURE", "CANCEL"):
raise RuntimeError(task.get("failReason") or status)
else:
raise TimeoutError(f"Continue querying task {task_id}; do not submit again")
计费与支持范围
当前 1K 标准挂牌价为 $0.10 / 已受理的 Imagine 任务。code=1 或 22 且返回有效任务 ID 即按一个任务收费,不按四宫格中的每张图分别收费;实际金额可能受账号倍率、专属费率或促销影响。
查询任务不会再次扣 Imagine 费。未受理的请求释放预扣;已受理任务后续变为 FAILURE 或 CANCEL,当前不会自动退款。可在账单中按提交响应的 x-request-id 核对这次费用。
当前只开放 Imagine 的 1K 任务,不支持 2K / 4K、Niji、Action(放大、变体、重绘)、批量查询和回调。不要设置 notifyHook,也不要通过 accountFilter.channelId、instanceId 或 modes 指定供应商或模式。
以下 prompt 选项会改变费用或任务数量,当前返回 400:--hd、--q / --quality、--repeat / --r、--fast、--turbo、--relax、--niji,包括 --参数=值 写法。--ar、版本和普通风格参数保留 MJ 语义,由上游校验。
错误处理与重试
| 情况 | 处理方式 |
|---|---|
| 400 参数错误 | 检查 prompt、model、botType 和不支持的费用参数。 |
| 401 | 使用当前环境有效的 TeamoRouter API Key。 |
| 402 余额或预算不足 | 检查当前环境余额及账号预算。 |
| 404 查询不到任务 | 确认任务 ID、用户和 7 天保留期;提交响应为 pending 时稍后再查询。 |
| 429 限流 | 降低请求频率;提交请求仍需遵守下方的未知结果处理。 |
| 5xx 或连接超时 | 先判断请求是否可能已提交,不要直接重发 POST。 |
当错误类型为 midjourney_submission_unknown,或响应头包含 X-Teamo-Submission-State: unknown、X-Should-Retry: false 时,上游可能已创建任务。关闭 HTTP 客户端、SDK 和代理对提交 POST 的自动重试,保留 x-request-id 并联系支持核对;有任务 ID 时继续查询该任务。
当前没有端到端的 Idempotency-Key 去重保证,相同 state 或相同提示词再次 POST 都可能创建并收费一个新任务。查询 GET 可以继续轮询;未知提交结果和等待出图超时都不能作为重新提交的依据。