---
title: "编辑图像"
description: "编辑图像（OpenAI Images 协议）。"
canonical: "https://pirouter.ai/zh/docs/api/image-edits"
last-updated: "2026-10-16"
lang: "zh"
---

# 编辑图像

编辑图像（OpenAI Images 协议）。

`POST https://api.pirouter.ai/v1/images/edits`

兼容 OpenAI 的图像编辑接口：一张或多张输入图（可附一张 mask）加一段提示词，以 `multipart/form-data` 发送。平台只读取 `model` 和 `stream` 两个文本部件；其他部件（`image[]`、`mask`、`size`、`quality`、`n` 等）原样转发给上游 provider，但路由字段 `provider`、`models`、`route` 和 `fallbacks` 在这里不支持，会被删掉。只有上游支持编辑的图像模型才在本端点提供；调用其他模型返回 `validation_failed`（422）。`stream: true` 时响应是 `image_edit.*` 事件组成的 Server-Sent Events 流。请求体上限 32 MiB，超出返回 413。

## 鉴权

用下面任一请求头传 API key。

| 请求头 | 值 | 说明 |
| --- | --- | --- |
| `Authorization` | `Bearer sk-pi-…` | 平台签发的 API key，以 `sk-pi-` 开头，作为 Bearer token 发送。 |
| `X-Api-Key` | `sk-pi-…` | 同一个平台签发的 `sk-pi-` key，放在 `X-Api-Key` 请求头里发送。只有 `Authorization` 缺失或为空时才读取这个请求头；两个都带时，以 `Authorization` 为准。两个请求头都接受不带 `Bearer ` 前缀的裸 key。 |

## 请求体

`multipart/form-data`

OpenAI Images 编辑请求，以 multipart/form-data 发送。这里只列出平台读取或协议要求的部件，其余部件原样转发给上游；但本端点不支持路由部件 `provider`、`models`、`route` 和 `fallbacks`，转发前会删掉。

| 字段 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- |
| `model` | string | 是 | 模型 ID，格式为 "vendor/model"。必须是登记在本端点上的图像模型。 |
| `prompt` | string | 是 | 对编辑内容的文字描述。原样转发。 |
| `image` | file[] | 是 | 输入图像，以 `image[]` 文件部件发送（也可以是单个 `image` 部件）。原样转发。 |
| `stream` | boolean | 否 | 为 true 时，以 image_edit 事件组成的 Server-Sent Events 流返回。 默认值: `false`. |

## 响应

`200` `application/json`, `text/event-stream`

编辑后的图像，结构由 OpenAI Images 协议定义；响应体来自上游，原样透传。

## 错误

| 状态码 | 错误码 | 说明 |
| --- | --- | --- |
| `400` | `invalid_request` | 改图请求不完整：没有 `model` 部件（`invalid_request`，`model is required`），或者上游需要的部件缺失或格式不对（没有图像文件部件、没有 `prompt`）。后一种在生成任何图像之前就被拒绝，消息会点名是哪个部件（例如 `failed to parse request: at least one image is required`）。不计费。 |
| `401` | `invalid_api_key`, `api_key_not_found`, `api_key_revoked`, `api_key_expired` | API key 缺失或格式错误（`invalid_api_key`）、不存在（`api_key_not_found`）、已吊销（`api_key_revoked`）或已过期（`api_key_expired`）。 |
| `402` | `insufficient_balance` | 钱包余额不足以支付这次调用（`insufficient_balance`）。 |
| `404` | `model_not_found` | 请求的模型 ID 不存在或未启用（`model_not_found`），或者当前没有 provider 提供它。 |
| `413` |  | 请求体超过了本端点接受的上限（改图为 32 MiB）。在准入之前就被拒绝：不转发，也不计费。错误类型是 `invalid_request_error`，不带错误码。 |
| `422` | `validation_failed` | 该模型不在改图端点提供（`validation_failed`）：消息会列出它提供的端点。不转发，不计费。 |
| `429` | `rate_limited` | 请求因限流被拒绝（`rate_limited`）。 |
| `500` | `internal_error` | 其他失败：没有健康的上游可以处理请求时返回 `provider_unavailable`（503）；平台侧出现意外故障时返回 `internal_error`（500）。 |
| `503` | `provider_unavailable` | 其他失败：没有健康的上游可以处理请求时返回 `provider_unavailable`（503）；平台侧出现意外故障时返回 `internal_error`（500）。 |

