Короткий ответ: POST /v1/images/edits, модель и формат результата
Чтобы изменить готовое изображение через OpenAI API, отправьте POST /v1/images/edits — в SDK это client.images.edit(). В запросе передаются исходный файл, текст задания (промпт) и модель. Ответ приходит в JSON, сама картинка — строка base64 в поле data[0].b64_json; её нужно декодировать и записать в файл.
По состоянию на 25 сентября 2026 года OpenAI советует для новых интеграций модели GPT Image 2.5 (руководство по генерации и правке изображений):
gpt-image-2.5-sunburst— по описанию OpenAI, для задач, где важнее всего точность правки;gpt-image-2.5-flare— самая быстрая модель семейства для повседневной работы; эндпоинт правки она тоже поддерживает;gpt-image-2— предыдущая модель. Правку она по-прежнему принимает, и в работающей интеграции её разумно оставить, если изображения обрабатываются через Batch API: у моделей 2.5 Batch нет.
Из старых примеров не переносите три вещи:
model="dall-e-2"и вызовimages/variations. DALL·E 2 удалена из API, а variations работал только с ней;response_format. Для моделей GPT Image результат всегда приходит вb64_json, ссылкаurlне поддерживается;input_fidelity. Дляgpt-image-2параметр не передают — высокая точность обработки исходников у неё включена всегда; поддерживают ли его модели 2.5, в документации не сказано.
Условие доступа стоит проверить до отладки кода. России нет в списке стран, где работает OpenAI API, и OpenAI предупреждает, что использование сервиса за пределами списка может привести к блокировке аккаунта. Украина (с отдельными исключениями) и Казахстан в списке есть. Это условие договора, а не техническая ошибка: ни один параметр запроса его не меняет. Кроме того, перед работой с моделями GPT Image OpenAI может потребовать верификацию организации в консоли.
Какую модель указать в model
| Ситуация | Что указать | Почему |
|---|---|---|
| Новый проект; важно точно заменить объект и не испортить лицо, товар или композицию | gpt-image-2.5-sunburst | OpenAI рекомендует Sunburst для правки, где точность важнее всего; на этой модели построены и официальные примеры с маской |
| Новый проект; важнее скорость ответа и поток простых правок | gpt-image-2.5-flare | По описанию OpenAI, самая быстрая из моделей 2.5; /v1/images/edits поддерживает |
Интеграция на gpt-image-2, правки идут через Batch API | оставить gpt-image-2 | Batch есть только у неё, а тарифы Batch ровно вдвое ниже стандартных |
Интеграция на gpt-image-2, Batch не нужен | сравнить с моделями 2.5 на своих изображениях | Модель не удалена, но OpenAI перенесла её в раздел «Earlier GPT Image models» и для новых проектов советует 2.5 |
В коде стоит dall-e-2 | одна из моделей выше | DALL·E 2 удалена из API и намеренно отклоняется |
Разделение «Sunburst для правки, Flare для скорости» — позиционирование OpenAI, а не граница возможностей: страницы обеих моделей отмечают поддержку v1/images/edits. Если качество Flare на ваших примерах устраивает, запрет на её использование для правки нигде не описан. Для воспроизводимости можно указать снимок с датой: gpt-image-2.5-sunburst-2026-09-08, gpt-image-2.5-flare-2026-09-08 или gpt-image-2-2026-04-21.
Стандартные ставки у всех трёх моделей одинаковые (страница цен): $8 за 1 млн входных токенов изображения, $2 за кэшированные, $30 за 1 млн выходных токенов изображения, $5 и $1,25 за входные и кэшированные текстовые токены. В Batch у gpt-image-2 — $4, $1 и $15 соответственно. Одинаковая ставка не означает одинаковую цену картинки: число токенов зависит от модели и параметра quality, а калькулятор GPT Image 2 расход моделей 2.5 не оценивает. Надёжнее всего смотреть поле usage в ответах на своих реальных запросах. Подробный выбор между Flare и Sunburst и расчёт стоимости — в статье GPT Image 2.5: Flare или Sunburst, стоимость и работа с API, а оплата входных изображений и Batch для gpt-image-2 — в материале Сколько стоит изображение в GPT Image 2 API.
Минимальный запрос через cURL
Начните с одного файла без маски и дополнительных настроек: сначала нужно убедиться, что простая правка проходит. В примере room.png — фотография гостиной, где над диваном висит картина с горным пейзажем.
curl --fail-with-body -sS https://api.openai.com/v1/images/edits \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-D edit-headers.txt \
-F "model=gpt-image-2.5-sunburst" \
-F "image[]=@room.png;type=image/png" \
-F 'prompt=Замените картину с горным пейзажем над диваном на абстрактную картину в тёплых тонах в той же раме. Мебель, растения, свет, тени и ракурс оставьте как есть.' \
-F "output_format=png" \
-o edit-response.json
jq -r '.data[0].b64_json' edit-response.json | base64 --decode > room-v1.pngЧто здесь важно:
image[]=@room.pngзагружает сами байты файла в телеmultipart/form-data. Квадратные скобки — как в примерах OpenAI; для нескольких исходников повторите строку, всего до 16 файлов PNG, WebP или JPG размером меньше 50 MB каждый.- Заголовок
Content-Typeвручную не задавайте: cURL сам выставитmultipart/form-dataс нужной границей частей. -D edit-headers.txtсохраняет заголовки ответа, среди нихx-request-id— идентификатор запроса (request ID), который понадобится при обращении в поддержку.--fail-with-bodyзаставляет cURL завершиться с ошибкой при HTTP 4xx/5xx, но тело ответа с объектомerrorвсё равно окажется вedit-response.json.
Успешный результат — файл room-v1.png, который открывается как изображение (команда file room-v1.png покажет PNG image data). Если jq выводит null, картинки в ответе нет: откройте edit-response.json и прочитайте поле error. Переименовать JSON в .png недостаточно — изображением становится только декодированная строка base64.
В справочнике эндпоинта описан и JSON-вариант тела: массив images, где каждый элемент — {"image_url": ...} с полным URL или base64 data URL либо {"file_id": ...} для файла, загруженного через Files API. Строка с локальным путём вроде "room.png" в JSON ничего не загружает. Для файлов с диска надёжнее multipart, как выше.
Этот и следующие примеры кода составлены по справочнику и руководству OpenAI; перед выкладкой в продакшен прогоните их со своим ключом и своими файлами.
Python и Node.js: та же правка из кода
В Python SDK файл удобно открыть контекстным менеджером: он останется открытым на время запроса и закроется после него. Расширение выходного файла берите из output_format, иначе JPEG-байты окажутся в файле .png.
import base64
from pathlib import Path
from openai import OpenAI
client = OpenAI() # ключ читается из переменной окружения OPENAI_API_KEY
output_format = "png"
with open("room.png", "rb") as source:
result = client.images.edit(
model="gpt-image-2.5-sunburst",
image=source,
prompt=(
"Замените картину с горным пейзажем над диваном на абстрактную "
"картину в тёплых тонах в той же раме. Мебель, растения, свет, "
"тени и ракурс оставьте как есть."
),
output_format=output_format,
)
images = result.data or []
if not images or not images[0].b64_json:
raise RuntimeError("В ответе нет изображения b64_json")
for index, item in enumerate(images, start=1):
suffix = "" if len(images) == 1 else f"-{index}"
Path(f"room-v1{suffix}.{output_format}").write_bytes(
base64.b64decode(item.b64_json)
)Если исходник уже в памяти — пришёл из формы загрузки или из другого сервиса, — сохранять его на диск не нужно. Python SDK принимает bytes, путь PathLike или кортеж «имя, содержимое, MIME-тип»:
image=("room.png", image_bytes, "image/png")Имя и MIME-тип должны соответствовать содержимому: расширение .png не превращает JPEG-байты в PNG.
В Node.js SDK принимает File, Response, поток fs.ReadStream или объект, созданный помощником toFile. Пример ниже — модуль ES (файл .mjs), поэтому await можно писать на верхнем уровне:
import fs from "node:fs";
import OpenAI, { toFile } from "openai";
const client = new OpenAI();
const result = await client.images.edit({
model: "gpt-image-2.5-sunburst",
image: await toFile(fs.createReadStream("room.png"), null, {
type: "image/png",
}),
prompt:
"Замените картину с горным пейзажем над диваном на абстрактную картину " +
"в тёплых тонах в той же раме. Мебель, растения, свет, тени и ракурс оставьте как есть.",
output_format: "png",
});
const b64 = result.data?.[0]?.b64_json;
if (!b64) throw new Error("В ответе нет изображения b64_json");
fs.writeFileSync("room-v1.png", Buffer.from(b64, "base64"));Промпт работает лучше, когда в нём названы и изменение, и то, что нужно сохранить. «Замени картину» оставляет модели свободу перерисовать свет или мебель; «замени картину, мебель и ракурс оставь как есть» эту свободу сужает, хотя и не гарантирует неизменность каждого пикселя.
Параметры правки и input_fidelity: что передавать и что убрать
| Параметр | Как использовать |
|---|---|
model | Задавайте явно: gpt-image-2.5-sunburst, gpt-image-2.5-flare или gpt-image-2 |
image | Файл PNG, WebP или JPG меньше 50 MB; до 16 изображений |
prompt | Обязателен и не может быть пустым; до 32 000 символов |
mask | Необязательный PNG с альфа-каналом, подробнее ниже |
n | Количество результатов, от 1 до 10; сохраняйте каждый элемент data |
quality | low, medium, high, auto; у моделей 2.5 ещё xhigh и max. По умолчанию auto |
size | 1024x1024, 1536x1024, 1024x1536 или свой ШИРИНАxВЫСОТА: стороны кратны 16, соотношение от 1:3 до 3:1, всего от 655 360 до 8 294 400 пикселей, максимум 3840x2160; выше 2560x1440 — экспериментальный режим |
output_format | png (по умолчанию), jpeg или webp; JPEG, по данным OpenAI, отдаётся быстрее PNG |
output_compression | 0–100, только для jpeg и webp |
background | transparent, opaque или auto; у моделей 2.5 прозрачный фон поддерживается, у gpt-image-2 он в статусе preview; для прозрачности нужен png или webp |
moderation | auto (по умолчанию) или low — менее строгая фильтрация |
response_format | Не передавать: относится только к DALL·E |
input_fidelity | Не передавать: для gpt-image-2 параметр не действует, для 2.5 его поддержка не описана |
Легче всего спутать response_format и output_format. Первый у DALL·E определял, придёт ссылка или base64; второй задаёт формат байтов самой картинки. Если нужен PNG в base64, достаточно output_format="png": b64_json придёт и так.
Нестандартные размеры вроде 1024x576 отклоняются из-за минимальной площади — как подобрать допустимый size, разобрано в статье почему size 1024x576 недопустим. Проверка прозрачного фона на результате — в материале GPT Image 2: прозрачные PNG и WebP через API.
Маска: как указать область правки
Маска подсказывает модели, какую часть первого изображения нужно изменить. Требования справочника и руководства вместе дают такой список:
- маска — PNG с альфа-каналом; полностью прозрачные пиксели (alpha = 0) обозначают зону правки;
- ширина и высота маски совпадают с исходником;
- файл маски меньше 4 MB. В руководстве встречается более мягкий предел в 50 MB, в справочнике — 4 MB; укладываясь в 4 MB, вы соблюдаете оба;
- если исходников несколько, маска применяется к первому.

