Saltar al contenido

Editar imágenes con la API de OpenAI: modelo, máscara y migración

Sunburst si importa la precisión, Flare si prima la rapidez y gpt-image-2 si ya lo usas o editas por Batch. La máscara guía el cambio, pero no lo recorta.

•••11 min de lectura•Guías de API
Dibujo a lápiz de un salón antes y después de cambiar un cuadro abstracto por una lámina botánica, unidos por una tarjeta con una petición de edición

Para editar una imagen con la API de OpenAI, envía la imagen original y un prompt a POST /v1/images/edits (en los SDK, client.images.edit()) con uno de los dos modelos de GPT Image 2.5: gpt-image-2.5-sunburst cuando la precisión de la edición es lo más importante o gpt-image-2.5-flare cuando prima la rapidez. La respuesta trae la imagen en base64 dentro de data[0].b64_json; tienes que decodificarla antes de escribir el archivo.

Tres detalles deciden si la llamada funciona:

  • Archivos locales por multipart. Una imagen va en el campo image; varias, como image[]. Admite hasta 16, en PNG, WebP o JPG y de menos de 50 MB cada una.
  • La máscara es opcional y orientativa. Un PNG con canal alfa, del mismo tamaño que la primera imagen, en el que la zona transparente marca lo que se puede cambiar. El modelo la usa como guía, no como recorte exacto.
  • El código antiguo tiene fecha de caducidad. dall-e-2 y el endpoint variations ya no funcionan desde el 12 de mayo de 2026; gpt-image-1 se apaga el 23 de octubre de 2026, y gpt-image-1.5, gpt-image-1-mini y chatgpt-image-latest, el 1 de diciembre de 2026, según la página de retiradas de OpenAI.

gpt-image-2 sigue activo y sin fecha de retirada, así que puedes quedarte en él si tu integración ya funciona o si editas en lote con Batch, que los modelos 2.5 no admiten. Los ejemplos de código siguen el uso que documenta OpenAI en su guía de generación de imágenes y en la referencia del endpoint de edición; no son la salida de una llamada ejecutada, así que prueba primero con una imagen de muestra.

Qué modelo usar para editar

Tu situaciónModeloMotivo
Integración nueva; cambios delicados en productos, rostros o textogpt-image-2.5-sunburstOpenAI lo recomienda cuando la precisión de la edición es lo más importante
Integración nueva; borradores o ediciones diarias donde importa la velocidadgpt-image-2.5-flareEs el modelo rápido de la familia 2.5 y también admite edición
Ya tienes código estable con gpt-image-2gpt-image-2No está retirado; migrar es opcional
Ediciones masivas por Batchgpt-image-2Los modelos 2.5 no admiten v1/batch
Código con gpt-image-1, gpt-image-1.5, gpt-image-1-mini o chatgpt-image-latestCualquiera de los anterioresDejan de funcionar en octubre o diciembre de 2026 (ver la tabla de migración)

La diferencia entre Sunburst y Flare es una recomendación de uso, no un límite: las páginas de modelo de Sunburst y de Flare indican que los dos admiten v1/images/edits. No existe un modelo llamado gpt-image-2.5 a secas; escribe el ID completo. Si quieres comparar calidad, tiempos y precio de ambas variantes, tienes el detalle en GPT Image 2.5: qué cambia, cuánto cuesta y cómo usar la API.

La página de retiradas propone gpt-image-2 como sustituto de los modelos 1.x, porque se redactó antes de que existiera 2.5, mientras que la guía actual pide GPT Image 2.5 para integraciones nuevas. Las dos opciones son válidas; OpenAI no obliga a pasar a 2.5.

Una edición mínima que termina en un archivo

Antes de añadir máscara o varias referencias, consigue que una edición sencilla produzca un PNG que se abra. Necesitas una clave en OPENAI_API_KEY, una imagen salon.png junto al programa y la organización verificada si la API te lo pide: el error «Your organization must be verified» se resuelve como explica GPT Image 2: cómo resolver el error de organización sin verificar.

En Python (python -m pip install --upgrade openai):

python
import base64
from pathlib import Path
from openai import OpenAI

client = OpenAI()  # lee OPENAI_API_KEY del entorno