错误体

```json
{
  "error": {
    "message": "No such model. Model names are vendor-qualified (vendor/model), so a name copied from a provider's own docs needs its vendor prefix added. Call GET /v1/models for the slugs this gateway serves.",
    "type": "not_found_error",
    "code": "model_not_found"
  }
}
```

## 响应头

每个响应头后面列出它会出现在哪些状态码的响应里。

| 响应头 | 出现于 | 说明 |
| --- | --- | --- |
| `X-Pi-Trace-Id` | `200`, `400`, `401`, `402`, `404`, `413`, `422`, `429`, `default` | 平台请求 id，由网关生成。推理端点的每个响应都带；反馈问题时请附上它。 |
| `Access-Control-Allow-Origin` | `200`, `400`, `401`, `402`, `404`, `413`, `422`, `429`, `default` | 恒为 `*`。凭证是携带 API key 的请求头，从不是 cookie。 |
| `Access-Control-Expose-Headers` | `200`, `400`, `401`, `402`, `404`, `413`, `422`, `429`, `default` | 列出每一个 `X-Pi-*` 响应头，外加 `Retry-After` 与 `Content-Encoding`，让浏览器里的代码能读到它们。 |
| `X-Pi-Provider` | `200`, `default` | 处理这次响应的 provider，取值与 `provider.order`、`GET /v1/models/{vendor}/{model}/endpoints` 同一套词表。只有上游 provider 作出应答时才有（一次补全或一次上游错误）；发生 fallback 后是接手的那个 provider。 |
| `X-Pi-Model` | `200`, `default` | 处理这次响应的模型 slug，与响应体里的 `model` 是同一个值。由 fallback 模型接手时就是那个模型。只有上游 provider 作出应答时才有。 |
| `X-Pi-Upstream-Model` | `200`, `default` | 上游 provider 在它自己的响应里报告的完整模型 id，原样透传（流式时取自第一个数据事件）。上游响应没有给出模型时不带这个头；不会用平台自己的记录补上。 |
| `X-Pi-Upstream-Request-Id` | `200`, `default` | 上游 provider 自己的请求 id，原样透传，用于在 provider 一侧查这次调用。provider 没有返回时不带这个头。 |

## 请求示例

cURL

```bash
curl https://api.pirouter.ai/v1/images/edits \
  -H "Authorization: Bearer $PIROUTER_API_KEY" \
  -F model=openai/gpt-image-2 \
  -F "prompt=Make the kite blue" \
  -F "image[]=@kite.png"
```

Python

```python
import os
import requests

response = requests.post(
    "https://api.pirouter.ai/v1/images/edits",
    headers={"Authorization": f"Bearer {os.environ['PIROUTER_API_KEY']}"},
    data={
        "model": "openai/gpt-image-2",
        "prompt": "Make the kite blue",
    },
    files=[("image[]", open("kite.png", "rb"))],
)
print(response.json())
```

TypeScript

```ts
import { openAsBlob } from 'node:fs'

const form = new FormData()
form.append('model', 'openai/gpt-image-2')
form.append('prompt', 'Make the kite blue')
form.append('image[]', await openAsBlob('kite.png'), 'kite.png')

const response = await fetch('https://api.pirouter.ai/v1/images/edits', {
  method: 'POST',
  headers: { Authorization: `Bearer ${process.env.PIROUTER_API_KEY}` },
  body: form,
})
console.log(await response.json())
```

## 响应示例

`200` `application/json`

```json
{
  "created": 1791795980,
  "data": [
    {
      "b64_json": "iVBORw0KGgoAAAANSUhEUg..."
    }
  ]
}
```

`200` `text/event-stream`

```text
event: image_edit.partial_image
data: {"type":"image_edit.partial_image","partial_image_index":0,"b64_json":"iVBORw0KGgoAAAANSUhEUg...","size":"1024x1024","quality":"low","output_format":"png"}

event: image_edit.completed
data: {"type":"image_edit.completed","b64_json":"iVBORw0KGgoAAAANSUhEUg...","size":"1024x1024","quality":"low","output_format":"png","usage":{"input_tokens":282,"input_tokens_details":{"image_tokens":256,"text_tokens":26},"output_tokens":273,"output_tokens_details":{"image_tokens":273},"total_tokens":555}}

data: [DONE]
```