Белый прямоугольник на непрозрачной картинке маской не считается: нужна именно прозрачность. Если у вас чёрно-белая маска, альфа-канал можно добавить в Pillow — так делает пример из руководства OpenAI:
from PIL import Image
mask = Image.open("mask-bw.png").convert("L")
mask_rgba = mask.convert("RGBA")
mask_rgba.putalpha(mask) # чёрные пиксели станут прозрачными — это зона правки
mask_rgba.save("mask.png")После такого преобразования чёрные участки получают alpha = 0, белые остаются непрозрачными. Значит, закрашивать чёрным нужно то место, которое вы хотите изменить.
От обычного запроса этот отличается параметром mask и промптом:
with open("room.png", "rb") as source, open("mask.png", "rb") as mask:
result = client.images.edit(
model="gpt-image-2.5-sunburst",
image=source,
mask=mask,
prompt=(
"Та же гостиная, над диваном — абстрактная картина в тёплых тонах "
"в той же раме. Мебель, растения, свет и ракурс без изменений."
),
output_format="png",
)В cURL добавьте строку -F "mask=@mask.png". Промпт при этом описывает весь итоговый кадр, а не только содержимое прозрачной области — так сделан и пример OpenAI.
Главное ограничение: у моделей GPT Image маска — ориентир, а не трафарет. Руководство прямо говорит, что правка с маской целиком опирается на промпт и модель может не повторить форму маски точно. Неизменность пикселей за пределами маски не гарантирована, поэтому после каждой правки проверяйте то, что для вас критично: лицо, товар, логотип, текст. Если снаружи зоны правки не должен сдвинуться ни один пиксель, накладывайте результат на оригинал по той же маске уже у себя — например, через Image.composite в Pillow.
Следующая правка и когда нужен Responses API
Images API ничего не помнит между вызовами. Чтобы сделать вторую правку поверх первой, отправьте в images.edit() уже принятый результат: например, room-v1.png с промптом «сделайте раму картины тоньше». Если снова отправить исходный room.png, замена картины в новый результат не попадёт.
Удобно хранить оригинал отдельно, а удачные версии нумеровать — room-v1.png, room-v2.png — и записывать рядом промпт, модель и родительскую версию. Тогда неудачную правку можно отбросить и вернуться к предыдущему файлу, не повторяя всю цепочку.
Для одной правки по одному промпту OpenAI прямо рекомендует Images API. Responses API нужен, когда правка идёт в диалоге: пользователь пишет «теперь рама тоньше», «верни прежний фон», а модель помнит контекст через previous_response_id. Структура запроса там другая: в верхнем model стоит основная модель (в примерах OpenAI — gpt-6-astra), а модель изображений указывается внутри инструмента image_generation:
import base64
from pathlib import Path
from openai import OpenAI
client = OpenAI()
with open("room-v1.png", "rb") as f:
file_id = client.files.create(file=f, purpose="vision").id
response = client.responses.create(
model="gpt-6-astra",
input=[{
"role": "user",
"content": [
{"type": "input_text", "text": "Сделайте раму картины тоньше, остальное не меняйте"},
{"type": "input_image", "file_id": file_id},
],
}],
tools=[{"type": "image_generation", "model": "gpt-image-2.5-sunburst"}],
)
results = [o.result for o in response.output if o.type == "image_generation_call"]
if results:
Path("room-v2.png").write_bytes(base64.b64decode(results[0]))Модель изображений в верхний model Responses не ставьте: на страницах моделей GPT Image эндпоинт v1/responses помечен как неподдерживаемый. Маска в Responses передаётся как input_image_mask: {"file_id": ...} внутри инструмента. К стоимости картинки здесь добавляются токены основной модели.
Переход с images/variations на edits
Эндпоинт images/variations работал только с dall-e-2: он брал квадратный PNG и возвращал вариант изображения без всякого промпта. После удаления DALL·E 2 для него не осталось ни одной модели. То, что в общем типе ImageModel перечислены модели GPT Image, а в SDK остались методы create_variation() и createVariation(), совместимым эндпоинт не делает.
| В старом коде | Что поставить вместо |
|---|---|
client.images.create_variation() (Python) | client.images.edit() |
client.images.createVariation() (Node.js) | client.images.edit() |
POST /v1/images/variations | POST /v1/images/edits |
model="dall-e-2" | gpt-image-2.5-sunburst, gpt-image-2.5-flare или gpt-image-2 |
| только исходная картинка | картинка и непустой промпт: что изменить, что сохранить |
response_format="url" | убрать; сохранить b64_json в файл |
Точной замены старому «варианту без промпта» нет: edits всегда требует задания. Чтобы получить похожий эффект, опишите, насколько далеко можно уйти от оригинала. Например: «Другой вариант этой гостиной: смените палитру подушек и картины, мебель, планировку и ракурс сохраните». Параметр n вернёт несколько результатов по одному промпту, но это всё равно правка по заданию, а не прежние variations. Если исходной картинки нет вовсе, нужна генерация через images.generate().
Кнопку «Ещё вариант» в интерфейсе можно оставить: пусть пользователь выбирает, что менять — фон, свет, стиль, — а приложение собирает из выбора промпт.
Ошибки: по каким полям искать причину
Смотрите не только на HTTP-статус, но и на тело ответа: error.message, error.type, error.param, error.code, а также заголовок x-request-id. По рекомендациям OpenAI временные ошибки — лимит запросов (429) и сбои сервера (5xx) — повторяют с нарастающей паузой, а ошибки квоты и ошибки в самом запросе автоматически не повторяют: сначала нужно изменить запрос.
| Что видно | Что это значит | Что делать |
|---|---|---|
Invalid value: 'gpt-image-2'. Value must be 'dall-e-2'., param: "model" | Так выглядел сбой проверки модели на стороне OpenAI весной и летом 2026 года (разбор ниже) | Сравнить минимальный cURL и приложение на одном и том же адресе API |
The model 'dall-e-2' does not exist, code: "model_not_found" | DALL·E 2 удалена | Сменить модель на GPT Image |
Запрос уходит на /images/variations | Старый метод в обёртке или на сервере | Перейти на edits с промптом |
error.type = "image_generation_user_error" | Ошибка, которую исправляет сам пользователь | Разбирать по error.code, не повторять без изменений |
error.code = "moderation_blocked" | Запрос или результат заблокировала модерация; moderation_details.moderation_stage покажет input, output или unknown | При input менять промпт или исходник, при output — промпт; пользователю показывать общее сообщение |
param указывает на image или mask | Формат, размер файла, размеры маски, нет альфа-канала | Проверить по требованиям выше |
Ошибка про size | Размер вне допустимых границ | Подобрать size по таблице параметров |
Неизвестный параметр, например style | Поле из кода DALL·E | Убрать; см. как исправить Unknown parameter: 'style' |
| Требование верифицировать организацию | Для моделей GPT Image нужна верификация | См. что делать с ошибкой Your organization must be verified |
| 429 | Превышен лимит; на Tier 1 у всех трёх моделей — 5 изображений в минуту | Повторить с паузой, растущей от попытки к попытке |
| Клиент обрывает соединение по тайм-ауту | Сложные запросы, по данным OpenAI, обрабатываются до 2 минут | Увеличить тайм-аут HTTP-клиента |
В Python SDK поля ошибки доступны прямо в исключении. Фрагмент ниже сделан по образцу из руководства OpenAI; client, source и prompt — из примеров выше:
import openai
try:
result = client.images.edit(model="gpt-image-2.5-sunburst", image=source, prompt=prompt)
except openai.BadRequestError as error:
body = error.body if isinstance(error.body, dict) else {}
print("code:", error.code, "param:", body.get("param"))
print("request id:", getattr(error, "request_id", None))
if error.code == "moderation_blocked":
print("stage:", (body.get("moderation_details") or {}).get("moderation_stage"))
raiseЧто означала ошибка Value must be 'dall-e-2'
27 апреля 2026 года в issue #1844 репозитория openai-node описали, что images.edit с gpt-image-2 возвращает HTTP 400:
{
"error": {
"message": "Invalid value: 'gpt-image-2'. Value must be 'dall-e-2'.",
"type": "invalid_request_error",
"param": "model",
"code": null
}
}Та же ошибка воспроизводилась и через SDK 6.34.0, и чистым cURL, а запрос с dall-e-2 при этом отвечал The model 'dall-e-2' does not exist. Поле code в этом ответе было null. 13 августа 2026 года issue закрыли с пояснением: проверку исправили на стороне сервиса, модели GPT Image эндпоинт правки принимает, а DALL·E отклоняется намеренно.
Это история конкретного сбоя, а не гарантия, что сегодня любой запрос к любому адресу пройдёт. Если вы видите это сообщение сейчас, выясните, где оно возникает: в приложении или уже в минимальном запросе.

