Un descuento en los planes premium al registrarteConsigue tu descuento
eroq

blog/developers·8 oct 2026·7 min·por el equipo de eroq

API de GPT Image, Nano Banana y Seedream: un endpoint asíncrono

Llama a GPT Image 2.5, Nano Banana Pro, Seedream 5.0 Pro y Flux 3 con un endpoint de eroq: petición, jobs 202, polling, lotes y errores, en curl y JS.


Cinco creadores de imágenes suelen significar cinco SDK, cinco claves, cinco paneles de facturación y cinco ideas distintas de lo que es "async". En eroq es un solo endpoint donde model es lo único que cambia. GPT Image 2.5 de OpenAI, Nano Banana de Google, Seedream de ByteDance, Flux 3 de Black Forest Labs y Grok Imagine 2.0 de xAI pasan por POST /v1/images/generations, con el mismo cuerpo, el mismo contrato asíncrono y un precio fijo en créditos por toma. Es el mismo motor, precio y política que el estudio. La referencia está en /docs/images; esta es la versión práctica.

Los ids de modelo

Modelo id de model Creador Créditos por toma Render habitual
GPT Image 2.5 Sunburst gpt-image-2-5-sunburst OpenAI 31 unos 15 s
GPT Image 2.5 Flare gpt-image-2-5-flare OpenAI 31 unos 15 s
Nano Banana Pro nano-banana-pro Google 124 unos 25 s
Nano Banana 2.1 nano-banana-2-1 Google 31 unos 8 s
Seedream 5.0 Pro seedream-5-0-pro ByteDance 36 unos 20 s
Flux 3 Image flux-3-image Black Forest Labs 32 unos 20 s
Grok Imagine 2.0 grok-imagine-2-0 xAI 32 unos 15 s

Cada uno cuesta lo mismo en los cinco formatos. Flux 3 Image y Grok Imagine 2.0 se abren con el plan Hobby, los otros cinco con Creator; por debajo, la llamada responde 403 plan_required y no cobra nada.

Envía un render

curl https://eroq.ai/v1/images/generations \
  -H "Authorization: Bearer $EROQ_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "nano-banana-pro",
    "prompt": "An aerial view of a harbour city at blue hour, lit windows and light trails along the quays, layered haze between the towers, crisp architectural detail, cinematic wide shot",
    "aspect": "16:9",
    "batch": 2
  }'

La llamada cobra el lote y responde 202 al instante, con un job por toma:

{
  "object": "image.generation",
  "jobs": [
    { "id": "b7e6c2d4-…", "poll": "/v1/images/generations/b7e6c2d4-…" },
    { "id": "4f19a0e3-…", "poll": "/v1/images/generations/4f19a0e3-…" }
  ],
  "created": 1791450000,
  "model": "nano-banana-pro",
  "failed": 0,
  "usage": { "credits_spent": 248, "credits_remaining": 752 }
}

Los campos que importan en estos motores:

  • model: envíalo siempre. Si se omite, usa eroq-uncensored por defecto; un id desconocido responde 400 invalid_body con la lista de los válidos.
  • prompt: el brief completo. Pon la rotulación entre comillas dobles.
  • aspect: 1:1 (el valor por defecto), 3:4, 4:3, 9:16 o 16:9, renderizado de forma nativa.
  • batch: de 1 a 4 tomas, cobradas por adelantado. failed cuenta las tomas que no pudieron empezar, ya reembolsadas.
  • store: true para una URL pública permanente, a 2 créditos por cada 10 MB iniciados además.
  • folder_id: deja el render directamente en una de tus carpetas de la Biblioteca.

Lo que estos motores ignoran

Los motores de arriba son solo de texto a imagen, y algunos campos no hacen nada en ellos:

  • references se descartan. Para un rostro o producto que debe coincidir con fotos, envía la petición a eroq-one (hasta diez imágenes) o a eroq-krea2 (hasta cuatro).
  • negative_prompt y cfg_scale pasan la validación pero estos motores no los usan. Describe en el prompt el resultado que quieres.
  • edit: true responde 400 edit_unsupported, y aspect: "retain" responde 400 retain_requires_reference. Ambas son operaciones sobre una imagen; envíalas a Eroq One.

Haz polling hasta que el job sea terminal

const BASE = 'https://eroq.ai/v1'
const headers = {
  Authorization: `Bearer ${process.env.EROQ_API_KEY}`,
  'Content-Type': 'application/json',
}
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms))

export async function generate(body) {
  const res = await fetch(`${BASE}/images/generations`, { method: 'POST', headers, body: JSON.stringify(body) })
  const json = await res.json()
  if (!res.ok) throw Object.assign(new Error(json.error.message), { status: res.status, code: json.error.code })
  // One job per take: each succeeds, or fails and refunds, on its own
  return Promise.all(json.jobs.map(job => waitFor(job.id)))
}

async function waitFor(id, { every = 2500, timeout = 11 * 60_000 } = {}) {
  const started = Date.now()
  while (Date.now() - started < timeout) {
    const res = await fetch(`${BASE}/images/generations/${id}`, { headers })
    const job = await res.json()
    if (!res.ok) return { id, error: job.error?.code ?? `http_${res.status}` }
    if (job.status === 'succeeded') {
      const [image] = job.data
      return { id, url: image.url, libraryId: image.library_id, ms: job.generation_ms }
    }
    if (job.status === 'failed') return { id, error: job.error.code } // credits already back
    await sleep(every) // processing: phase is "queued" or "generating"
  }
  return { id, error: 'client_timeout' }
}