with open("salon.png", "rb") as original:
    result = client.images.edit(
        model="gpt-image-2.5-sunburst",
        image=original,
        prompt=(
            "Fotografía de este mismo salón. Sustituye el cuadro de la pared "
            "por una lámina botánica enmarcada. Conserva los muebles, la "
            "distribución, la luz y el encuadre de la imagen original."
        ),
        output_format="png",
    )

if not result.data or not result.data[0].b64_json:
    raise RuntimeError("La respuesta no contiene b64_json")

Path("salon-editado.png").write_bytes(base64.b64decode(result.data[0].b64_json))

En Node.js (npm install openai), guarda el código como editar.mjs para poder usar await en el nivel superior y ejecútalo con node editar.mjs:

javascript
import fs from "node:fs";
import OpenAI from "openai";

const client = new OpenAI();

const result = await client.images.edit({
  model: "gpt-image-2.5-sunburst",
  image: fs.createReadStream("salon.png"),
  prompt:
    "Fotografía de este mismo salón. Sustituye el cuadro de la pared " +
    "por una lámina botánica enmarcada. Conserva los muebles, la " +
    "distribución, la luz y el encuadre de la imagen original.",
  output_format: "png",
});

const encoded = result.data?.[0]?.b64_json;
if (!encoded) throw new Error("La respuesta no contiene b64_json");
fs.writeFileSync("salon-editado.png", Buffer.from(encoded, "base64"));

Con cURL la petición es multipart; cURL genera el Content-Type con su delimitador, así que no añadas application/json a mano. La respuesta se guarda como JSON y la imagen se extrae después con jq:

bash
curl --fail-with-body -sS https://api.openai.com/v1/images/edits \
  -H "Authorization: Bearer $OPENAI_API_KEY" \
  -D cabeceras.txt \
  -F "model=gpt-image-2.5-sunburst" \
  -F "image[]=@salon.png" \
  -F "prompt=Sustituye el cuadro por una lámina botánica enmarcada. Conserva el resto del salón." \
  -F "output_format=png" \
  -o respuesta.json

jq -r '.data[0].b64_json' respuesta.json | base64 --decode > salon-editado.png
grep -i x-request-id cabeceras.txt

La edición está completa cuando salon-editado.png se abre como imagen y conserva lo que pediste. Una respuesta HTTP 200 no basta, y guardar el texto base64 con extensión .png tampoco produce un PNG. Si pides varias imágenes con n, recorre todos los elementos de data; los ejemplos solo guardan el primero. Una edición compleja puede tardar hasta dos minutos según la guía de OpenAI, así que no configures un tiempo de espera del cliente más corto que eso.

En estos ejemplos faltan, a propósito, dos parámetros habituales en código antiguo. response_format no se admite con los modelos GPT Image: siempre devuelven b64_json y nunca una url. input_fidelity se omite porque gpt-image-2 procesa las imágenes de entrada con alta fidelidad y la referencia indica que ignora ese parámetro; para Sunburst y Flare, la documentación no dice si lo aceptan, así que es mejor no enviarlo.

Combinar varias imágenes de referencia

Con varias imágenes, pon primero la escena base y después las que aportan objetos, texturas o estilo. En multipart se repite el campo image[] una vez por archivo; en Python se pasa una lista:

python
with open("salon.png", "rb") as escena, open("lamina.png", "rb") as lamina:
    result = client.images.edit(
        model="gpt-image-2.5-sunburst",
        image=[escena, lamina],
        prompt=(
            "Usa la primera imagen como escena base. Coloca la lámina de la "
            "segunda imagen dentro del marco de la pared, con la misma "
            "perspectiva. No cambies los muebles ni la luz."
        ),
    )

El orden importa: si después añades una máscara, se aplica a la primera imagen. Di en el prompt qué aporta cada archivo; el modelo no sabe por sí solo cuál es la escena y cuál el detalle que debe copiar. Los límites de la referencia de edición son los mismos que para una sola imagen: hasta 16 archivos PNG, WebP o JPG de menos de 50 MB cada uno.

Cuerpo JSON en lugar de multipart

