API de generación de imágenes de OpenAI en Python: ejemplo paso a paso
Envía un prompt desde Python, recibe una imagen en base64 y guárdala en disco. Este tutorial usa los modelos GPT Image actuales y muestra cómo el tamaño, la calidad y el formato cambian el resultado; después añade ediciones con máscaras, vistas previas en streaming, lotes asíncronos y un registro de costos.
La mayoría de los tutoriales sobre el endpoint de imágenes de OpenAI se quedan en "aquí tienes una URL". Eso funcionaba con DALL-E. No funciona con los modelos GPT Image, que devuelven datos en base64 y nada más, así que el script que la gente copia primero suele fallar en result.data[0].url. Este tutorial parte de un script que sí funciona y lo amplía con tamaños, niveles de calidad, ediciones basadas en máscaras, vistas previas en streaming, lotes asíncronos y un pequeño control de costos.
Los parámetros, los nombres de los modelos y los precios que aparecen aquí provienen de la documentación actual de generación de imágenes de OpenAI y de su página de precios, consultadas el 2026-10-06. Cuando una cifra puede cambiar, el texto lo indica.
Antes de escribir código
Antes de que la primera solicitud funcione, necesitas tres cosas: un nombre de modelo, el SDK instalado y una organización de OpenAI verificada. Esta última es la que más problemas da a las cuentas nuevas, porque los modelos GPT Image devuelven un error de acceso hasta que se completa la verificación en la configuración de la consola para desarrolladores.
Elige un modelo
Estos son los modelos GPT Image que OpenAI tiene hoy en su lista de precios. Los cinco también funcionan en PicassoIA, lo que resulta útil para probar un prompt antes de escribir código.
Crea un secreto de proyecto en el panel de OpenAI y expórtalo como variable de entorno. El SDK lo lee automáticamente, así que el secreto nunca aparece en tu archivo de código.
# macOS / Linux
export OPENAI_API_KEY="sk-..."
# Windows PowerShell
$env:OPENAI_API_KEY = "sk-..."
💡 Consejo: mantén el secreto fuera de los cuadernos que vayas a compartir y fuera del historial de git. Si se filtra, revócalo en el panel y crea uno nuevo.
Tu primera imagen en Python
El script mínimo
Este es el script completo. Ejecútalo y aparecerá un PNG junto al script.
import base64
from pathlib import Path
from openai import OpenAI
client = OpenAI()
result = client.images.generate(
model="gpt-image-2.5-flare",
prompt="A ceramic bowl of ripe peaches on a linen cloth, soft window light, 50mm photograph",
size="1536x1024",
quality="medium",
)
image_bytes = base64.b64decode(result.data[0].b64_json)
Path("peaches.png").write_bytes(image_bytes)
Cuatro argumentos hacen todo el trabajo. model elige el motor, prompt describe la imagen, size fija las dimensiones en píxeles y quality equilibra velocidad y costo frente a detalle. Todo lo demás tiene un valor por defecto razonable, y el formato de salida es PNG salvo que pidas otra cosa.
Escribe prompts que aguanten
Un prompt que funciona en una demostración puede desmoronarse en un bucle de cincuenta imágenes. Cuatro hábitos mantienen los resultados constantes:
Empieza por el sujeto, luego el escenario, la luz y el objetivo. "Un cuenco de cerámica con melocotones maduros sobre un paño de lino, luz suave de ventana desde la izquierda, fotografía de 50 mm" funciona mejor que una lista de adjetivos.
Entrecomilla cualquier texto que deba aparecer en la imagen, y que sean una o dos palabras.
Di lo que quieres evitar en términos positivos. "Pared blanca lisa" funciona mejor que "nada de desorden".
Cambia una sola cosa por ejecución. Si alteras el sujeto, la luz y el tamaño a la vez, no podrás saber qué cambio ayudó.
Por qué la respuesta es base64
Los modelos GPT Image siempre devuelven base64. La opción response_format="url" que aceptaba DALL-E no es compatible, así que result.data[0].url no existe y la imagen llega dentro de b64_json. De ahí se derivan tres hábitos:
Decodifica una vez y guarda en disco.base64.b64decode te da los bytes en bruto para guardar, subir o pasar a Pillow.
Aloja el archivo tú mismo. Si un blog o una app necesita un enlace público, sube los bytes a tu propio almacenamiento (S3, R2, un CDN) y guarda esa URL.
Omite el disco para vistas previas rápidas. Construye un data URI con f"data:image/png;base64,{b64}" y colócalo en una etiqueta <img>.
💡 ¿Migras código antiguo? Buscar en tu proyecto .url y response_format encuentra casi todas las líneas que hay que cambiar.
Tamaño, calidad y formato de salida
Estos parámetros deciden lo que recibes y lo que cuesta. Esta es la lista completa que publica la documentación:
Parámetro
Valores aceptados
Notas
size
1024x1024, 1536x1024, 1024x1536, o WIDTHxHEIGHT personalizado
Los lados personalizados deben ser múltiplos de 16, la proporción entre 1:3 y 3:1, el lado más largo hasta 3840 px y el total de píxeles entre 655.360 y 8.294.400
quality
low, medium, high, auto
Los modelos 2.5 también admiten xhigh y max
output_format
png (por defecto), jpeg, webp
Elige webp o jpeg para archivos más ligeros
output_compression
0 a 100
Solo JPEG y WebP
background
transparent, opaque, auto
La transparencia necesita un formato con canal alfa, así que usa PNG o WebP
n
Entero
Varias imágenes en una misma solicitud
moderation
auto (por defecto), low
low aplica un filtrado más ligero
stream, partial_images
Booleano, de 0 a 3
Fotogramas de vista previa mientras se renderiza la imagen final
Algunas reglas prácticas:
Horizontal y vertical.1536x1024 y 1024x1536 sirven para la mayoría de formatos de blog y redes sociales. Para un 16:9 real, pide 2048x1152: ambos lados son múltiplos de 16 y el número de píxeles queda bien dentro del rango permitido.
Itera barato, termina con calidad. Redacta los prompts en quality="low" y vuelve a generar el ganador en high. Pagas por los tokens de salida, y una calidad más alta produce más de ellos.
Elige el formato según el destino. Mantén PNG para editar y para la transparencia, y cambia a webp con output_compression=85 para páginas que deban cargar rápido.
Edita imágenes existentes con máscaras
Edita una imagen
images.edit recibe un archivo de origen y un prompt que describa el cambio. Pasa una lista de archivos cuando quieras combinar varias referencias.
with open("living-room.png", "rb") as photo:
edited = client.images.edit(
model="gpt-image-2.5-sunburst",
image=photo,
prompt="Swap the grey sofa for a green velvet armchair, keep the window light unchanged",
)
Path("living-room-edit.png").write_bytes(base64.b64decode(edited.data[0].b64_json))
Describe con la misma claridad lo que debe seguir igual que lo que debe cambiar. Los modelos se desvían cuando el prompt solo nombra el elemento nuevo.
Añade una máscara
Una máscara limita la edición a una región. Es un PNG con canal alfa y las mismas dimensiones que el original. Los píxeles totalmente transparentes marcan la zona a repintar; todo lo opaco queda protegido. Pillow crea una en pocas líneas:
from PIL import Image, ImageDraw
base = Image.open("living-room.png").convert("RGBA")
mask = Image.new("RGBA", base.size, (0, 0, 0, 255)) # opaque: keep
ImageDraw.Draw(mask).rectangle((620, 380, 1180, 900), fill=(0, 0, 0, 0)) # transparent: repaint
mask.save("mask.png")
with open("living-room.png", "rb") as photo, open("mask.png", "rb") as hole:
edited = client.images.edit(
model="gpt-image-2.5-sunburst",
image=photo,
mask=hole,
prompt="A green velvet armchair with a wooden side table, matching the room's light",
)
La máscara es una guía, no un recorte exacto. Los bordes pueden desbordarse un poco, así que deja un pequeño margen alrededor del objeto que quieres reemplazar.
Vistas previas en streaming y lotes
Imágenes parciales mientras esperas
Los renders de alta calidad pueden tardar un rato. Con stream=True y partial_images, la API envía fotogramas de borrador antes de la imagen final, para que una interfaz pueda mostrar el progreso en lugar de un spinner.
stream = client.images.generate(
model="gpt-image-2.5-flare",
prompt="A vintage red bicycle leaning on a brick wall, golden hour, 35mm photograph",
size="1536x1024",
quality="high",
stream=True,
partial_images=2,
)
for event in stream:
if event.type == "image_generation.partial_image":
Path(f"preview-{event.partial_image_index}.png").write_bytes(base64.b64decode(event.b64_json))
else:
Path("final.png").write_bytes(base64.b64decode(event.b64_json))
Las vistas previas pueden sumar tokens al conteo, así que compara usage con y sin streaming antes de activarlo en cada solicitud.
Lotes asíncronos sin errores de límite
Para una lista de prompts, AsyncOpenAI junto con un semáforo mantiene bajo control el número de solicitudes en curso:
import asyncio
from openai import AsyncOpenAI
client = AsyncOpenAI(max_retries=4, timeout=180)
gate = asyncio.Semaphore(3)
async def render(index: int, prompt: str) -> Path:
async with gate:
result = await client.images.generate(
model="gpt-image-2.5-flare", prompt=prompt, size="1536x1024", quality="low",
)
path = Path(f"batch-{index:02d}.png")
path.write_bytes(base64.b64decode(result.data[0].b64_json))
return path
async def main(prompts: list[str]) -> list[Path]:
return await asyncio.gather(*(render(i, p) for i, p in enumerate(prompts)))
Un semáforo limita la concurrencia, no las imágenes por minuto. En los niveles de uso bajos, el límite por minuto es el que se alcanza primero, así que sube max_retries y deja que el SDK espere antes de reintentar. Cuando nadie espera la salida, estos modelos admiten el endpoint Batch, que cobra los tokens de salida a mitad de precio.
Costos, límites y errores
Tarifas de tokens por modelo
OpenAI cobra estos modelos por tokens, no por imagen, y no publica una tabla de precios por imagen para los actuales. Tarifas por 1M de tokens:
Modelo
Entrada de texto
Entrada de imagen
Salida de imagen
Salida de imagen (Batch)
gpt-image-2.5-flare
$5.00
$8.00
$30.00
$15.00
gpt-image-2.5-sunburst
$5.00
$8.00
$30.00
$15.00
gpt-image-2
$5.00
$8.00
$30.00
$15.00
gpt-image-1
$5.00
$10.00
$40.00
$20.00
gpt-image-1-mini
$2.00
$2.50
$8.00
$4.00
La calidad y el tamaño cambian cuántos tokens de salida usa una imagen, por eso un mismo prompt puede costar cantidades muy distintas en low y en high.
Controla el gasto en código
La respuesta incluye un objeto usage con el conteo de tokens. Conviértelo en dólares después de cada llamada y nunca te llevarás una sorpresa en la factura:
Antes de gastar presupuesto de API en experimentos de prompts, ejecútalos en el navegador. GPT Image 2 en PicassoIA renderiza texto legible dentro de las imágenes, admite fondos transparentes, acepta imágenes de referencia y genera hasta 10 variaciones por ejecución.
Prueba prompts sin código
Abre la página del modelo y escribe tu prompt. Pon entre comillas cualquier texto que deba aparecer dentro de la imagen.
Fija la calidad en low para los borradores. Cambia a high para el render final.
Elige una relación de aspecto: 3:2 o 2:3 reflejan 1536x1024 y 1024x1536, y 16:9 da un encuadre panorámico.
Elige el formato de salida. WebP es el valor por defecto; PNG mantiene limpia la transparencia.
Fija el número de imágenes entre 1 y 10, y genera.
Sube imágenes de referencia si quieres editar en lugar de crear desde cero.
Los campos del formulario se corresponden con la llamada en Python, así que una receta que funciona en el navegador se traslada directamente al código:
Campo de PicassoIA
Argumento de Python
aspect_ratio
size
quality
quality
output_format
output_format
output_compression
output_compression
background
background
moderation
moderation
number_of_images
n
input_images
image (en images.edit)
Un campo opcional acepta tu propia credencial de OpenAI; déjalo vacío y el proxy de PicassoIA gestiona la solicitud.
Dos hábitos más dan buenos resultados. Primero, redacta el prompt con un modelo de lenguaje: pega una idea aproximada en GPT 5 o Claude Sonnet 4.6 y pide tres variantes fotográficas con objetivo, luz y encuadre. Segundo, pasa el mismo prompt por PicassoIA Image y Seedream 4.5 para ver qué estilo encaja con tu proyecto. Para ediciones que no necesitan código, PicassoIA Image Editor Pro gestiona los cambios de foto directamente en el navegador.
La API para desarrolladores de PicassoIA
Si tu flujo de trabajo necesita otro proveedor, PicassoIA tiene su propia API para desarrolladores con una forma similar a la de Replicate:
URL base:https://api.picassoia.com/v1
Autenticación: un token Bearer que empieza por pia_sk_
Flujo: crea una predicción con POST /v1/models/{owner}/{name}/predictions, consulta GET /v1/predictions/{id} y luego lee el resultado
Límite: 5 predicciones simultáneas por cuenta
Los trabajos son asíncronos, así que el bucle de crear y consultar sustituye a la llamada bloqueante única que escribiste arriba. Revisa los requisitos del plan en la página de la API de PicassoIA antes de construir sobre ella.
Crea tus propias imágenes hoy
Ya tienes un flujo de trabajo funcional: instala el SDK, verifica la organización, pide una imagen, decodifica el base64, ajusta size y quality, edita con una máscara, muestra vistas previas en streaming, procesa en lotes con límites y registra el gasto. La forma más rápida de mejorar los resultados es el volumen. Escribe diez prompts, ejecútalos en low, quédate con los dos mejores y vuelve a renderizar esos en high.
Si quieres probar prompts antes de tocar la API, abre GPT Image 2 en PicassoIA, genera algunas variaciones y compáralas con otros modelos en la biblioteca de modelos de PicassoIA. Cuando tengas imágenes fijas que te gusten, la colección de efectos de PicassoIA puede añadir movimiento y estilo sobre ellas. Elige un tema que te importe, escribe un prompt detallado y mira qué sale.