Precios+7% extra

Generar varias imágenes a la vez en ChatGPT

Aprende a generar imágenes de producto en masa con la API de GPT Image 2 usando Python, archivos CSV, concurrencia segura, reintentos, decodificación Base64 y controles de costos.

Generar varias imágenes a la vez en ChatGPT

Imagina que tienes una hoja de cálculo con 100 productos. Cada fila tiene un nombre de producto, color, material y ángulo de cámara preferido. En lugar de copiar esos detalles en ChatGPT 100 veces, puedes dejar que un script de Python lea la hoja de cálculo y solicite una imagen de producto por cada fila.

Eso es lo que significa “generación masiva de imágenes de productos” en esta guía. Es diferente de pedir diez variaciones de una misma imagen. Cada producto recibe su propio prompt, ID de tarea, archivo de salida y registro de estado.

Construirás este flujo de trabajo en Python con la GPT Image 2 API en GPTProto. No se requiere experiencia previa con API de imágenes. El script final lee un catálogo CSV, verifica el tamaño de la imagen, procesa unos pocos productos a la vez, descarga los resultados y recuerda el trabajo completado para que un reinicio no vuelva a generar los mismos productos.

La versión corta es:

  1. Usa n=1 para cada prompt de producto distinto.

  2. Envía varios trabajos de SKU diferentes a través de un pequeño grupo de trabajadores.

  3. Guarda el ID de predicción devuelto antes de esperar la imagen.

  4. Descarga las URL de salida temporales tan pronto como se complete un trabajo.

  5. Reanuda desde el manifiesto en lugar de enviar de nuevo los productos exitosos.

No te preocupes si términos como “ID de predicción”, “sondeo” o “manifiesto” no te resultan familiares. Se explican a continuación antes de que aparezca el script completo.

Tabla de contenido

Lo que tendrás al final

Comenzarás con dos archivos:

  • products.csv: una lista similar a una hoja de cálculo de los productos que quieres procesar.

  • bulk_product_images.py: el script de Python de esta guía.

Cuando se ejecuta el script, crea automáticamente dos elementos más:

  • generated/: una carpeta que contiene archivos como MUG-001-01.png.

  • manifest.json: un registro que muestra qué productos se completaron, fallaron o aún necesitan revisión.

El flujo es sencillo:

products.csv → script de Python → API de GPT Image 2 → imágenes descargadas
                         ↘ manifest.json

Comienza con los tres productos de ejemplo de esta guía. Cuando funcionen, reemplaza las filas de ejemplo con tu catálogo real.

Términos para principiantes usados en esta guía

Término Significado en lenguaje sencillo
API Una forma de que tu script envíe instrucciones a GPT Image 2 sin usar una ventana de chat.
Clave de API Una contraseña privada que identifica tu cuenta de GPT Proto y autoriza el uso de la API.
SKU Tu propio código único para un producto, como MUG-001. También se usa en el nombre del archivo de salida.
Solicitud Un mensaje enviado por el script a la API. En este flujo de trabajo, normalmente un producto crea una solicitud de generación.
ID de tarea o predicción El número de recibo devuelto después de que se acepta una solicitud de generación. El script lo usa para verificar el resultado.
Sondeo Preguntar al endpoint de resultados cada pocos segundos si una tarea ha terminado.
Concurrencia El número de productos que se procesan al mismo tiempo. Tres workers significa hasta tres trabajos activos.
Manifiesto Un registro local de cada SKU, ID de tarea, resultado y error. Permite que un script detenido continúe de forma segura.
Base64 Datos de imagen representados como una cadena de texto larga. Debe decodificarse antes de convertirse en un archivo de imagen normal.

El flujo de trabajo de la API es similar a recoger un pedido de un restaurante. La primera solicitud realiza el pedido. El ID de tarea devuelto es el número de recogida. El sondeo verifica si el pedido está listo. La URL de salida es donde se puede recoger el artículo terminado. El manifiesto es tu libro de recibos.

¿Puede ChatGPT generar varias imágenes de productos a la vez?

ChatGPT puede crear varias imágenes a lo largo de una conversación, por lo que es adecuado para la exploración visual y un pequeño número de variaciones manuales. No es un pipeline de catálogo confiable: no hay cola CSV, mapeo estable de SKU a archivo, control de concurrencia ni manifiesto de reintentos.

La API es más adecuada cuando la pregunta es “¿Cómo creo varias imágenes de productos y sé qué archivo pertenece a qué producto?”

Necesidad Interfaz de ChatGPT Flujo de trabajo de la API de GPT Image 2
Explorar un concepto Buena opción Posible, pero con más configuración
Procesar un catálogo CSV Manual Automatizado
Usar un prompt diferente por SKU Repetitivo Una fila se convierte en una solicitud
Controlar nombres de archivos y carpetas Descarga manual Nomenclatura determinista
Reanudar trabajo fallido Revisión manual Basado en manifiesto
Integrar con un DAM, PIM o tienda Limitado Diseñado para integración de código

Búsquedas como “ChatGPT generate multiple images at once” a menudo mezclan dos objetivos diferentes: varias variaciones de un mismo prompt y muchas imágenes para productos diferentes. La distinción importa una vez que entran en juego el costo, la propiedad de los archivos y las repeticiones.

Qué puede significar “GPT Image 2 API Batch”

La gente usa “batch” para tres patrones separados:

1. Varias variaciones a partir de un prompt

Establece n entre 1 y 10. Esto es útil cuando un diseñador quiere varios candidatos para el mismo producto y composición. No crea prompts específicos de producto para diez SKUs.

2. Varias solicitudes independientes al mismo tiempo

Lee varios productos de un archivo y envía una solicitud por fila. Un pequeño ThreadPoolExecutor proporciona concurrencia controlada. Este es el patrón usado en este tutorial porque cada producto puede tener su propio nombre, color, material, ángulo y fondo.

3. La API Batch asíncrona de OpenAI

La API Batch directa de OpenAI acepta archivos de solicitud JSONL, usa un grupo de límite de velocidad separado, ofrece un 50% de descuento y se completa en una ventana de 24 horas. La guía de la API Batch de OpenAI enumera /v1/images/generations y /v1/images/edits como endpoints compatibles.

Esa es una característica separada de OpenAI. No asumas que el mismo endpoint JSONL Batch existe en GPT Proto. El flujo de trabajo de GPT Proto a continuación usa su endpoint de imágenes documentado y la ruta de estado de predicción.

Lo que necesitas

Necesitas una computadora, Python 3, una clave de API de GPT Proto y un editor de texto. Visual Studio Code es conveniente, pero cualquier editor que guarde archivos de texto plano funcionará.

1. Comprueba que Python esté instalado