La referencia también acepta un cuerpo JSON. En ese caso el campo se llama images y es una lista de objetos, cada uno con image_url (una URL completa o una data URL en base64) o con file_id (un archivo subido antes con la API de archivos, Files API):

bash
curl --fail-with-body -sS https://api.openai.com/v1/images/edits \
  -H "Authorization: Bearer $OPENAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "gpt-image-2.5-flare",
    "images": [
      { "image_url": "https://ejemplo.com/salon.png" },
      { "file_id": "file-ID_DE_TU_ARCHIVO" }
    ],
    "prompt": "Coloca la lámina de la segunda imagen en el marco de la primera."
  }' \
  -o respuesta.json

Una ruta local como "salon.png" dentro del JSON no sube el archivo: la API solo acepta URL, data URL o file_id. Para archivos que están en tu disco, multipart sigue siendo la forma directa. La documentación tampoco garantiza que todas las versiones de los SDK expongan esta forma JSON; si la necesitas desde un SDK, comprueba la firma del método en la versión que tienes instalada.

Editar solo una zona con máscara (mask)

Tres paneles de un salón: la foto original, la zona del cuadro marcada como transparente en la máscara y un resultado ilustrativo con una lámina botánica

Una máscara indica qué parte de la imagen puede cambiar. La guía de OpenAI y la referencia no dan exactamente los mismos requisitos (la guía habla de 50 MB y la referencia exige menos de 4 MB para la máscara), así que lo más seguro es cumplir ambos:

RequisitoValor seguro
FormatoPNG con canal alfa
Zona editablePíxeles totalmente transparentes (alfa cero)
DimensionesIdénticas a las de la primera imagen, en píxeles
PesoMenos de 4 MB
Con varias imágenesSe aplica solo a la primera

Si tu máscara es en blanco y negro, sin transparencia, la guía de OpenAI la convierte así con Pillow. El propio gris se usa como alfa: lo negro queda transparente y editable, y lo blanco, opaco y protegido.

python
from PIL import Image

mascara = Image.open("mascara-bn.png").convert("L")
mascara_rgba = mascara.convert("RGBA")
mascara_rgba.putalpha(mascara)
mascara_rgba.save("mascara.png")

original = Image.open("salon.png")
assert original.size == mascara_rgba.size, "La máscara debe medir lo mismo que la imagen"

Después basta con añadir mask a la llamada. Describe en el prompt la imagen final completa, no solo lo que va dentro de la zona transparente:

python
with open("salon.png", "rb") as original, open("mascara.png", "rb") as mask:
    result = client.images.edit(
        model="gpt-image-2.5-sunburst",
        image=original,
        mask=mask,
        prompt=(
            "Salón luminoso con un sofá beige, una mesa baja de madera y una "
            "lámina botánica enmarcada en la pared. Mantén el sofá, la mesa, "
            "las plantas, la luz y el encuadre de la foto original."
        ),
    )

En cURL, la máscara es un campo más: -F "mask=@mascara.png", junto a -F "image[]=@salon.png".

La máscara no garantiza que todo lo de fuera quede idéntico. Según la guía, la edición con máscara en GPT Image «se basa por completo en el prompt» y el modelo puede no seguir la forma exacta. Compara el resultado con el original, sobre todo en rostros, logotipos, texto y geometría de producto. Si tu aplicación necesita que el exterior de la selección no cambie ni un píxel, tendrás que componer tú el resultado sobre el original y comprobarlo; añadir mask no basta.

Parámetros de edición que conviene revisar

ParámetroValoresLo que debes saber
prompt1–32.000 caracteresDescribe el resultado final y lo que debe conservarse
qualitylow, medium, high; xhigh y max solo en 2.5; auto por defectolow sirve para borradores rápidos
size1024x1024, 1536x1024, 1024x1536 o personalizadoEn gpt-image-2 y 2.5: lados múltiplos de 16, proporción entre 1:3 y 3:1, hasta 3840x2160; por encima de 2560x1440 es experimental
output_formatpng (por defecto), jpeg, webpUsa la misma extensión al guardar; jpeg es más rápido
output_compression0–100Solo con jpeg o webp
backgroundtransparent, opaque, autoTransparente admitido en 2.5 y en versión preliminar en gpt-image-2; requiere png o webp
n1–10Número de imágenes devueltas; no controla cuánto cambia la original
input_fidelity—No lo envíes
response_format—No se admite con GPT Image

