Редактирование изображений
Редактируйте изображения GPT Image через OpenAI-совместимый API BetterToken: multipart/form-data, маски, сохранение, расчёт стоимости и обработка ошибок.
POST /v1/images/edits
Endpoint для редактирования изображений использует тело запроса multipart/form-data. Загрузите одно или несколько референсных изображений и передайте prompt и параметры как поля формы. Приём Model ID или параметра не гарантирует доступность модели, размера или операции редактирования для запроса.
Рекомендуемые значения
Это начальные рекомендации, а не фиксированные требования. Рекомендуется n=1; endpoint принимает целое число от 1 до 10, но может вернуть меньше изображений.
model=gpt-image-2n=1response_format=b64_jsonoutput_format=pngДля одного референсного изображения используйте image. Для нескольких референсных изображений используйте повторяющиеся поля image[].
Используйте gpt-image-2, gpt-image-2.5-flare или gpt-image-2.5-sunburst, если модель доступна для запроса. quality принимает auto, low, medium, high для gpt-image-2; две модели 2.5 также принимают xhigh и max. Приём параметра сам по себе не гарантирует доступность модели или операции редактирования. В multipart quality должна быть непустой строкой, а size не может быть пустым.
Одно референсное изображение
Подготовьте reference.png и замените YOUR_API_KEY своим BetterToken API Key. Один ключ подходит для всех доступных моделей. Не задавайте Content-Type вручную: curl -F или Python files должны сформировать boundary.
| Поле формы | Пример |
|---|---|
model | gpt-image-2 |
prompt | Using this reference image, create a more polished square product key visual while preserving the subject style. |
n | 1 |
size | 1024x1024 |
response_format | b64_json |
output_format | png |
image | Выберите файл reference.png |
curl --max-time 300 'https://bettertoken.ai/v1/images/edits' \ -H 'Authorization: Bearer YOUR_API_KEY' \ -F 'model=gpt-image-2' \ -F 'prompt=Using this reference image, create a more polished square product key visual while preserving the subject style.' \ -F 'n=1' \ -F 'size=1024x1024' \ -F 'response_format=b64_json' \ -F 'output_format=png' \ -F 'image=@reference.png'Несколько референсных изображений
Отправьте следующие поля:
| Поле формы | Пример |
|---|---|
model | gpt-image-2 |
prompt | Combine these reference images into a promotional poster with a consistent style. |
n | 1 |
size | 1024x1024 |
response_format | b64_json |
output_format | png |
image[] | Выберите reference-1.png |
image[] | Выберите reference-2.jpg |
image[] | Выберите reference-3.png |
curl --max-time 300 'https://bettertoken.ai/v1/images/edits' \ -H 'Authorization: Bearer YOUR_API_KEY' \ -F 'model=gpt-image-2' \ -F 'prompt=Combine these reference images into a promotional poster with a consistent style.' \ -F 'n=1' \ -F 'size=1024x1024' \ -F 'response_format=b64_json' \ -F 'output_format=png' \ -F 'image[]=@reference-1.png' \ -F 'image[]=@reference-2.jpg' \ -F 'image[]=@reference-3.png'Локальное редактирование по маске
Добавьте необязательное файловое поле mask, если нужно направить редактирование на выбранную область. Текущий endpoint BetterToken читает один файл mask. Не отправляйте повторяющиеся поля mask[].
- Исходное изображение и mask должны иметь одинаковый формат и размеры в пикселях. Размер каждого файла должен быть меньше 50 MB. Для максимальной совместимости рекомендуется использовать PNG с Alpha-каналом для исходного изображения и mask.
- Mask должна содержать Alpha-канал. Полностью прозрачные пиксели (
Alpha 0) отмечают область, которую модель может перерисовать. Полностью непрозрачные пиксели (Alpha 255) направляют модель на сохранение области. - Обычное чёрно-белое RGB-изображение не является корректной mask, если в файле нет Alpha-канала.
- Если вы передаёте несколько полей
image[], mask применяется к первому входному изображению. - Mask служит ориентиром для модели и не гарантирует, что пиксели за пределами прозрачной области останутся абсолютно неизменными.
- Доступность mask зависит от сочетания модели и размера. Для обычного редактирования по референсному изображению mask не нужна. В тестах GPT Image 2 и Flare заметно менялись и области вне маски; точность локального управления Sunburst не проверялась. Проверяйте результат перед использованием для товаров, логотипов и точной ретуши.
curl --max-time 300 'https://bettertoken.ai/v1/images/edits' \ -H 'Authorization: Bearer YOUR_API_KEY' \ -F 'model=gpt-image-2' \ -F 'prompt=Replace only the masked area with a blue circle. Preserve everything else.' \ -F 'n=1' \ -F 'size=1024x1024' \ -F 'response_format=b64_json' \ -F 'output_format=png' \ -F 'image=@original.png' \ -F 'mask=@mask.png'Редактирование по маске на Python
Откройте оба файла в одном операторе with, чтобы Python закрыл их после запроса:
import requests
url = "https://bettertoken.ai/v1/images/edits"headers = {"Authorization": "Bearer YOUR_API_KEY"}data = { "model": "gpt-image-2", "prompt": "Replace only the masked area with a blue circle. Preserve everything else.", "n": "1", "size": "1024x1024", "response_format": "b64_json", "output_format": "png",}
with open("original.png", "rb") as image_file, open("mask.png", "rb") as mask_file: files = { "image": ("original.png", image_file, "image/png"), "mask": ("mask.png", mask_file, "image/png"), } response = requests.post(url, headers=headers, data=data, files=files, timeout=300)
response.raise_for_status()result = response.json()Проверка файлов перед загрузкой
Этот минимальный пример с Pillow проверяет обязательные требования к формату, размерам и Alpha-каналу. Для обычной локальной mask он также выводит предупреждение, если Alpha-канал не содержит одновременно полностью прозрачные и полностью непрозрачные области:
from pathlib import Pathfrom PIL import Image
image_path = Path("original.png")mask_path = Path("mask.png")
if image_path.stat().st_size >= 50 * 1024 * 1024 or mask_path.stat().st_size >= 50 * 1024 * 1024: raise ValueError("Each file must be smaller than 50 MB")
with Image.open(image_path) as image, Image.open(mask_path) as mask: if image.format != mask.format: raise ValueError("The image and mask formats must match") if image.size != mask.size: raise ValueError("The image and mask dimensions must match") if "A" not in mask.getbands(): raise ValueError("The mask must contain an Alpha channel") alpha_min, alpha_max = mask.getchannel("A").getextrema() if alpha_min > 0 or alpha_max < 255: print("Warning: for a typical local mask, include fully transparent and fully opaque areas")Пример на Python
import base64from pathlib import Path
import requests
url = "https://bettertoken.ai/v1/images/edits"headers = { "Authorization": "Bearer YOUR_API_KEY",}
data = { "model": "gpt-image-2", "prompt": "Using this reference image, create a more polished square product key visual while preserving the subject style.", "n": "1", "size": "1024x1024", "response_format": "b64_json", "output_format": "png",}
with open("reference.png", "rb") as image_file: files = { "image": ("reference.png", image_file, "image/png"), } response = requests.post(url, headers=headers, data=data, files=files, timeout=300)
response.raise_for_status()result = response.json()for index, item in enumerate(result["data"], start=1): encoded = item["b64_json"] if "," in encoded and "base64" in encoded.split(",", 1)[0]: encoded = encoded.split(",", 1)[1] image_bytes = base64.b64decode(encoded) if image_bytes.startswith(b"\x89PNG\r\n\x1a\n"): extension = "png" elif image_bytes.startswith(b"\xff\xd8\xff"): extension = "jpg" elif image_bytes.startswith(b"RIFF") and image_bytes[8:12] == b"WEBP": extension = "webp" else: raise ValueError("Unsupported image signature") Path(f"output-{index}.{extension}").write_bytes(image_bytes)Рекомендуемые размеры
size | Соотношение | Сценарий |
|---|---|---|
auto | Auto | Автоматический выбор размера |
1024x1024 | 1:1 | Квадратные изображения, аватары, обложки, assets |
1536x1024 | 3:2 | Landscape-постеры, баннеры, сцены |
1024x1536 | 2:3 | Portrait-обложки для мобильных и постеры |
1536x1152 | 4:3 | Стандартные landscape-изображения, product images, content graphics |
1152x1536 | 3:4 | Стандартные portrait-изображения, mobile covers, vertical posters |
2048x2048 | 1:1 | High-resolution квадратные изображения |
2048x1152 | 16:9 | High-resolution landscape-изображения |
3840x2160 | 16:9 | 4K landscape-изображения |
2160x3840 | 9:16 | 4K portrait-изображения |
Таблица содержит примеры, а не закрытый список. Явные ширина и высота должны быть положительными кратными 16, каждая не больше 3840; соотношение сторон не должно превышать 3:1, а число пикселей должно быть от 655360 до 8294400. Например, 2080x2080 допустим. Фактический размер может отличаться от запрошенного, поэтому проверяйте размеры изображения и не обрезайте его принудительно.
Сохранение изображения
Успешный ответ следует OpenAI-совместимой форме image response. Суммы ниже показывают формат квитанции и не являются фиксированными ценами:
{ "created": 1710000000, "data": [ { "b64_json": "iVBORw0KGgoAAAANSUhEUgAA...(truncated)", "revised_prompt": "Optional revised prompt" } ], "bettertoken_image_billing": { "currency": "CNY", "settled_amount": "0.15", "outputs": [ { "index": 0, "width": 1254, "height": 1254, "billing_resolution": "2K", "currency": "CNY", "amount": "0.15" } ] }}Прочитайте все data[].b64_json и разрешайте дополнительные поля, например revised_prompt. BetterToken возвращает расчёт в корневом bettertoken_image_billing: суммы settled_amount и amount являются decimal strings, а outputs[].index начинается с 0. output_format — запрос формата, не гарантия кодирования; определяйте фактический формат по байтам и используйте соответствующее расширение. На странице Text to image приведены примеры сохранения PNG, JPEG и WebP без дополнительных зависимостей.
Фактическая стоимость
bettertoken_image_billing возвращается в том же ответе; отдельный запрос стоимости не нужен.
| Поле | Значение |
|---|---|
currency | Валюта расчёта; это не обязательно USD или CNY. |
settled_amount | Итоговая сумма запроса в виде десятичной строки. |
requested_resolution | Уровень разрешения, определённый по запросу. |
outputs[].index | Индекс с нуля, соответствующий изображению в data[]. |
outputs[].width, height | Фактические размеры изображения в пикселях. |
outputs[].billing_resolution | Итоговый тарифный уровень изображения. |
outputs[].currency, amount | Валюта и сумма расчёта за изображение. |
Размер запроса и тарифный уровень могут различаться: запрос 1024x1024 возвращал 1254x1254 с тарификацией 2K. Используйте сумму из квитанции; текущие цены указаны на модельной площадке. Для отображения сохраняйте суммы строками, для вычислений используйте десятичную арифметику. Отсутствующая квитанция не означает нулевую стоимость.
from decimal import Decimal
billing = result.get("bettertoken_image_billing")if billing is None: print("Billing receipt unavailable; check your usage records.")else: total = Decimal(billing["settled_amount"]) print(f"Total: {billing['currency']} {total}") for output in billing.get("outputs", []): print( f"Image {output['index'] + 1}: " f"{output['width']}x{output['height']}, " f"{output['billing_resolution']}, " f"{output['currency']} {output['amount']}" )Поток ответа
Endpoint синхронный. После отправки POST /images/edits держите HTTP-запрос открытым до ответа сервера. При успехе каждое изображение возвращается в data[].b64_json со сведениями о доставке и расчёте.
Endpoint не возвращает task_id; отдельного status query или endpoint для скачивания результата нет.
Тайм-ауты, повторные запросы и списания
Задайте timeout HTTP-клиента в несколько минут. Timeout, disconnect или неоднозначный 5xx не показывают, было ли редактирование выполнено и списана ли оплата; соединение может прерваться при записи результата после расчёта, поэтому возможны повторные списания. Не повторяйте автоматически POST редактирования. Сначала проверьте записи потребления или логи запросов; сохраните возвращённый request ID и, если получены, заголовки X-BetterToken-Image-Receipt и X-BetterToken-Billing-State. Собственный request ID клиента не обеспечивает идемпотентность сервера; повторная отправка может создать новый запрос и списание. Отдельного endpoint проверки статуса нет.
Исправьте отклонённые данные, аутентификацию или проблему квоты перед повторной отправкой. Повторяйте 429 с backoff только после подтверждения отказа запроса. Перед обработкой BetterToken может предварительно удержать максимум, равный наибольшей применимой цене за единицу, умноженной на n. Поэтому баланса, достаточного для фактически доставленного изображения 1K, может не хватить на удержание; такой отказ возвращается как 403 с error.code=insufficient_user_quota. Итоговая сумма рассчитывается по фактически доставленным изображениям. Корневой bettertoken_image_billing содержит сумму, валюту и сведения по каждому результату.
Чек-лист подключения
- Base URL:
https://bettertoken.ai/v1. - Заголовок содержит
Authorization: Bearer sk-.... - Запрос использует
multipart/form-data. - Выберите доступный для запроса Model ID:
gpt-image-2,gpt-image-2.5-flareилиgpt-image-2.5-sunburst. - Для одного референса используйте
image, для нескольких повторяйтеimage[]. - Для маски передайте один
maskтого же формата и размера, что и оригинал, с Alpha-каналом. - Используйте
response_format=b64_json.output_format— запрошенное значение; при сохранении определяйте формат по байтам. nпринимает целые числа от1до10. Рекомендуется1; изображений может быть меньше.sizeможно пропустить, задатьautoили допустимые размеры.- HTTP-клиент допускает ожидание генерации в течение нескольких минут.
- Логи и скриншоты ошибок не раскрывают API Key или полное содержимое изображения в base64.
Смежные документы
/v1/images/editsАвторизации
Используйте API Key BetterToken как bearer token. Не публикуйте API keys во frontend-коде, скриншотах, логах, тикетах или Git-репозиториях.
Тело multipart/form-data
Model ID GPT Image. Один BetterToken API Key работает со всеми доступными моделями.
"gpt-image-2"Инструкция для редактирования изображения или генерации по референсному изображению.
"На основе этого изображения создай более аккуратное квадратное главное изображение продукта, сохранив общий стиль основного объекта."Поле для одного референсного изображения. В этом случае используйте image.
Поле для нескольких референсных изображений. Повторяйте image[] для каждого файла.
Необязательный одиночный файл mask. Сервер читает только один файл mask. Формат и размеры mask должны совпадать с исходным изображением, размер каждого файла должен быть меньше 50 MB, а mask должна содержать Alpha-канал. Для максимальной совместимости рекомендуется использовать PNG с Alpha-каналом для обоих файлов. При нескольких image[] mask применяется к первому входному изображению. Доступность зависит от сочетания модели и размера. Для обычного редактирования по референсному изображению mask не нужна. Mask направляет генерацию, но не гарантирует сохранение пикселей за целевой областью; проверяйте результат.
Допустимо 1–10; начните с 1. Может быть передано меньше изображений, чем запрошено. Перебирайте data[] и используйте расчет по фактическому результату.
"1"Опустите поле или передайте auto для автоматического размера. Для WxH обе стороны должны быть положительными и кратными 16, не более 3840 каждая; соотношение сторон не более 3:1, площадь 655360–8294400 пикселей. Примеры не являются закрытым списком: 2080x2080 тоже соответствует правилам. Проверяйте фактические размеры. Допустимые параметры не гарантируют доступный сервис.
"1024x1024"["auto","1024x1024","2048x2048","2080x2080","3840x2160"]Рекомендуется фиксировать b64_json.
"b64_json"Запрошенный формат; рекомендуется png, но перекодирование ответа в PNG не гарантируется. Определяйте PNG, JPEG или WebP по реальным байтам перед выбором расширения. Если нужен конкретный формат, преобразуйте файл самостоятельно.
"png"gpt-image-2 принимает auto, low, medium, high. Обе модели 2.5 также принимают xhigh и max. Если поле опущено, используется значение модели по умолчанию; JSON null считается отсутствующим, а multipart требует непустую строку. Допустимое значение не гарантирует доступный сервис для любого сочетания модели, размера и редактирования или улучшение изображения при повышении уровня.
"auto"Ответ
200Расширение тарификации BetterToken. Сумма определяется фактическими пикселями и тарифным уровнем результата, который может отличаться от запрошенного. Денежные суммы — десятичные строки; используйте значения из ответа.
OpenAI-совместимый ответ с изображениями. Перебирайте data[] и читайте b64_json каждого изображения, допуская дополнительные поля, например revised_prompt. Фактическое число изображений может быть меньше запрошенного.
1710000000Show child attributes
Содержимое изображения в кодировке Base64.
"iVBORw0KGgoAAAANSUhEUgAA...(truncated)"Необязательный переписанный prompt.
Расширение тарификации BetterToken. Сумма определяется фактическими пикселями и тарифным уровнем результата, который может отличаться от запрошенного. Денежные суммы — десятичные строки; используйте значения из ответа.
Show child attributes
Номера переданных изображений, фактические размеры, тарифные уровни и стоимость каждого изображения. index начинается с 0 и соответствует изображению в data[].
Уровень разрешения, определённый по запросу; он может отличаться от фактического тарифа.
{
"created": 1710000000,
"data": [
{
"b64_json": "iVBORw0KGgoAAAANSUhEUgAA...(truncated)",
"revised_prompt": "<string>"
}
],
"bettertoken_image_billing": {
"currency": "<string>",
"settled_amount": "<string>",
"outputs": [
{
"index": 0,
"width": 1,
"height": 1,
"billing_resolution": "1K",
"currency": "<string>",
"amount": "<string>"
}
],
"partial": false,
"requested_resolution": "<string>"
}
}400Неверный формат запроса, отсутствующие параметры, ошибка разбора JSON или multipart, либо неверный формат размера.
Show child attributes
"invalid request""invalid_request_error""invalid_request""insufficient quota"{
"error": {
"message": "invalid request",
"type": "invalid_request_error",
"code": "invalid_request"
},
"message": "insufficient quota"
}401API Key отсутствует или недействителен.
Show child attributes
"invalid request""invalid_request_error""invalid_request""insufficient quota"{
"error": {
"message": "invalid request",
"type": "invalid_request_error",
"code": "invalid_request"
},
"message": "insufficient quota"
}402Недостаточно квоты или баланса.
Show child attributes
"invalid request""invalid_request_error""invalid_request""insufficient quota"{
"error": {
"message": "invalid request",
"type": "invalid_request_error",
"code": "invalid_request"
},
"message": "insufficient quota"
}403Недостаточно баланса или квоты ключа: error.code равен insufficient_user_quota. Средств также должно хватать на резерв по максимальной цене уровня, умноженной на верхний предел числа изображений; затем выполняется расчет по результату. Проверяйте error.code, а не считайте любой 403 ошибкой доступа.
{
"error": {
"code": "insufficient_user_quota",
"message": "Insufficient balance or token quota."
}
}429Сработал rate limit, лимит параллельных запросов или upstream временно перегружен.
Show child attributes
"invalid request""invalid_request_error""invalid_request""insufficient quota"{
"error": {
"message": "invalid request",
"type": "invalid_request_error",
"code": "invalid_request"
},
"message": "insufficient quota"
}500Ошибка gateway или upstream-сервиса.
Show child attributes
"invalid request""invalid_request_error""invalid_request""insufficient quota"{
"error": {
"message": "invalid request",
"type": "invalid_request_error",
"code": "invalid_request"
},
"message": "insufficient quota"
}