Abre Terminal en macOS o Linux, o PowerShell en Windows, y ejecuta:

python --version

Si Windows dice que python no se reconoce, prueba:

py --version

Un resultado como Python 3.11.8 significa que Python está listo. Si ninguno de los comandos funciona, instala una versión actual de Python 3 y habilita la opción que agrega Python a la ruta del sistema.

2. Crea una carpeta de proyecto

Crea una carpeta llamada gpt-image-bulk. Dentro de ella, crea estos dos archivos vacíos:

  • bulk_product_images.py

  • products.csv

Pega el script completo de Python de esta guía en el primer archivo. Pega el CSV de ejemplo que se muestra a continuación en el segundo archivo. Ambos archivos deben estar en la misma carpeta a menos que cambies las rutas en el script.

3. Crea y guarda tu clave de API

Crea una cuenta en GPT Proto, agrega crédito y genera una clave de API. Una clave de API debe tratarse como una contraseña. No la publiques en un artículo, no la subas a GitHub ni la pegues directamente en el archivo de Python.

Guarda la clave temporalmente en una variable de entorno. En macOS o Linux, ejecuta:

export GPTPROTO_API_KEY="your-key-here"

En Windows PowerShell, ejecuta:

$env:GPTPROTO_API_KEY="your-key-here"

La variable normalmente dura para la ventana de terminal actual. Si cierras esa ventana y abres otra, vuelve a configurarla antes de ejecutar el script.

4. Instala el único paquete de Python requerido

El script usa requests para comunicarse con la API y descargar imágenes. Instálalo con:

python -m pip install requests

Los usuarios de Windows que necesitaron el comando py deben ejecutar py -m pip install requests en su lugar.

5. Añade tus productos al CSV

CSV significa “valores separados por comas”. Es un formato de hoja de cálculo simple que Excel, Google Sheets y la mayoría de las herramientas de catálogo pueden exportar. Añade un producto por fila:

Crea products.csv con una fila por producto:

sku,name,color,material,angle,background,avoid
MUG-001,Taza de café apilable,azul marino mate,gres,tres cuartos,gris cálido suave,vapor o manos
BAG-014,Bolso bandolera compacto,verde bosque,cuero granulado,tres cuartos frontal,beige claro,accesorios o un modelo
LAMP-207,Lámpara de mesa portátil,crema,aluminio con recubrimiento en polvo,frontal,blanco limpio,cable visible u objetos adicionales

Cada columna tiene una función:

Columna Qué introducir Ejemplo
sku Un código de producto único MUG-001
name Un nombre de producto corto Taza de café apilable
color El color o acabado visible exacto azul marino mate
material El material visible principal gres
angle La vista de cámara deseada tres cuartos
background El fondo del catálogo gris cálido suave
avoid Objetos o errores que la imagen no debe contener vapor o manos

Mantén la fila de encabezado exactamente como se muestra porque el script de Python busca esos nombres de columna. Cada SKU debe ser único. Si un nombre de producto o campo contiene una coma, exporta el archivo desde un programa de hoja de cálculo para que el valor se cite correctamente.

Usa campos breves y factuales en lugar de escribir un prompt largo diferente en cada fila. El script combinará los campos con las mismas reglas de fotografía, lo que ayuda a que el catálogo se vea coherente.

Paso 1: Crea un prompt de producto reutilizable

Un prompt de producto debe separar los hechos que cambian según el SKU de las reglas que se aplican a todo el catálogo. Esto hace que las revisiones del prompt sean medibles: cambia la plantilla compartida una vez, regenera un pequeño conjunto de prueba y compáralo con la versión anterior.

def build_prompt(row):
    return f"""Crea una fotografía de producto de catálogo.
Producto: {row['name']}
Color: {row['color']}
Material: {row['material']}
Vista: {row['angle']}
Fondo: {row['background']}

Mantén el producto centrado con proporciones realistas y textura del material.
Usa iluminación de estudio suave, una sombra de contacto natural y un amplio espacio seguro para el recorte.
Sin texto, logotipos, etiquetas, personas ni objetos adicionales.
También evita: {row.get('avoid', 'none')}.
Devuelve una sola imagen de producto, no un collage ni una hoja de contactos."""

No necesitas editar esta función para cada producto. Cuando el script lee la fila de la taza, {row['name']} se convierte en Taza de café apilable; cuando lee la fila del bolso, el mismo marcador se convierte en Bolso bandolera compacto. Esto se llama plantilla: la estructura permanece igual mientras cambian los datos del producto.

La instrucción “una sola imagen de producto” ayuda a evitar que un modelo convierta una solicitud en un collage. Las instrucciones compartidas sobre vista de cámara, recorte, iluminación, sombra y fondo ayudan a que productos separados parezcan un solo catálogo en lugar de imágenes no relacionadas.

Si los resultados iniciales son inconsistentes, edita primero las reglas compartidas. Por ejemplo, reemplaza “amplio espacio seguro para el recorte” por “el producto debe ocupar aproximadamente el 75% del lienzo cuadrado”. Prueba ese cambio en tres tipos de producto diferentes antes de aplicarlo a todo el archivo.

Para productos de marca reales, texto a imagen es mejor para tomas conceptuales o merchandising genérico. Si la geometría exacta, el empaque o la ubicación de la etiqueta importan, usa edición de imagen con referencias de producto aprobadas; ese flujo de trabajo se cubre más adelante.

Paso 2: Prueba un producto antes de iniciar la cola

Comienza con un producto, n=1, quality="low", y un tamaño común como 1024x1024. La calidad baja es útil para comprobar la composición antes de pagar por generaciones de calidad final.

El siguiente comando cURL es una pequeña prueba de API. cURL es una herramienta de línea de comandos que envía una solicitud web. Este comando exacto usa sintaxis de Bash, así que pégalo en Terminal en macOS o Linux después de configurar tu clave de API. Los principiantes en Windows pueden omitir la prueba manual de cURL y usar el script de Python multiplataforma que aparece más adelante en esta guía.

curl --request POST \
  --url "https://gptproto.com/api/v3/openai/gpt-image-2/text-to-image" \
  --header "Authorization: Bearer $GPTPROTO_API_KEY" \
  --header "Content-Type: application/json" \
  --data '{
    "prompt": "Una taza de café de gres mate azul marino centrada, vista de catálogo de tres cuartos, luz de estudio suave, fondo gris cálido, sombra de contacto natural, sin texto ni accesorios",
    "n": 1,
    "quality": "low",
    "size": "1024x1024",
    "enable_sync_mode": false,
    "response_format": "url"
  }'

Esto es lo que significan las líneas importantes:

Campo Significado
Authorization Envía tu clave de API privada para que GPT Proto sepa qué cuenta está realizando la solicitud.
prompt Describe la imagen del producto que deseas.
n: 1 Solicita un resultado para este prompt.
quality: low Usa la configuración de borrador de menor costo para la primera prueba.
size: 1024x1024 Solicita una imagen cuadrada.
enable_sync_mode: false Devuelve primero un recibo de tarea en lugar de mantener la solicitud abierta hasta que finalice la generación.
response_format: url Devuelve un enlace de descarga cuando la imagen está lista.

Una primera respuesta exitosa es un objeto JSON que contiene un ID de predicción en data.id. JSON es simplemente un formato de texto estructurado que usan las API. Puede verse como este ejemplo abreviado:

{
  "data": {
    "id": "abc123",
    "status": "created",
    "urls": {
      "get": "https://gptproto.com/api/v3/predictions/abc123/result"
    }
  }
}

Esta respuesta no es la imagen. Confirma que el trabajo fue aceptado. Copia el ID real que se te devolvió y consulta su resultado:

curl --request GET \
  --url "https://gptproto.com/api/v3/predictions/abc123/result" \
  --header "Authorization: Bearer $GPTPROTO_API_KEY"

Reemplaza abc123 con tu ID de tarea. Si data.status sigue siendo created o processing, espera unos segundos y ejecuta el comando GET de nuevo. Cuando pase a completed, data.outputs contiene la salida generada. El script completo de Python realiza estas comprobaciones automáticamente, así que no necesitarás repetir este proceso manual para cada producto.

Consulta la referencia de texto a imagen de GPT Proto GPT Image 2 para conocer los campos actuales de solicitud y respuesta.

No lances el catálogo completo hasta que un producto pase estas comprobaciones:

  • El recorte deja suficiente espacio para cada ubicación en la tienda.

  • El color y el material son lo suficientemente cercanos para el uso previsto.

  • El fondo y la sombra de contacto coinciden con el estilo del catálogo.

  • No aparece ningún logotipo, etiqueta, accesorio o producto duplicado inventado.

  • La ruta de resultado y el código de descarga funcionan en tu entorno.

Si la prueba devuelve 401 Unauthorized, lo primero que debes revisar es la clave de API y el encabezado Authorization. Si devuelve 403, revisa el acceso al modelo y el saldo de la cuenta. No continúes con el catálogo completo hasta que esta prueba de un solo artículo tenga éxito.

Paso 3: Evita los errores “El tamaño debe ser múltiplo de 16”

GPT Image 2 acepta ajustes preestablecidos comunes y dimensiones personalizadas válidas. Para tamaños personalizados, ambos lados deben ser múltiplos de 16, el lado más largo no puede exceder los 3,840 píxeles, la relación de aspecto no puede exceder 3:1, y la imagen debe contener entre 655,360 y 8,294,400 píxeles. Las salidas por encima de 2560×1440 de resolución total están marcadas como experimentales en la documentación de la API.

“Ambos lados deben ser múltiplos de 16” significa que el ancho y el alto deben dividirse cada uno entre 16 sin dejar resto. Números como 1024, 1200, 1536 y 2048 pasan esa comprobación. Un número como 1000 no. Esta regla se aplica tanto al ancho como al alto, no solo al lado más largo.

Valida antes de enviar una solicitud de pago:

def validate_size(size):
    if size == "auto":
        return

    width, height = map(int, size.lower().split("x"))
    pixels = width * height

    if width % 16 or height % 16:
        raise ValueError("El ancho y el alto de GPT Image 2 deben ser múltiplos de 16")
    if max(width, height) > 3840:
        raise ValueError("El lado más largo no puede exceder 3840 píxeles")
    if max(width, height) / min(width, height) > 3:
        raise ValueError("La relación de aspecto no puede exceder 3:1")
    if not 655_360 <= pixels <= 8_294_400:
        raise ValueError("El total de píxeles está fuera del rango admitido")

1200x1200 es válido porque ambos lados se dividen entre 16. 1000x1000 no lo es. Si una tienda finalmente necesita 1000x1000, genera a 1024x1024 y redimensiona después de la aprobación.

Los ajustes preestablecidos admitidos incluyen 1024x1024, 1536x1024, 1024x1536, 2048x2048, 2048x1152, 3840x2160, y 2160x3840.

Paso 4: Use concurrencia controlada, no un bucle sin límites

La concurrencia significa cuántos productos procesa el script al mismo tiempo. Piense en los trabajadores como cajas de pago: un trabajador procesa un producto activo; tres trabajadores permiten que hasta tres productos pasen por el sistema a la vez.

Un bucle que inicia cientos de trabajadores puede alcanzar los límites de velocidad, consumir memoria y dificultar la recuperación ante fallos. Comience con dos o tres. Aumente solo después de verificar el comportamiento de la cuenta y los tiempos de finalización.

from concurrent.futures import ThreadPoolExecutor, as_completed

with ThreadPoolExecutor(max_workers=3) as pool:
    futures = [pool.submit(process_product, row) for row in products]
    for future in as_completed(futures):
        print(future.result())

No necesita entender los hilos de Python para usar este ejemplo. max_workers=3 es la configuración importante. Si recibe errores 429 repetidos, redúzcalo a 2 o 1. Si una prueba larga se ejecuta sin errores de límite de velocidad, puede probar con cautela un valor mayor.

Los límites de velocidad publicados por el proveedor del modelo no son automáticamente los límites de una cuenta de API intermediaria. Trate 429 Too Many Requests y su encabezado Retry-After como la fuente de verdad para la clave GPT Proto activa. Mantenga la concurrencia configurable en lugar de incrustar un número agresivo en el código.

OpenAI también publica límites basados en niveles para el acceso directo al modelo. Esas cifras son un contexto útil, pero no deben presentarse como los límites de velocidad de GPT Proto.

Paso 5: Guarde correctamente los resultados de URL y Base64

GPT Proto admite response_format="url" y response_format="b64_json" para este endpoint.

Los principiantes deben usar url primero. La API devuelve un enlace de imagen normal, y el script descarga el archivo en la carpeta generated. Descárguelo inmediatamente porque la URL del resultado es un enlace de entrega, no un almacenamiento permanente de activos. Guarde los archivos aprobados en su propia carpeta de medios de producto, DAM o almacenamiento de objetos.

Base64 es otra forma de entregar la misma imagen: la respuesta contiene una cadena de texto muy larga en lugar de una URL. Es útil cuando un sistema debe mantener todos los datos dentro de JSON, pero el texto debe convertirse de nuevo en bytes binarios de imagen antes de que el archivo pueda abrirse. No necesita Base64 para la primera versión de este flujo de trabajo.

Para la salida Base64, decodifique la cadena en bytes:

import base64
from pathlib import Path

