04Cómo empezar
Solicitudes en el formato de la API de GeminiNuevo
Los modelos Nano Banana aceptan solicitudes en el formato de la API de Gemini de Google: las mismas rutas, el mismo cuerpo de solicitud y la misma forma de respuesta. Solo cambian la dirección y la clave.
Qué es y qué rutas hay
Es una ruta aparte para los modelos Nano Banana. Acepta solicitudes en el formato de la API de Gemini de Google en la raíz del host https:// y las pasa por el mismo procesamiento de solicitudes que la API normal (/api/): las validaciones, los precios, los límites y el saldo son los mismos.
| Ruta | Qué hace |
|---|---|
POST /v1beta/ | Crea una imagen y devuelve una sola respuesta. La ruta recomendada. |
POST /v1beta/ | Lo mismo, pero con la respuesta en flujo. Con ?alt=sse llegan eventos SSE; sin él, el array JSON de Google con una sola respuesta. |
GET /v1beta/ | La lista de modelos que puede llamar tu clave. |
GET /v1beta/ | Un modelo. |
Bajo /v1 solo funcionan dos rutas POST: POST /v1/ y POST /v1/. Las demás rutas de /v1 responden como una ruta desconocida.
Clave
La clave de Rendergrid se puede pasar de tres maneras: en el encabezado x-goog-api-key, en el encabezado Authorization: Bearer o en el parámetro de la dirección ?key=. El servidor toma la primera que no esté vacía en este orden. La clave no llega a nuestros registros.
Una clave revocada, caducada, desactivada o bloqueada se comporta como en la API normal: consulta “Claves y acceso”.
Ejemplos
En los ejemplos la clave se lee de la variable de entorno RENDERGRID_API_KEY. La imagen llega en la respuesta en base64, en el campo inlineData.data.
Para Python y Node.js se usa la biblioteca Google Gen AI SDK: pip install google-genai y npm install @google/genai. El cliente se crea con nuestra dirección https:// y con tu clave de Rendergrid. El ejemplo de Node.js es un módulo ES (un archivo .mjs): en él await va en el nivel superior.
Una respuesta — generateContent:
curl --fail-with-body --max-time 620 \ -X POST "https://api.rendergrid.io/ v1beta/ models/ gemini-3-pro-image:generateContent" \ -H "x-goog-api-key: $RENDERGRID_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "contents": [{"parts": [{"text": "A ceramic mug on marble, soft window light"}]}], "generationConfig": { "responseModalities": ["IMAGE"], "imageConfig": {"aspectRatio": "1:1", "imageSize": "1K"} } }' \ -o response.json
import os from google import genai from google.genai import types client = genai.Client( api_key=os.environ["RENDERGRID_API_KEY"], http_options=types.HttpOptions( base_url="https://api.rendergrid.io", timeout=620_000, ), ) response = client.models.generate_content( model="gemini-3-pro-image", contents="A ceramic mug on marble, soft window light", config=types.GenerateContentConfig( response_modalities=["IMAGE"], image_config=types.ImageConfig(aspect_ratio="1:1", image_size="1K"), ), ) for part in response.parts: if part.inline_data: extension = part.inline_data.mime_type.removeprefix("image/") with open("result." + extension, "wb") as file: file.write(part.inline_data.data)
import { writeFileSync } from "node:fs"; import { GoogleGenAI } from "@google/genai"; const ai = new GoogleGenAI({ apiKey: process.env.RENDERGRID_API_KEY, httpOptions: { baseUrl: "https://api.rendergrid.io", timeout: 620_000 }, }); const response = await ai.models.generateContent({ model: "gemini-3-pro-image", contents: "A ceramic mug on marble, soft window light", config: { responseModalities: ["IMAGE"], imageConfig: { aspectRatio: "1:1", imageSize: "1K" }, }, }); for (const part of response.candidates[0].content.parts) { if (part.inlineData) { const extension = part.inlineData.mimeType.replace("image/", ""); writeFileSync(`result.${extension}`, Buffer.from(part.inlineData.data, "base64")); } }
En Python, para las generaciones largas recomendamos generate_content, sin flujo.
Un flujo — streamGenerateContent con el parámetro alt=sse (curl y Node.js):
curl --fail-with-body --no-buffer --max-time 620 \ -X POST "https://api.rendergrid.io/ v1beta/ models/ gemini-3-pro-image:streamGenerateContent?alt=sse" \ -H "x-goog-api-key: $RENDERGRID_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "contents": [{"parts": [{"text": "A ceramic mug on marble, soft window light"}]}], "generationConfig": { "responseModalities": ["IMAGE"], "imageConfig": {"aspectRatio": "1:1", "imageSize": "1K"} } }'
import { writeFileSync } from "node:fs"; import { GoogleGenAI } from "@google/genai"; const ai = new GoogleGenAI({ apiKey: process.env.RENDERGRID_API_KEY, httpOptions: { baseUrl: "https://api.rendergrid.io", timeout: 620_000 }, }); const stream = await ai.models.generateContentStream({ model: "gemini-3-pro-image", contents: "A ceramic mug on marble, soft window light", config: { responseModalities: ["IMAGE"], imageConfig: { aspectRatio: "1:1", imageSize: "1K" }, }, }); for await (const chunk of stream) { for (const part of chunk.candidates?.[0]?.content?.parts ?? []) { if (part.inlineData) { const extension = part.inlineData.mimeType.replace("image/", ""); writeFileSync(`result.${extension}`, Buffer.from(part.inlineData.data, "base64")); } } }
El tiempo de espera del cliente en los ejemplos es de 620 segundos, según la regla de la sección “Espera de la respuesta”.
Las demás rutas. La lista de modelos y un modelo:
curl --fail-with-body --max-time 30 \ "https://api.rendergrid.io/ v1beta/ models" \ -H "x-goog-api-key: $RENDERGRID_API_KEY"
curl --fail-with-body --max-time 30 \ "https://api.rendergrid.io/ v1beta/ models/ gemini-3-pro-image" \ -H "x-goog-api-key: $RENDERGRID_API_KEY"
Bajo /v1 el cuerpo de la solicitud está en el archivo request.json: es el cuerpo del primer ejemplo.
curl --fail-with-body --max-time 620 \ -X POST "https://api.rendergrid.io/ v1/ models/ gemini-3-pro-image:generateContent" \ -H "x-goog-api-key: $RENDERGRID_API_KEY" \ -H "Content-Type: application/json" \ -d @request.json \ -o response.json
curl --fail-with-body --no-buffer --max-time 620 \ -X POST "https://api.rendergrid.io/ v1/ models/ gemini-3-pro-image:streamGenerateContent?alt=sse" \ -H "x-goog-api-key: $RENDERGRID_API_KEY" \ -H "Content-Type: application/json" \ -d @request.json
Modelos
Se pueden llamar los identificadores de la tabla. Los identificadores de Google y los nuestros llevan al mismo modelo de Rendergrid; el precio y el saldo son los de ese modelo en la API normal.
| Identificador en la solicitud | Modelo de Rendergrid |
|---|---|
gemini-3-pro-image | nano-banana-pro |
gemini-3.1-flash-image | nano-banana-2 |
gemini-2.5-flash-image | nano-banana-flash |
gemini-3-pro-image-preview | nano-banana-pro |
gemini-3.1-flash-image-preview | nano-banana-2 |
nano-banana-pro | nano-banana-pro |
nano-banana-2 | nano-banana-2 |
nano-banana-2.1 | nano-banana-2.1 |
nano-banana-flash | nano-banana-flash |
nano-banana-2-lite | nano-banana-2-lite |
GET /v1beta/ muestra solo lo que puede llamar tu clave: primero el identificador de Google, luego el nuestro. Las variantes con -preview se aceptan, pero no figuran en la lista.
Si la cuenta no tiene permiso para un modelo, la respuesta es 403. Un modelo desconocido o no abierto da 404. En esta ruta no hay otros modelos: los modelos GPT responden 404.
Solicitud
El cuerpo de la solicitud es el cuerpo de generateContent de Google. Esto es lo que se tiene en cuenta:
- Partes. Una parte es
text,inlineData(mimeTypeydataen base64) ofileData(fileUriy, si se quiere,mimeType). La direcciónfileUrisolo puede ser http o https; se comprueba igual que las direcciones de las imágenes de referencia en la API normal (consulta “Imágenes de referencia”). - Texto del prompt. Son las partes de texto del último turno del usuario, unidas con un salto de línea; antes de ellas, separado por una línea vacía, va el texto de
systemInstruction. - Imágenes de referencia. Las imágenes de referencia son todas las imágenes de todos los turnos, tanto del usuario como del modelo, por orden. Las imágenes insertadas solo pueden ser PNG, JPEG y WebP; cada una se sube una vez y se pasa después por enlace, como cualquier carga. No se pueden enviar juntas en una misma solicitud imágenes insertadas y
fileUri: la respuesta es400. - Formato del encuadre.
generationConfig.imageConfig.aspectRatio. Los valores admitidos son los mismos que los deaspect_ratioen la API normal (consulta “Modelos y parámetros”). Sin él:autosi la solicitud lleva imágenes de referencia y1:1si no las lleva;nano-banana-2-liteno tiene el modoautoy su encuadre sale1:1. - Tamaño.
generationConfig.imageConfig.imageSize:1K,2Ko4K. Sin él:1K, y no2Kcomo en la API normal. Un tamaño que el modelo no tiene (consulta la tabla de la sección “Respuesta”) se rechaza al aceptar la solicitud, como en la API normal:400. - Formato del archivo.
generationConfig.imageConfig.imageOutputOptions.mimeType:image/pngoimage/jpeg. Sin él, el archivo llega en el formato propio del modelo, sin recodificar. Cualquier otro valor da400. - Variantes y modalidades.
responseModalitiesdebe contenerIMAGE(soloTEXTda400);candidateCountsolo puede ser 1.
{
"contents": [
{
"role": "user",
"parts": [
{ "text": "Put this mug on a marble table, soft window light" },
{ "fileData": { "mimeType": "image/png", "fileUri": "https:// example.com/ mug.png" } }
]
}
],
"generationConfig": {
"responseModalities": ["IMAGE"],
"imageConfig": { "aspectRatio": "4:5", "imageSize": "2K" }
}
}Estos campos se aceptan y se ignoran: tools, safetySettings, thinkingConfig, temperature, topP, topK, seed, thoughtSignature, cachedContent, labels. No afectan al resultado y no provocan errores.
Límites. El texto del prompt: como máximo 20 000 caracteres; las imágenes de referencia: como máximo 14; una imagen insertada: como máximo 25 MiB una vez decodificada, como la carga de un archivo (consulta “Imágenes de referencia”); el cuerpo de la solicitud: como máximo 64 MiB. Pasarse da 400 INVALID_ARGUMENT y, para el cuerpo, 413 INVALID_ARGUMENT. Un cuerpo de ese tamaño puede ser detenido por el servidor de entrada antes de aceptar la solicitud: entonces la respuesta es su propia página, y no un sobre de Google.
Respuesta
Una respuesta correcta es el GenerateContentResponse de Google: la imagen está en candidates[0].content.parts[0].inlineData y finishReason es STOP. El tipo de la imagen, mimeType, se determina por los propios bytes del archivo.
{
"candidates": [
{
"content": {
"role": "model",
"parts": [{ "inlineData": { "mimeType": "image/png", "data": "iVBORw0KGgo…" } }]
},
"finishReason": "STOP",
"index": 0
}
],
"usageMetadata": {
"promptTokenCount": 11,
"candidatesTokenCount": 1120,
"totalTokenCount": 1131,
"candidatesTokensDetails": [{ "modality": "IMAGE", "tokenCount": 1120 }]
},
"modelVersion": "gemini-3-pro-image",
"responseId": "5b0214ae-9e39-460a-820a-f6ae3e24400b"
}modelVersion es el identificador del modelo de tu solicitud, con -preview si lo indicaste. responseId es el id de la solicitud, el mismo que va en el encabezado x-rendergrid-request-id. Si no se llegó a aceptar la solicitud en 90 segundos y la respuesta 200 salió antes de que existiera el id, el encabezado no está y el id figura solo en responseId.
usageMetadata: promptTokenCount es una estimación, no un recuento de tokens del modelo: la longitud de la solicitud en caracteres dividida entre 4 y redondeada hacia arriba. candidatesTokenCount y totalTokenCount se calculan con la tabla de abajo; candidatesTokensDetails lleva los mismos tokens de la imagen. Los clientes de Gemini necesitan los tokens para su propia contabilidad; el precio de la solicitud no depende de ellos. Un guion en la tabla significa que el modelo no tiene ese tamaño.
| Modelo | 1K | 2K | 4K |
|---|---|---|---|
nano-banana-pro | 1120 | 1120 | 2000 |
nano-banana-2 | 1120 | 1680 | 2520 |
nano-banana-2.1 | 1120 | 1680 | 2520 |
nano-banana-flash | 1290 | — | — |
nano-banana-2-lite | 1120 | — | — |
Espera de la respuesta
Una imagen no se crea al instante y la conexión debe mantenerse abierta. Los plazos:
| Qué | Valor |
|---|---|
| El servidor calla hasta el primer byte | 90 segundos |
| La señal de “sigue vivo” a partir de entonces | cada 15 segundos |
| El límite de espera | 600 segundos desde que se acepta la solicitud |
- Durante los primeros 90 segundos el servidor no envía nada: una respuesta que esté lista antes llega con su propio código HTTP, y un error también.
- Si a los 90 segundos todavía no hay imagen, el servidor responde
200y envía un keep-alive cada 15 segundos — señal de que la solicitud sigue viva — y después la respuesta. - Después de un
200así, el error llega en el cuerpo: como un objeto de error de Google, y en SSE como un eventodata:. - El límite son 600 segundos desde que se acepta la solicitud: entonces llega en el cuerpo
504 DEADLINE_EXCEEDEDcon el textoGeneration timed out., y el importe se devuelve. Esta ruta nunca responde con los códigos HTTP 502 ni 504; una generación interrumpida por tiempo antes del200responde503 UNAVAILABLE. - Fija en el cliente un tiempo de espera de lectura de más de 90 segundos y un tiempo total de al menos 610: el límite del servidor es de 600 segundos y la respuesta de error por ese límite debe tener tiempo de llegar. Una solicitud cortada por el cliente se cobra.
Dinero
- El importe se cobra al aceptar la solicitud, antes de empezar el trabajo — como en una solicitud idéntica a la API normal y al mismo precio.
- La imagen se paga en cuanto se crea — como en Google.
- Si la conexión se corta por tu parte — mientras la solicitud esperaba o mientras llegaba la respuesta —, el cargo se mantiene: la imagen se crea hasta el final y está disponible por el id de la solicitud (
GET /api/) y en el historial de la cuenta.public/ v1/ creations/ {id} - Si la imagen no se entregó por culpa nuestra — venció el límite de 600 segundos, la respuesta se cortó de nuestro lado o el servidor se estaba deteniendo —, el importe se devuelve al saldo automáticamente, una sola vez.
- Una solicitud fallida y un rechazo de moderación son gratuitos, como en la API normal: el importe se devuelve al saldo.
- Repetir una solicitud es una solicitud nueva y un cargo nuevo: en esta ruta no hay clave de idempotencia.
- Si el saldo no alcanza, la respuesta es
429 RESOURCE_EXHAUSTED(en la API normal,402).
Errores
El error llega en el sobre de Google: error.code, error.message y error.status.
{
"error": {
"code": 404,
"message": "The requested model was not found.",
"status": "NOT_FOUND"
}
}| Código HTTP | Estado de Google | Cuándo |
|---|---|---|
400 | INVALID_ARGUMENT | El cuerpo, los campos o las imágenes de referencia no pasaron la validación (consulta “Solicitud”), o la solicitud se rechazó al aceptarla, como en la API normal. |
401 | UNAUTHENTICATED | No hay clave o no es válida; el texto es el de la API normal. |
403 | PERMISSION_DENIED | La cuenta está bloqueada, la clave no tiene permiso para este modelo o la ruta está cerrada para la cuenta. |
404 | NOT_FOUND | Un modelo o una ruta desconocidos; en esta ruta no hay modelos GPT. |
413 | INVALID_ARGUMENT | El cuerpo de la solicitud supera los 64 MiB. |
429 | RESOURCE_EXHAUSTED | El límite de frecuencia, el límite de trabajos en curso, un bloqueo por errores de acceso o falta de dinero en el saldo; el texto de la respuesta dice cuál. La pausa antes de reintentar va en el encabezado Retry-After. |
500 | INTERNAL | Un error interno o una generación fallida; el dinero de un fallo se devuelve. |
503 | UNAVAILABLE | El servicio no está disponible temporalmente, la generación no terminó a tiempo antes de la respuesta 200, o las imágenes insertadas no llegaron a subirse. En este último caso llega Retry-After: 5 y no se cobra nada. |
504 | DEADLINE_EXCEEDED | Solo en el cuerpo de la respuesta: después del 200, cuando se alcanza el límite de 600 segundos. El importe se devuelve. |
Esta ruta no envía 502 ni 504 como código HTTP. El límite de 600 segundos llega como 504 dentro del cuerpo de una respuesta que ya empezó con 200:
{
"error": {
"code": 504,
"message": "Generation timed out.",
"status": "DEADLINE_EXCEEDED"
}
}Los textos de los errores que se dan al aceptar la solicitud (401, 429, rechazos de validación) son los de la API normal: consulta “Códigos de error”.
Rechazos de moderación
Un rechazo de moderación llega como 200, como en Google:
finishReason: "IMAGE_SAFETY"y ninguna parte — el modelo se negó por el resultado;promptFeedback.blockReason: "SAFETY"— rechazo por la solicitud o por una imagen de referencia;finishReason: "NO_IMAGE"— no hay imagen.
{
"candidates": [{ "finishReason": "IMAGE_SAFETY", "index": 0 }],
"usageMetadata": { "promptTokenCount": 11, "totalTokenCount": 11 },
"modelVersion": "gemini-3-pro-image",
"responseId": "5b0214ae-9e39-460a-820a-f6ae3e24400b"
}Estas respuestas son gratuitas, como en la API normal: el importe se devuelve al saldo.
Límites
Rigen los límites de la API normal: la frecuencia de solicitudes, la protección contra ráfagas y el número de trabajos en curso (consulta “Límites”). Cada imagen insertada es una carga aparte: una unidad de frecuencia y una unidad del límite de cargas. El lugar en el límite de trabajos está ocupado mientras dura la generación, no mientras la conexión está abierta.