El polling es gratis, y cada pocos segundos es la cadencia prevista. Sigue hasta que status sea succeeded o failed. Los webhooks image.generation.succeeded y image.generation.failed son un aviso adicional, no un reemplazo; la guía de la API asíncrona muestra cómo verificar su firma.

Si todo sale bien, data[0].url es una URL firmada que dura 30 minutos. Los ids de job viven 24 horas, y volver a pedir el job (o la creación, por library_id) genera una URL nueva. Envía store: true cuando tus usuarios necesiten una dirección que no cambie nunca. generation_ms es el tiempo real del render, y un job que sigue procesándose pasado el plazo de 10 minutos del servidor se marca como fallido con generation_timeout y se reembolsa, por eso el timeout del cliente de arriba queda justo después.

Lotes, en breve

Un lote es una llamada, un prompt y hasta cuatro tomas. Cada toma es su propio job, así que haz polling de todos en paralelo y quédate con la que salga mejor; una toma que falla se reembolsa sola sin afectar a las demás. Además, un lote cuenta como una sola llamada para el límite de frecuencia por minuto. Para cuatro prompts distintos, envía cuatro llamadas.

Errores

Estado code Qué pasó Qué hacer
400 invalid_body Un campo no pasó la validación Corrige el cuerpo; el mensaje nombra el campo
400 content_blocked Rechazado por la política de uso aceptable, antes de cualquier cobro Cambia el prompt, no lo reintentes tal cual
400 edit_unsupported Una operación sobre una imagen enviada a un motor solo de texto Usa eroq-one
402 insufficient_credits Saldo por debajo de precio × lote Recarga; nunca reintentes en bucle
403 plan_required El modelo requiere un plan superior, antes de cualquier cobro Mejora el plan o elige un modelo que tu plan incluya
429 rate_limit_exceeded Demasiadas llamadas este minuto con esta clave Espera el Retry-After
502 generation_failed El render no pudo empezar No se cobra; reintenta
503 model_unavailable El motor está fuera de línea en este despliegue No se cobra; elige otro id
404 not_found Id de job desconocido, o con más de 24 horas Guarda los ids y las imágenes a tiempo

Un job que termina en failed lleva un error.code content_blocked (el proveedor lo rechazó), generation_failed o generation_timeout, y sus créditos ya están de vuelta en el monedero. Los planes elevan el límite por minuto de cada clave: 2× en Creator, 4× en Studio.

Enruta según el trabajo

const ENGINE = {
  text: 'gpt-image-2-5-sunburst', // posters, packaging, UI mockups
  keyArt: 'gpt-image-2-5-flare', // dramatic, cinematic frames
  detail: 'seedream-5-0-pro', // texture, architecture, landscapes
  hero: 'nano-banana-pro', // the final frame, top fidelity
  draft: 'nano-banana-2-1', // fast iteration
  explore: 'grok-imagine-2-0', // many directions, characterful
  daylight: 'flux-3-image', // natural light and typography
}

model: "auto" deja que el router de eroq elija entre los motores que tu plan desbloquea y cobra el que eligió, indicado en model. Puede ser Nano Banana Pro a 124 créditos / imagen, así que nombra el modelo siempre que el costo importe. La aritmética por toma está en lo que cuestan las imágenes con IA en 2026, y los mejores modelos de imagen con IA de 2026 explica las decisiones de enrutamiento.

MCP y CLI

Los mismos modelos están detrás de la herramienta generate_image del servidor MCP, que espera el render y devuelve la imagen (con get_image_status para los lentos), y detrás de la CLI: eroq image "a foggy harbour at dawn" -m seedream-5-0-pro --aspect 16:9 -o harbour.webp. La configuración de ambos está en /docs/mcp.

Preguntas frecuentes

¿Necesito claves de API de OpenAI, Google o ByteDance?

No. Una clave de eroq cubre todos los modelos, y todo se cobra en créditos de eroq desde un solo monedero.

¿Puedo enviar una imagen de referencia a Nano Banana Pro o GPT Image 2.5 a través de eroq?

No. En eroq son motores de texto a imagen y las referencias se descartan. Para el trabajo guiado por imágenes, usa eroq-one, que lee hasta diez referencias y edita según instrucciones.

¿Cuánto dura la URL de la imagen?

La URL firmada en data dura 30 minutos y el job 24 horas. Vuelve a pedirlo para obtener una URL nueva, o envía con store: true para una pública y permanente.

¿Los renders fallidos o bloqueados cuestan créditos?

No. Un prompt rechazado por la política se responde antes de cualquier cobro, y un job que falla o es rechazado por el proveedor se reembolsa solo.

Consigue una clave, elige un id de modelo y envía una petición a /docs/images.

Etiquetasimage-apigpt-imagenano-bananaseedreamflux-3async

Hazlo en eroq

Constrúyelo sobre la API

Una clave de API se obtiene en un minuto. Los mismos motores, los mismos precios, trabajos asíncronos y webhooks.

40 créditos gratis · sin tarjeta · modo privado en todas las cuentas