def save_base64_image(value, destination):
    # Also accepts a data URI such as data:image/png;base64,...
    encoded = value.split(",", 1)[1] if value.startswith("data:") else value
    Path(destination).write_bytes(base64.b64decode(encoded, validate=True))

Un error común de decodificación Base64 en GPT Image 2 es escribir el texto codificado directamente en un archivo .png. Un archivo de imagen válido contiene bytes binarios decodificados, no la cadena JSON devuelta por la API.

Si usa OpenAI directamente en lugar de GPT Proto, tenga en cuenta que los modelos de imagen GPT devuelven b64_json; la guía de generación de imágenes de OpenAI dice que el formato de respuesta URL no es compatible con los modelos de imagen GPT. La diferencia específica del proveedor es la razón por la que el análisis de la respuesta debe coincidir con el endpoint que realmente llama.

Paso 6: Reintente sin crear imágenes duplicadas

Una solicitud de API puede fallar porque la cuenta envía trabajo demasiado rápido, la red se desconecta o la generación en sí es rechazada. Es tentador repetir cada comando fallido, pero eso puede crear imágenes duplicadas.

Los reintentos no son igualmente seguros para cada solicitud. POST crea una nueva tarea; GET solo verifica una existente.

  • Una consulta de estado GET es segura de reintentar porque no crea otra imagen.

  • Un POST que recibe un rechazo definitivo 429 puede esperar Retry-After y volver a intentarlo.

  • Un tiempo de espera de conexión POST es ambiguo. El servidor puede haber aceptado el trabajo aunque el cliente no haya visto la respuesta. Repetirlo a ciegas puede crear y facturar un duplicado.

  • Un trabajo completado, fallido o bloqueado por moderación no debe enviarse de nuevo automáticamente.

El script a continuación escribe el ID de la tarea en manifest.json inmediatamente después del envío. En la siguiente ejecución, consulta esa tarea existente en lugar de publicar el mismo SKU de nuevo. Si el envío termina con un estado de red desconocido y sin ID de tarea, el registro se marca como manual_check; verifique el historial de la cuenta antes de reintentarlo.

Use retroceso exponencial con jitter para reintentos seguros. Respete Retry-After cuando la API lo envíe, limite el retraso y limite el número de intentos. Los fallos repetidos deben entrar en el manifiesto, no en un bucle infinito.

Paso 7: Maneje image_generation_user_error

image_generation_user_error es una categoría, no un diagnóstico completo. Significa que la solicitud no se pudo generar debido a algo asociado con la entrada o la solicitud. Lea el código de error interno y el mensaje antes de decidir qué cambiar.

Las acciones típicas son:

Señal de error Acción probable
moderation_blocked o detalle de política Revise el prompt y cualquier imagen de referencia; no reintente sin cambios
Tamaño inválido Ejecute la validación local y corrija las dimensiones
Parámetro inválido o formato no compatible Compare la carga útil con la referencia actual del endpoint
Fallo de autenticación (401) Verifique el valor de la clave y el encabezado de autorización
Fallo de permiso o saldo (403) Verifique el acceso al modelo y el saldo; no use retroceso automático
Límite de velocidad (429) Respete Retry-After, reduzca la concurrencia, reintente con un límite
Error del servidor (500) Registre la solicitud y reintente solo cuando se entienda el riesgo de duplicados

No “arregle” los errores de moderación eliminando palabras hasta que la solicitud pase. Confirme que el producto, el uso previsto, los activos de referencia y el prompt cumplan con las políticas del proveedor.

Para un principiante, use este orden cuando falle un trabajo:

  1. Encuentre el SKU en manifest.json.

  2. Lea su campo error en lugar de enviar el producto de nuevo inmediatamente.

  3. Si el mensaje menciona un tamaño o parámetro, corrija ese valor.

  4. Si es 429, reduzca IMAGE_WORKERS y espere antes de continuar.

  5. Si es un error de política o moderación, revise el producto y el prompt manualmente.

  6. Si el estado es manual_check, verifique el historial de tareas antes de eliminar el registro; la solicitud original puede que ya exista.

Paso 8: Mantén un manifiesto de generación amigable para el equipo

El manifiesto no es una función de API que necesites configurar. Es un archivo JSON creado en tu computadora por el script. Considéralo como un registro de generación.

Para un equipo o una empresa, la carpeta de imágenes por sí sola no es suficiente. Un manifiesto conecta cada recurso con el SKU, el prompt, la configuración, el ID de tarea, el estado, la ruta de salida y el error.

Ejemplo de registro:

{
  "MUG-001": {
    "sku": "MUG-001",
    "status": "completed",
    "task_id": "prediction-id",
    "quality": "low",
    "size": "1024x1024",
    "prompt": "Crea una fotografía de producto de catálogo...",
    "files": ["generated/MUG-001-01.png"],
    "error": null
  }
}

En este registro, completed significa que la imagen ha sido generada y descargada. task_id es el número de recibo de la API. files indica al equipo dónde se guardó la imagen. Si la generación falla, error contiene la información necesaria para decidir si el SKU debe corregirse, reintentarse o revisarse.

Esto le da a un revisor un traspaso estable. También permite regenerar solo los elementos rechazados, comparar versiones de prompts y rastrear un recurso hasta la solicitud que lo creó. Para equipos más grandes, almacena los mismos campos en una base de datos y añade revisor, estado de aprobación, versión del prompt, campaña y notas de derechos.

Script completo de Python para la generación de productos en lote

El siguiente script combina el flujo de trabajo. Utiliza el endpoint actual de texto a imagen de GPT Proto v3 y la ruta de resultados de predicción, envía n=1 por SKU, usa tres workers por defecto y reanuda las tareas conocidas desde manifest.json.

El código es más largo que una demo básica de API porque realiza varias tareas de seguridad por ti:

  • Comprueba las dimensiones de la imagen antes de enviar una solicitud.

  • Guarda el ID de la tarea en cuanto la API acepta un producto.

  • Espera cada imagen sin pedirte que ejecutes comandos GET manualmente.

  • Descarga los resultados de URL y decodifica los resultados Base64.

  • Limita la cantidad de productos procesados a la vez.

  • Registra los fallos y continúa las tareas conocidas después de un reinicio.

Para tu primera ejecución, copia el script sin cambiar sus funciones. Solo necesitas proporcionar products.csv y la clave de API. La configuración cerca de la parte superior—SIZE, QUALITY, WORKERS, y POLL_TIMEOUT—se puede ajustar más tarde.

import base64
import csv
import json
import os
import random
import re
import threading
import time
from concurrent.futures import ThreadPoolExecutor, as_completed
from pathlib import Path
from urllib.parse import urlparse

import requests