Si editas una imagen con fondo transparente, repite en cada prompt que la transparencia debe mantenerse; de lo contrario, una edición posterior puede añadir un fondo nuevo. Cómo pedir y comprobar un PNG o WebP con alfa real está en Fondo transparente con GPT Image 2 y 2.5: modelo, código y fallos.

Revisiones sucesivas y cuándo pasar a Responses

El endpoint de edición no recuerda llamadas anteriores. Para retocar un resultado, vuelve a enviar como imagen la última versión que hayas aceptado, no cualquier resultado recién generado, y pide un solo cambio: «Conserva esta versión y reduce el reflejo del cristal del cuadro». Guarda el original y cada versión aceptada con nombres distintos; si una edición deforma el sofá o la perspectiva, vuelves a la anterior sin arrastrar el error.

La guía de OpenAI reserva la Images API para generar o editar una imagen a partir de un prompt, y la Responses API para experiencias conversacionales en las que el usuario pide cambios sobre lo anterior. Si tu producto es un chat de edición, en Responses:

  • el model de nivel superior es un modelo de texto compatible, nunca un modelo de imagen;
  • la edición la hace la herramienta image_generation, a la que indicas el modelo de imagen, por ejemplo {"type": "image_generation", "model": "gpt-image-2.5-sunburst"};
  • la máscara se pasa como input_image_mask con un file_id;
  • los turnos siguientes se enlazan con previous_response_id.

Para una aplicación que recibe una imagen, hace un cambio y devuelve un archivo, la Images API es suficiente.

Migrar desde variations, dall-e-2 y gpt-image-1

Si tu código usa…Estado según OpenAIQué cambiar
dall-e-2 o dall-e-3Retirados el 12 de mayo de 2026Pasa a gpt-image-2.5-sunburst, -flare o gpt-image-2; quita response_format y la lectura de url
/v1/images/variations (create_variation en Python, createVariation en Node)Solo admite dall-e-2, que ya no existeUsa /v1/images/edits con la imagen de origen y un prompt explícito
gpt-image-1Se apaga el 23 de octubre de 2026Cambia el modelo; quita input_fidelity si lo envías
gpt-image-1.5, gpt-image-1-mini, chatgpt-image-latestSe apagan el 1 de diciembre de 2026Cambia el modelo; quita input_fidelity si lo envías; xhigh y max solo existen en 2.5
gpt-image-2Sin fecha de retiradaNada obligatorio; 2.5 para funciones nuevas si no dependes de Batch

Fuentes: página de retiradas y referencia de variations. La página del modelo DALL·E 2 recomienda hoy GPT-Image-2.5 Sunburst para generar y editar.

Con variations, actualizar el SDK o cambiar solo model no sirve: la llamada sigue llegando a un endpoint que solo aceptaba DALL·E 2. Hay que cambiar de método y de ruta. Además, variations funcionaba sin prompt, y ese comportamiento no tiene un equivalente exacto; en edits debes decir qué puede cambiar y qué se conserva. «Crea otra versión» deja demasiado abierto; «mantén los muebles y el encuadre, cambia la luz a la de una tarde cálida y no añadas objetos» define la alternativa.

Si llegas con código de gpt-image-1.5 y máscara, la máscara, los campos image e image[] y la lectura de b64_json no cambian; basta con cambiar el ID del modelo y quitar input_fidelity. Aun así, revisa con unas cuantas imágenes que el resultado sigue siendo aceptable, porque el modelo que interpreta la máscara es otro.

Cuando la edición falla: dónde mirar

Infografía de cuatro etapas para localizar un fallo de edición: modelo y destino, imagen y máscara, respuesta y bytes, y resultado visual

Antes de retocar el prompt, averigua en qué capa está el fallo. Cambia una sola cosa cada vez:

Lo que observasDónde está el problemaQué comprobar primero
Error sobre model, modelo no encontrado o un modelo retiradoRuta y modeloID completo (no gpt-image-2.5), URL real incluida la base_url de tu cliente, y que no sigues llamando a variations
Error sobre image o maskArchivosFormato, peso, número de imágenes; máscara PNG con alfa, del tamaño exacto de la primera imagen y de menos de 4 MB
HTTP 200 pero no hay archivo válidoRespuestaQue existe data[0].b64_json y que lo decodificas antes de guardar
El archivo se abre, pero cambia demasiadoResultado visualPrompt que describa la escena completa, orden de las referencias, máscara y comparación con el original

Para los errores de la API, la guía de OpenAI pide mirar el estado HTTP o la excepción del SDK y guardar el identificador de la petición (la cabecera x-request-id). Reintenta con espera creciente solo los errores transitorios: límite de peticiones (429) y fallos del servidor (5xx). Según sus páginas de modelo, en el nivel 1 de uso Sunburst y Flare admiten 5 imágenes por minuto, así que un 429 temprano puede ser simplemente ese límite. No reintentes automáticamente los errores de cuota ni los que traen error.type = "image_generation_user_error": ahí hay que cambiar el prompt o las imágenes, y error.code indica el motivo. Si el código es moderation_blocked, el campo opcional moderation_details dice si el bloqueo vino de la entrada o de la imagen generada.

Si ves Value must be 'dall-e-2', el mensaje corresponde a un fallo ya corregido. En el issue #1844 de openai-node, abierto en abril de 2026, el endpoint de edición rechazaba gpt-image-2 con ese mensaje tanto desde el SDK como con cURL. OpenAI lo corrigió en el servicio y el issue se cerró el 13 de agosto de 2026. Si hoy te aparece contra api.openai.com, revisa primero si tu código sigue llamando a variations; si pasas por un intermediario, puede que su validación no se haya actualizado. Cambiar de SDK o de modelo a ciegas no lo aclara.

Si usas un servicio compatible con la API de OpenAI en lugar de la conexión directa, la ruta y la clave son las de ese servicio. Por ejemplo, según su documentación de GPT Image 2.5, laozhang.ai usa la misma ruta /v1/images/edits con la base URL https://api.laozhang.ai/v1; sus modelos de pago por petición (los que acaban en -vip) no admiten mask, mientras que gpt-image-2.5-flare y gpt-image-2.5-sunburst se facturan por tokens a la tarifa de OpenAI o con un 20 % más, según el grupo de la clave. Ningún intermediario es necesario para editar desde España: la API de OpenAI está disponible allí directamente, según su lista de países admitidos. Compara siempre las peticiones contra el mismo proveedor y no reutilices una clave en otro servicio.

Preguntas frecuentes

¿Puedo editar con Flare o solo con Sunburst?

Con los dos. Ambos admiten /v1/images/edits. OpenAI recomienda Sunburst cuando la precisión de la edición es lo más importante, pero es una orientación de uso, no una restricción. Si dudas, prueba la misma edición con los dos sobre tus propias imágenes.

¿Puedo recibir una URL en lugar de base64?

No con los modelos GPT Image: url no se admite y response_format tampoco. Si tu aplicación necesita un enlace, decodifica b64_json, sube el archivo a tu propio almacenamiento y sirve esa URL.

¿Cuánto cuesta una edición?

Sunburst, Flare y gpt-image-2 tienen la misma tarifa estándar por tokens (8 $ por millón de tokens de imagen de entrada y 30 $ por millón de tokens de imagen de salida), según los precios de OpenAI. Una edición paga además los tokens de las imágenes que envías, así que el total depende también de cuántas referencias incluyes. Revisa el campo usage de la respuesta para ver el consumo real; el cálculo por calidad y tamaño está en Precio por imagen de la API de OpenAI: GPT Image 2.5 y GPT Image 2.

¿Hay forma de editar fotos con ChatGPT sin programar?

Sí, pero no a través de la API. La aplicación de ChatGPT edita fotos desde el chat, y si trabajas en Codex puedes seguir Cómo generar y editar imágenes con GPT Image 2 en Codex.

¿Encontraste un error o un problema que el artículo no cubre? Envía la URL del artículo y lo que viste a hi@laozhang.ai. Lo revisamos y actualizamos el artículo.