| Результат сравнения | Где искать причину |
|---|---|
| Минимальный cURL проходит, приложение получает ошибку | Отличия тела: лишние поля обёртки, другой base_url, файл превратился в строку или пустой буфер |
| Оба запроса на один адрес получают одну и ту же ошибку модели | Проверка параметров у этого провайдера или недоступность модели для вашего ключа; передайте в поддержку воспроизводимый пример |
| В приложении запрос даже не уходит | Локальная проверка схемы, типы и версия SDK |
| Картинка пришла, но изменилось лишнее | Промпт и маска, а не параметры запроса |
Сравнение работает, только если запросы отличаются одним фактором. Когда одновременно меняются файл, модель, размер и адрес, результат ничего не доказывает. Возвращайте поля приложения по одному, пока ошибка не появится снова.
Для поддержки соберите: время запроса с часовым поясом, URL, HTTP-статус, error.message, error.type, error.param, error.code, x-request-id, версию SDK, список переданных параметров и для файла — имя, MIME-тип и размер. Ключи, токены и приватные изображения в публичный отчёт не включайте.
Если запрос идёт не напрямую в OpenAI
Код из примеров рассчитан на https://api.openai.com/v1 и ключ OpenAI. OpenAI-совместимый сервис, который поддерживает этот эндпоинт, принимает тот же запрос на своём адресе: в SDK меняется base_url (в Node.js — baseURL), в cURL — URL. Минимальную проверку в этом случае делайте на фактическом адресе провайдера и с его ключом; ключ одного сервиса другому не отправляйте. То, что провайдер принимает модель, ещё не значит, что он передаёт все параметры правки, лимиты и условия OpenAI.
Один из таких сервисов — laozhang.ai. По его документации по GPT Image 2.5 правка идёт через тот же /v1/images/edits с моделями gpt-image-2.5-flare и gpt-image-2.5-sunburst. В группе Sora2Official оплата по токенам по тем же ставкам, что у OpenAI, запросы уходят к смеси ключей Azure OpenAI и OpenAI; корпоративная группа работает только на ключах OpenAI и стоит на 20 % дороже. Есть и оплата за каждый вызов — модели с суффиксом -vip по $0,03 за запрос; с 16 по 22 сентября 2026 года они не работали, затем их вернули. Поддерживаемые страны, способы оплаты, SLA и работа масок на VIP-моделях в этой документации не описаны — уточните их, прежде чем переводить туда рабочие запросы.
Если нужна правка без кода, в интерфейсе Codex, — в материале GPT Image 2 в Codex: создать, отредактировать и сохранить изображение.
Частые вопросы
Можно ли редактировать изображение без маски?
Да, маска необязательна. Без неё модель меняет то, что описано в промпте, и сама решает, какие области затронуть. Маска полезна, когда правка должна касаться конкретного места, но и с ней сохранность остальной картинки нужно проверять.
Умеет ли gpt-image-2.5-flare редактировать?
Да. У Flare и Sunburst одинаковая поддержка v1/images/edits. OpenAI советует Sunburst для точной правки, а Flare — там, где важнее скорость; какую выбрать, покажет сравнение на ваших изображениях и расход токенов в usage.
Сколько стоит одна правка изображения?
Фиксированной цены за правку у OpenAI нет: оплачиваются входные токены (текст промпта и исходные изображения) и выходные токены картинки. Ставки у всех трёх моделей одинаковые, но расход токенов разный, поэтому реальную цену показывает поле usage в ответе. Расчёт по шагам — в статье GPT Image 2.5: Flare или Sunburst, стоимость и работа с API.
Можно ли передать в images.edit несколько изображений?
Да, до 16 файлов в одном запросе — например, фотографию комнаты и снимок предмета, который нужно в неё поставить. В промпте укажите, какое изображение основное и что взять из остальных. Маска, если она есть, относится к первому файлу.
Работает ли OpenAI API из России?
России нет в списке поддерживаемых стран, и OpenAI предупреждает о возможной блокировке аккаунтов, используемых вне этого списка. Украина (с исключениями) и Казахстан в списке есть. Никакой параметр запроса это условие не меняет.
![Три слоя над подсвеченной платформой — исходник image[], маска с прозрачной областью alpha = 0 и результат gpt-image-2.5-sunburst; рядом заголовок OpenAI Image Editing API и POST /v1/images/edits](/posts/ru/openai-image-editing-api/img/cover.webp)