API_ROOT = "https://gptproto.com/api/v3"
SUBMIT_URL = f"{API_ROOT}/openai/gpt-image-2/text-to-image"
RESULT_URL = f"{API_ROOT}/predictions/{{task_id}}/result"

CSV_PATH = Path(os.getenv("PRODUCTS_CSV", "products.csv"))
OUTPUT_DIR = Path(os.getenv("IMAGE_OUTPUT_DIR", "generated"))
MANIFEST_PATH = Path(os.getenv("IMAGE_MANIFEST", "manifest.json"))

SIZE = os.getenv("IMAGE_SIZE", "1024x1024")
QUALITY = os.getenv("IMAGE_QUALITY", "low")
WORKERS = int(os.getenv("IMAGE_WORKERS", "3"))
POLL_TIMEOUT = int(os.getenv("IMAGE_POLL_TIMEOUT", "900"))

API_KEY = os.environ["GPTPROTO_API_KEY"]
HEADERS = {
    "Authorization": f"Bearer {API_KEY}",
    "Content-Type": "application/json",
}

manifest_lock = threading.Lock()


class SubmissionStateUnknown(RuntimeError):
    """The POST may have reached the server, but no task ID was received."""


def validate_size(size):
    if size == "auto":
        return
    if not re.fullmatch(r"\d+x\d+", size.lower()):
        raise ValueError("Size must look like 1024x1024 or be 'auto'")

    width, height = map(int, size.lower().split("x"))
    pixels = width * height
    if width % 16 or height % 16:
        raise ValueError("GPT Image 2 width and height must be multiples of 16")
    if max(width, height) > 3840:
        raise ValueError("The longest edge cannot exceed 3840 pixels")
    if max(width, height) / min(width, height) > 3:
        raise ValueError("Aspect ratio cannot exceed 3:1")
    if not 655_360 <= pixels <= 8_294_400:
        raise ValueError("Total pixels are outside the supported range")


def build_prompt(row):
    return f"""Create one catalog product photograph.
Product: {row['name']}
Color: {row['color']}
Material: {row['material']}
View: {row['angle']}
Background: {row['background']}

Keep the product centered with realistic proportions and material texture.
Use soft studio lighting, a natural contact shadow, and generous crop-safe space.
No text, logos, labels, people, or extra objects.
Also avoid: {row.get('avoid', 'none')}.
Return a single product image, not a collage or contact sheet."""


def load_manifest():
    if not MANIFEST_PATH.exists():
        return {}
    return json.loads(MANIFEST_PATH.read_text(encoding="utf-8"))


manifest = load_manifest()


def save_record(sku, **changes):
    with manifest_lock:
        record = manifest.setdefault(sku, {"sku": sku})
        record.update(changes)
        temporary = MANIFEST_PATH.with_suffix(".json.tmp")
        temporary.write_text(
            json.dumps(manifest, indent=2, ensure_ascii=False),
            encoding="utf-8",
        )
        temporary.replace(MANIFEST_PATH)


def parse_json(response):
    try:
        return response.json()
    except ValueError:
        return {"raw_response": response.text[:1000]}


def api_error(response):
    payload = parse_json(response)
    return RuntimeError(
        f"HTTP {response.status_code}: {json.dumps(payload, ensure_ascii=False)}"
    )


def submit(prompt):
    payload = {
        "prompt": prompt,
        "n": 1,
        "quality": QUALITY,
        "size": SIZE,
        "enable_sync_mode": False,
        "response_format": "url",
    }

    # Retry only a definite 429 rejection. A connection failure is ambiguous:
    # the job may exist even though this client did not receive its ID.
    for attempt in range(5):
        try:
            response = requests.post(
                SUBMIT_URL,
                headers=HEADERS,
                json=payload,
                timeout=(10, 180),
            )
        except requests.RequestException as exc:
            raise SubmissionStateUnknown(str(exc)) from exc

        if response.status_code != 429:
            if response.status_code >= 400:
                raise api_error(response)
            body = parse_json(response)
            data = body.get("data", body)
            task_id = data.get("id")
            if not task_id:
                raise RuntimeError(f"Submission returned no data.id: {body}")
            return task_id

        if attempt == 4:
            raise api_error(response)
        retry_after = response.headers.get("Retry-After")
        delay = float(retry_after) if retry_after and retry_after.isdigit() else 2**attempt
        time.sleep(min(30, delay) + random.random())

    raise RuntimeError("Submission retry loop ended unexpectedly")


def get_result(task_id):
    deadline = time.monotonic() + POLL_TIMEOUT
    delay = 2.0

    while time.monotonic() < deadline:
        try:
            response = requests.get(
                RESULT_URL.format(task_id=task_id),
                headers=HEADERS,
                timeout=(10, 60),
            )
        except requests.RequestException:
            time.sleep(delay + random.random())
            delay = min(10, delay * 1.5)
            continue

        if response.status_code == 429 or response.status_code >= 500:
            retry_after = response.headers.get("Retry-After")
            wait = float(retry_after) if retry_after and retry_after.isdigit() else delay
            time.sleep(min(30, wait) + random.random())
            delay = min(10, delay * 1.5)
            continue
        if response.status_code >= 400:
            raise api_error(response)

        body = parse_json(response)
        data = body.get("data", body)
        status = str(data.get("status", "")).lower()

        if status in {"completed", "succeeded"}:
            outputs = data.get("outputs") or []
            if not outputs:
                raise RuntimeError(f"Completed task returned no outputs: {body}")
            return outputs
        if status in {"failed", "canceled", "cancelled"}:
            raise RuntimeError(json.dumps(data.get("error") or body, ensure_ascii=False))

        time.sleep(delay)
        delay = min(10, delay * 1.25)

    raise TimeoutError(f"Task {task_id} exceeded {POLL_TIMEOUT} seconds")


def filename_part(value):
    cleaned = re.sub(r"[^A-Za-z0-9._-]+", "-", value.strip())
    return cleaned.strip("-._") or "product"


def extension_from_response(response, url):
    content_type = response.headers.get("Content-Type", "").split(";", 1)[0]
    by_type = {"image/png": ".png", "image/jpeg": ".jpg", "image/webp": ".webp"}
    if content_type in by_type:
        return by_type[content_type]
    suffix = Path(urlparse(url).path).suffix.lower()
    return suffix if suffix in {".png", ".jpg", ".jpeg", ".webp"} else ".png"


def save_output(output, sku, index):
    OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
    stem = f"{filename_part(sku)}-{index:02d}"

    if isinstance(output, dict):
        output = output.get("url") or output.get("b64_json") or output.get("data")
    if not isinstance(output, str):
        raise TypeError(f"Unsupported output value: {output!r}")

    if output.startswith(("http://", "https://")):
        response = requests.get(output, timeout=(10, 180))
        response.raise_for_status()
        destination = OUTPUT_DIR / f"{stem}{extension_from_response(response, output)}"
        destination.write_bytes(response.content)
        return str(destination)

    encoded = output.split(",", 1)[1] if output.startswith("data:") else output
    destination = OUTPUT_DIR / f"{stem}.png"
    destination.write_bytes(base64.b64decode(encoded, validate=True))
    return str(destination)


def process_product(row):
    sku = row["sku"].strip()
    prompt = build_prompt(row)
    previous = manifest.get(sku, {})

    if previous.get("status") == "completed":
        return sku, "skipped: already completed"
    if previous.get("status") == "manual_check" and not previous.get("task_id"):
        return sku, "skipped: verify task history before resubmitting"
    if previous.get("status") == "failed":
        return sku, "skipped: review the recorded error before retrying"

    task_id = previous.get("task_id")

    try:
        if not task_id:
            task_id = submit(prompt)
            save_record(
                sku,
                status="submitted",
                task_id=task_id,
                quality=QUALITY,
                size=SIZE,
                prompt=prompt,
                files=[],
                error=None,
            )

        save_record(sku, status="processing")
        outputs = get_result(task_id)
        files = [save_output(value, sku, i) for i, value in enumerate(outputs, 1)]
        save_record(sku, status="completed", files=files, error=None)
        return sku, f"completed: {', '.join(files)}"

    except SubmissionStateUnknown as exc:
        save_record(
            sku,
            status="manual_check",
            task_id=None,
            quality=QUALITY,
            size=SIZE,
            prompt=prompt,
            files=[],
            error=f"Submission state unknown: {exc}",
        )
        return sku, "manual check required"
    except TimeoutError as exc:
        # Keep the task ID. The next run will poll this job instead of submitting
        # the SKU again, so a slow generation does not become a duplicate.
        save_record(sku, status="poll_timeout", task_id=task_id, error=str(exc))
        return sku, f"poll timed out; resume task {task_id} on the next run"
    except Exception as exc:
        save_record(sku, status="failed", task_id=task_id, error=str(exc))
        return sku, f"failed: {exc}"


def main():
    validate_size(SIZE)
    if WORKERS < 1:
        raise ValueError("IMAGE_WORKERS must be at least 1")

    with CSV_PATH.open(newline="", encoding="utf-8-sig") as handle:
        products = list(csv.DictReader(handle))

    required = {"sku", "name", "color", "material", "angle", "background"}
    missing = required - set(products[0]) if products else required
    if missing:
        raise ValueError(f"CSV is missing columns: {sorted(missing)}")

    skus = [row["sku"].strip() for row in products]
    if not all(skus):
        raise ValueError("Every CSV row must have a non-empty SKU")
    if len(skus) != len(set(skus)):
        raise ValueError("CSV contains duplicate SKUs; make each SKU unique before running")

    with ThreadPoolExecutor(max_workers=WORKERS) as pool:
        futures = [pool.submit(process_product, row) for row in products]
        for future in as_completed(futures):
            sku, message = future.result()
            print(f"[{sku}] {message}")


if __name__ == "__main__":
    main()

Cómo ejecutar el script: Guía para principiantes

1. Confirma los dos archivos de entrada

Abre tu gpt-image-bulk carpeta y confirma que contiene:

  • bulk_product_images.py, que contiene el código Python completo.

  • products.csv, que contiene el encabezado y al menos una fila de producto.

No guardes el script como bulk_product_images.py.txt. Si tu editor oculta las extensiones de archivo, verifica el tipo de archivo antes de continuar.

2. Abre la terminal en esa carpeta

La terminal debe estar ubicada en la misma carpeta que los dos archivos. Puedes usar cd seguido de la ruta de la carpeta, por ejemplo:

cd path/to/gpt-image-bulk

Puedes confirmar la carpeta actual con pwd en macOS/Linux o Get-Location en PowerShell.

3. Configura la clave de API e instala requests

Configura GPTPROTO_API_KEY con el comando que se muestra en la sección de configuración. Luego instala requests si aún no lo has hecho:

python -m pip install requests

4. Ejecuta el archivo

En macOS, Linux y la mayoría de las instalaciones de Python:

python bulk_product_images.py

En Windows, usa esto si tu comando de Python es py:

py bulk_product_images.py

El primer producto puede tardar en finalizar. Es posible que la terminal no se haya congelado mientras la API está generando. Cuando las tareas se completen, deberías ver líneas similares a:

[MUG-001] completed: generated/MUG-001-01.png
[BAG-014] completed: generated/BAG-014-01.png
[LAMP-207] completed: generated/LAMP-207-01.png

La carpeta ahora contendrá generated/ y manifest.json. Abre las imágenes generadas y compara cada una con la fila correspondiente del CSV. Luego abre manifest.json en un editor de texto para confirmar que cada SKU exitoso tenga "status": "completed".

5. Entiende qué sucede cuando lo ejecutas de nuevo

El script lee manifest.json antes de enviar nuevas solicitudes. Los SKU completados se omiten. Una tarea que se detuvo anteriormente durante el sondeo conserva su ID de tarea y se verifica de nuevo. Un registro marcado como manual_check o failed no se envía automáticamente.

Esto es intencional. Evita que “ejecutar el comando de nuevo” se convierta en “pagar de nuevo por cada imagen exitosa”. Si un registro fallido se puede reintentar de forma segura, elimina solo el registro de ese SKU de manifest.json después de revisar su error. No elimines todo el manifiesto a menos que quieras intencionalmente enviar el catálogo completo de nuevo.

6. Pasa de borradores a imágenes finales

Para una primera pasada, mantén IMAGE_QUALITY=low y IMAGE_WORKERS=2 o 3. Revisa los borradores y copia solo las filas de productos aprobados en un archivo nuevo llamado products-final.csv.

Para una ejecución final separada en macOS o Linux, usa nombres nuevos de salida y manifiesto para que los archivos de borrador permanezcan intactos:

PRODUCTS_CSV=products-final.csv \
IMAGE_OUTPUT_DIR=generated-final \
IMAGE_MANIFEST=manifest-final.json \
IMAGE_QUALITY=high \
python bulk_product_images.py

En Windows PowerShell, configura primero los mismos valores y luego ejecuta el script:

$env:PRODUCTS_CSV="products-final.csv"
$env:IMAGE_OUTPUT_DIR="generated-final"
$env:IMAGE_MANIFEST="manifest-final.json"
$env:IMAGE_QUALITY="high"
py bulk_product_images.py

Esto produce imágenes finales en generated-final/ mientras conserva las imágenes de borrador y el manifiesto de borrador para comparación.

Precios de GPT Image 2 para trabajos en lote

El precio de GPT Image 2 se basa en tokens, así que no existe un único “costo por imagen de producto” honesto para cada trabajo. La entrada de texto, la entrada de imagen de referencia, el tamaño de salida, la calidad y la cantidad de resultados afectan el total.

Para principiantes, “por un millón de tokens” es una unidad de facturación, no una suscripción mensual ni la obligación de comprar un millón de tokens por adelantado. Tu prompt se convierte en tokens de entrada de texto. Una foto de producto de referencia usada para edición se convierte en tokens de entrada de imagen. La imagen generada se convierte en tokens de salida de imagen. Una solicitud de texto a imagen no tiene cargo por entrada de imagen de referencia porque no se subió ninguna imagen de origen.

Al momento de escribir esto, la página del modelo GPT Proto enumera estas tarifas por millón de tokens:

Categoría de token Tarifa listada de GPT Proto Tarifa estándar de OpenAI
Entrada de texto $4.00 $5.00
Entrada de imagen $6.40 $8.00
Entrada de imagen en caché $1.60 $2.00
Salida de imagen $24.00 $30.00

Eso es un 20 % por debajo de las tarifas estándar de tokens de OpenAI listadas. Los precios pueden cambiar, así que consulta la página en vivo del modelo GPT Image 2 antes de publicar una cotización o iniciar una ejecución grande.

La referencia de precios de OpenAI también ofrece ejemplos útiles solo de salida para tamaños comunes. Una imagen de 1024x1024 se lista a aproximadamente $0.006 en baja, $0.053 en media y $0.211 en alta calidad; una imagen de 1536x1024 o 1024x1536 cuesta aproximadamente $0.005, $0.041 y $0.165 respectivamente. Los tokens de entrada son adicionales. Consulta la página actual de precios de la API de OpenAI en lugar de tratar estas cifras como permanentes.

Para una estimación del catálogo, ejecuta un piloto de 20 artículos que refleje la mezcla real de productos. Registra el gasto real, la tasa de imágenes aprobadas y la tasa de regeneración. Luego calcula:

costo estimado del catálogo =
  costo promedio de API por intento
  × intentos promedio por imagen aprobada
  × número de productos

La tasa de regeneración es fácil de pasar por alto. Un borrador barato que necesita cuatro intentos puede costar más que un trabajo de calidad media mejor controlado aprobado en el primer o segundo intento.

Cuándo usar imágenes de referencia en lugar de texto a imagen

El texto a imagen puro es adecuado cuando el producto es genérico, la imagen es conceptual o el empaque exacto no necesita coincidir con un artículo vendible. No debería ser la opción predeterminada para la representación exacta de un SKU.

Usa el endpoint de edición de imágenes de GPT Image 2 cuando necesites preservar la forma, los colores, el empaque o el lenguaje de diseño de un producto existente. GPT Image 2 acepta hasta 16 imágenes de referencia PNG, WebP o JPEG de menos de 50 MB cada una en la documentación del formato oficial, y usa automáticamente una fidelidad de entrada alta. El endpoint de formato de proveedor de GPT Proto acepta URL de imágenes o URI de datos Base64; consulta su referencia de edición de imágenes de GPT Image 2.

Los buenos conjuntos de referencia incluyen:

  • Una foto de producto limpia de frente o en tres cuartos.

  • Un segundo ángulo cuando la forma es ambigua.

  • Un primer plano para texturas de material o controles importantes.

  • Una referencia de estilo separada para iluminación y fondo, claramente identificada en el prompt.

Las imágenes de referencia mejoran el control, pero no garantizan etiquetas perfectas a nivel de píxel, texto pequeño o dimensiones exactas del producto. Un revisor humano debe comparar los recursos generados con el producto de origen antes de la publicación.

Lista de verificación de producción para uso empresarial y en equipo

Antes de pasar de un piloto a la generación masiva:

  • Fija una versión del prompt y guárdala en el manifiesto.

  • Usa un SKU inmutable como clave principal del activo.

  • Genera borradores en baja calidad; renderiza solo las composiciones aprobadas con la configuración final.

  • Mantén configurable el número de workers y redúcelo tras respuestas repetidas 429.

  • Guarda los ID de tarea antes de sondear.

  • Descarga inmediatamente cada URL de resultado temporal.

  • Separa los estados failed, manual_check y completed.

  • Nunca reintentes errores de moderación, permisos o saldo sin cambios.

  • Comprueba en los resultados de muestra la geometría, el color, las etiquetas, las sombras, el recorte y los objetos duplicados.

  • Conserva los derechos de la fuente/referencia y registra la aprobación de la revisión.

  • Mantén las claves de API en un gestor de secretos o una variable de entorno, no en un CSV ni en un repositorio.

Para el desarrollo de prompts antes de automatizar el catálogo, los ejemplos de prompts de GPT Image 2 pueden ayudar a establecer una dirección visual. Convierte la dirección elegida en campos estructurados antes de ejecutarla a escala.

Empieza con una pequeña porción del catálogo

La llamada a la API es la parte fácil. La generación masiva fiable proviene de separar un producto de una variación, limitar la concurrencia, validar las entradas, guardar los ID de predicción y hacer que cada ejecución se pueda reanudar.

Empieza con 10 a 20 SKU representativos en baja calidad. Mide las tasas de aprobación y regeneración, corrige el prompt compartido y solo entonces amplía la cola. Puedes revisar los parámetros y precios actuales en la página del modelo GPT Image 2, o crear una cuenta desde la página de inicio de GPT Proto cuando estés listo para ejecutar el script.

Preguntas frecuentes

¿Cómo creo varias imágenes de producto con la API de GPT Image 2?

Usa una solicitud por fila de producto, normalmente con n=1, y procesa esas solicitudes mediante un grupo de trabajadores controlado. Guarda cada ID de tarea devuelto y asigna su salida descargada al SKU de origen. Usa n>1 solo cuando quieras varias variaciones del mismo prompt.

¿Puedo pedirle a ChatGPT que genere varias imágenes a la vez?

Puedes pedirle a ChatGPT varias imágenes o variaciones durante una conversación, pero la interfaz es mejor para el trabajo creativo manual. Para entradas CSV, nombres de archivo estables, reintentos, estado de aprobación e integración con la tienda, usa la API.

¿Cuál es el límite de solicitudes de GPT Image 2?

Depende del proveedor, la cuenta y los límites actuales. OpenAI publica límites basados en niveles para el acceso directo, pero esos valores no deben copiarse a una cuenta de GPTProto. Empieza con dos o tres trabajadores, lee las respuestas 429 y Retry-After, y ajusta según el comportamiento observado.

¿Por qué GPT Image 2 dice que el tamaño debe ser múltiplo de 16?

El ancho y la altura personalizados deben ser divisibles exactamente entre 16. El tamaño también debe mantenerse dentro de los límites de borde, relación de aspecto y píxeles totales. Valida localmente antes de enviar la solicitud. Genera con un tamaño válido cercano y redimensiona después si una tienda requiere una dimensión no válida.

¿Cómo decodifico un resultado Base64 de GPT Image 2?

Elimina el prefijo data-URI si está presente, pasa la cadena restante a base64.b64decode y escribe los bytes resultantes en un archivo de imagen. No guardes la cadena JSON codificada directamente con una extensión de imagen.

¿La generación masiva con la API de GPT Image 2 es adecuada para un equipo?

Sí, si el flujo de trabajo incluye más que la generación. Los equipos necesitan un manifiesto, un mapeo inmutable de SKU, versiones de prompts, estado de revisión, seguimiento de costos y una regla para envíos ambiguos. Esos controles hacen prácticos los traspasos y las reejecuciones parciales.

¿Debería usar la Batch API de OpenAI para imágenes de producto?

Úsala para cargas de trabajo directas de OpenAI que puedan esperar hasta 24 horas y se ajusten a su proceso JSONL. Ofrece un grupo de límites separado y precios con descuento. Para el endpoint de GPTProto en esta guía, usa el flujo de trabajo de predicción documentado a menos que GPTProto documente explícitamente compatibilidad con la Batch API.

Artículos relacionados

Más blogs
6 generadores de imágenes con IA más baratos en 2026: costo real por imagen

6 generadores de imágenes con IA más baratos en 2026: costo real por imagen

El generador de imágenes con IA más barato depende de lo que intentes comprar. Si quieres tres o cuatro imágenes personales, una herramienta gratuita para el navegador cuesta menos que cualquier API de pago. Sin embargo, si necesitas cientos de imágenes dentro de una aplicación, cambiar los límites gratuitos y las descargas manuales no es un modelo de costos viable. Para la generación mediante API, GPT Image 1 Mini tiene el costo inicial más bajo de esta comparación, aproximadamente $0.0035 por una imagen de 1024×1024 de baja calidad , más un pequeño cargo por entrada de texto. Su factura cambia según la calidad, las dimensiones y el uso de tokens. Nano Banana 2 Lite es la opción de tarifa plana predecible más barata a $0.0202 por cada 1000 imágenes. Esa distinción importa. Una es el precio de entrada más bajo posible; la otra es más fácil de incluir en el presupuesto de una startup. Todos los precios a continuación se verificaron el 3 de septiembre de 2026, y se puede acceder a los seis modelos a través del generador de imágenes con IA de GPTProto. Genera una imagen ahora mismo

Schuyler Stacy | 2026-09-03

5 Best Midjourney API Alternatives in 2026: Real Model APIs, Not Discord Wrappers

5 Best Midjourney API Alternatives in 2026: Real Model APIs, Not Discord Wrappers

Midjourney can produce an excellent image from a loose creative brief. It is far less convenient when that image needs to sit inside an application, automated campaign system, product catalog, or customer-facing workflow. The reason is straightforward: Midjourney does not provide a standard public API for general developer use. Its current community guidelines also prohibit unauthorized automation of the service. Try Midjourney on GPTProto That leaves developers with two different routes: Use a third-party service that submits jobs to Midjourney on your behalf. Replace Midjourney with another image model that has a documented API. This guide covers the second route. The five options below are alternative image models—not Discord bots, browser automation tools, or unofficial Midjourney wrappers. The short answer: Seedream 5.0 Pro is the best overall Midjourney API alternative for polished visual concepts. Nano Banana Pro is the safer choice for complex commercial graphics, GPT Image 2 is better for exact instructions and text, FLUX.1 Kontext Max is worth considering for iterative editing, and Qwen Image Plus is the affordable option for bulk drafts. If you need Midjourney itself rather than a different model, compare the current third-party Midjourney API providers or open the Midjourney API page on GPTProto .

Tiffany Layne | 2026-09-01

Which AI Image Model Is Best for E-Commerce in 2026? 6 Models Tested on Product Photos

Which AI Image Model Is Best for E-Commerce in 2026? 6 Models Tested on Product Photos

One attractive sample tells you almost nothing about whether an AI image model is safe for e-commerce. A beautiful product photo can still contain the wrong bottle cap, a redesigned package, an invented feature, or—worse—the wrong price. We tested six image models with the same three e-commerce tasks: a nail-polish product-in-use photo, a floor lamp placed in a living room, and a wireless-earbuds ad with fixed copy. We tracked retries, product changes, physical mistakes, text accuracy, and whether the result could be published without repair. The short answer: Nano Banana Pro was the best AI image model for e-commerce overall. It was the only model that stayed near the top across all three tests. GPT Image 2 was the best for ads with exact text , while Qwen Image Plus was the lowest-cost option for bulk drafts . Midjourney produced the strongest lifestyle image, but it was the least trustworthy when the product and sales copy had to remain exact. If you only need general image-editing recommendations, see our separate guide to the best image-editing AI models . This comparison focuses specifically on e-commerce production and the cost of getting an image that is actually ready to publish. Try Nano Banana Pro Now

2026-08-27

7 Best Third-Party Midjourney API Providers in 2026: Pricing, Risks, and Verified Options

7 Best Third-Party Midjourney API Providers in 2026: Pricing, Risks, and Verified Options

Midjourney still does not document a public, self-serve API for general developer access. That has not stopped a market of third-party Midjourney API providers from offering hosted endpoints, Discord-account wrappers, and hybrid setups. The problem is not finding a list. It is finding a list that is still current. Several search results continue to recommend products that have removed Midjourney, no longer expose a verifiable product page, or cannot be checked securely. Other comparisons put a hosted request next to a bring-your-own-account wrapper as if they were equivalent. They are not. Try Midjourney on GPTProto My ranking is straightforward: GPTProto is the best overall choice for a multi-model workflow. It puts a versionless Midjourney route beside 200+ other text, image, video, and audio models under one key and shared balance. At about $0.06 per run , however, it is not the lowest listed price here. APIFrame is the strongest dedicated Midjourney workflow option. Its published range is $0.02–$0.10 per job , with four outputs per job, but its Basic plan starts at $39 per month . TTAPI is the price pick. Its listed Imagine rates start around $0.03 in Relax mode , rising to $0.05 Fast and $0.07 Turbo . One warning matters more than any ranking. Midjourney's Terms of Service , effective May 27, 2026, prohibit automated tools that access or generate through the service and prohibit reselling or redistributing access. Every provider in this list is unofficial. A working endpoint today does not remove account, policy, or service-continuity risk tomorrow.

Schuyler Stacy | 2026-08-25