> ## Documentation Index
> Fetch the complete documentation index at: https://docs.bettertoken.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# API Nano Banana: generación, referencias y edición

> Conecta Nano Banana Pro, 2 y 2.1 a BetterToken: autenticación, resoluciones, Python, imágenes de referencia, guardado de resultados, edición sucesiva y límites.

Usa el mismo endpoint nativo de Gemini para generar con texto, editar referencias y continuar editando. Los ejemplos usan `nano-banana-2.1`; cambia el modelo en la URL o SDK para usar los otros dos.

* [API de texto a imagen de Gemini](/es/api-reference/gemini-images-generations)
* [API de edición de imágenes de Gemini](/es/api-reference/gemini-images-edits)

<Warning>
  BetterToken API no admite generación con búsqueda, incluidos Google Web Search e Image Search grounding. No añadas herramientas de búsqueda.
</Warning>

## Datos de conexión

| Campo | Valor |
| - | - |
| Base URL | `https://bettertoken.ai` |
| Solicitud | `POST /v1beta/models/{model}:generateContent` |
| Autenticación | `x-goog-api-key: YOUR_API_KEY` o `Authorization: Bearer YOUR_API_KEY`. Elige uno. |
| Cuerpo | `application/json` |
| Model IDs | `nano-banana-pro`, `nano-banana-2`, `nano-banana-2.1` |

<a href={"https://bettertoken.ai/register"}>Crear una BetterToken API Key</a> · <a href={"https://bettertoken.ai/pricing"}>catálogo de modelos</a> · [Presentación de Nano Banana](/es/model-updates/nano-banana)

<Warning>
  Estos tres modelos no admiten `POST /v1/images/generations` ni `POST /v1/images/edits`: devuelven `400 unsupported_endpoint`. No reutilices los ejemplos GPT Image de multipart, `image[]` o `data[].b64_json`.
</Warning>

## Genera la primera imagen

Define `BETTERTOKEN_API_KEY` en el terminal y ejecuta la solicitud. Espera a la generación y guarda todo el JSON en `response.json`.

<Tabs>
  <Tab title="macOS / Linux">
    ```bash theme={null}
    export BETTERTOKEN_API_KEY="YOUR_API_KEY"

    curl --fail-with-body --max-time 650 \
      "https://bettertoken.ai/v1beta/models/nano-banana-2.1:generateContent" \
      -H "x-goog-api-key: $BETTERTOKEN_API_KEY" \
      -H "Content-Type: application/json" \
      -H "X-BetterToken-Max-Images: 1" \
      -d '{
        "contents": [{
          "role": "user",
          "parts": [{"text": "Una foto de estudio de una taza de cerámica azul sobre una mesa blanca."}]
        }],
        "generationConfig": {
          "responseModalities": ["IMAGE"],
          "candidateCount": 1,
          "imageConfig": {"imageSize": "1K", "aspectRatio": "1:1"}
        }
      }' -o response.json
    ```
  </Tab>

  <Tab title="Windows PowerShell">
    ```powershell theme={null}
    $env:BETTERTOKEN_API_KEY = "YOUR_API_KEY"
    $headers = @{
        "x-goog-api-key" = $env:BETTERTOKEN_API_KEY
        "X-BetterToken-Max-Images" = "1"
    }
    $body = @{
        contents = @(@{
            role = "user"
            parts = @(@{ text = "Una foto de estudio de una taza de cerámica azul sobre una mesa blanca." })
        })
        generationConfig = @{
            responseModalities = @("IMAGE")
            candidateCount = 1
            imageConfig = @{ imageSize = "1K"; aspectRatio = "1:1" }
        }
    } | ConvertTo-Json -Depth 10
    Invoke-WebRequest `
        -Uri "https://bettertoken.ai/v1beta/models/nano-banana-2.1:generateContent" `
        -Method Post `
        -Headers $headers `
        -ContentType "application/json" `
        -Body ([System.Text.Encoding]::UTF8.GetBytes($body)) `
        -TimeoutSec 650 `
        -OutFile "response.json"
    ```
  </Tab>
</Tabs>

Cambia `imageSize` a `2K` o `4K` para mayor resolución. Empieza con `1K`, `1:1` y una imagen. No añadas reintentos automáticos con `--retry`.

## Parámetros habituales

Para fotos de producto, describe el objeto, material, iluminación y fondo. Para carteles, indica el texto exacto y su disposición. Para editar, explica qué debe cambiar y qué debe mantenerse.

HTTP nativo usa estos campos; Python SDK usa snake\_case. Coloca los ajustes de imagen dentro de `generationConfig.imageConfig`.

| Campo | Valor |
| - | - |
| `contents[].parts[].text` | Describe objeto, fondo, estilo y cambios. |
| `contents[].parts[].inlineData` | `mimeType` y base64 del archivo original de referencia, no su URL. |
| `generationConfig.responseModalities` | Establece `["IMAGE"]` explícitamente. |
| `generationConfig.imageConfig.imageSize` | `1K`, `2K` o `4K`, distingue mayúsculas; por defecto `1K`. |
| `generationConfig.imageConfig.aspectRatio` | Elige la proporción de tu modelo en la tabla siguiente; los ejemplos usan `1:1`. |
| `generationConfig.candidateCount` | Omite o usa entero `1`; no envíes `n` en la raíz nativa. |
| `X-BetterToken-Max-Images` | Cabecera opcional: entero `1` a `4`, defecto `1`. Máximo de imágenes cuya entrega y cobro autorizas. |

<span id="aspect-ratios" />

### Proporciones admitidas por Google

Nano Banana 2 y 2.1 admiten 14 proporciones; Pro admite 10. Empieza con `1:1` en la primera solicitud.

| Modelo | Resolución | Proporciones |
| - | - | - |
| `nano-banana-pro` | `1K`, `2K`, `4K` | `1:1`, `2:3`, `3:2`, `3:4`, `4:3`, `4:5`, `5:4`, `9:16`, `16:9`, `21:9` |
| `nano-banana-2` | `1K`, `2K`, `4K` | `1:1`, `1:4`, `1:8`, `2:3`, `3:2`, `3:4`, `4:1`, `4:3`, `4:5`, `5:4`, `8:1`, `9:16`, `16:9`, `21:9` |
| `nano-banana-2.1` | `1K`, `2K`, `4K` | `1:1`, `1:4`, `1:8`, `2:3`, `3:2`, `3:4`, `4:1`, `4:3`, `4:5`, `5:4`, `8:1`, `9:16`, `16:9`, `21:9` |

[Referencia de proporciones y tamaños de Google](https://ai.google.dev/gemini-api/docs/image-generation#aspect-ratios-and-image-size)

El máximo no garantiza una cantidad. Aunque pidas varias, el modelo puede devolver una. Para una cantidad fija, envía solicitudes separadas.

## Lee y guarda la imagen

Las imágenes están en `candidates[].content.parts[].inlineData`. La estructura inferior usa el marcador `BASE64_IMAGE_DATA`. Recorre todas las partes finales y omite las marcadas `thought: true`.

```json theme={null}
{
  "candidates": [{
    "content": {
      "role": "model",
      "parts": [{
        "inlineData": {
          "mimeType": "image/jpeg",
          "data": "BASE64_IMAGE_DATA"
        }
      }]
    },
    "finishReason": "STOP"
  }]
}
```

Este Python guarda el resultado de curl. Elige extensión según `mimeType`; puede ser JPEG, no siempre `.png`.

```python theme={null}
import base64
import json
from pathlib import Path

suffixes = {"image/png": ".png", "image/jpeg": ".jpg"}
result = json.loads(Path("response.json").read_text())
saved = 0
for candidate in result.get("candidates", []):
    for part in candidate.get("content", {}).get("parts", []):
        if part.get("thought"):
            continue
        blob = part.get("inlineData")
        if not blob:
            continue
        suffix = suffixes.get(blob["mimeType"])
        if suffix is None:
            raise ValueError("Unexpected image MIME type: " + blob["mimeType"])
        saved += 1
        Path(f"generated-{saved}{suffix}").write_bytes(
            base64.b64decode(blob["data"], validate=True)
        )
if saved == 0:
    raise RuntimeError("No final image returned; inspect response.json.")
```

## Ejemplo completo con Python SDK

Usa Python 3.10 o posterior y `google-genai==2.30.1`. Instálalo en el entorno virtual del proyecto; versiones anteriores pueden omitir `image_size`.

```bash theme={null}
python -m pip install "google-genai==2.30.1"
```

El Base URL del SDK es la raíz del dominio, sin `/v1` ni `/v1beta`. `timeout` está en milisegundos y `attempts=1` desactiva reintentos. `save_images` guarda todas las imágenes finales.

```python theme={null}
import os
from pathlib import Path
from google import genai
from google.genai import types

MODEL = "nano-banana-2.1"
client = genai.Client(
    vertexai=False,
    api_key=os.environ["BETTERTOKEN_API_KEY"],
    http_options=types.HttpOptions(
        base_url="https://bettertoken.ai",
        api_version="v1beta",
        timeout=650_000,
        retry_options=types.HttpRetryOptions(attempts=1),
        headers={"X-BetterToken-Max-Images": "1"},
    ),
)
config = types.GenerateContentConfig(
    response_modalities=["IMAGE"],
    candidate_count=1,
    image_config=types.ImageConfig(image_size="1K", aspect_ratio="1:1"),
)

def save_images(response, prefix):
    if len(response.candidates or []) != 1 or not response.candidates[0].content:
        raise RuntimeError("No single image candidate returned.")
    saved = 0
    suffixes = {"image/png": ".png", "image/jpeg": ".jpg"}
    for part in response.candidates[0].content.parts or []:
        if part.thought or not part.inline_data:
            continue
        blob = part.inline_data
        suffix = suffixes.get(blob.mime_type)
        if suffix is None:
            raise ValueError("Unexpected image MIME type: " + str(blob.mime_type))
        saved += 1
        Path(f"{prefix}-{saved}{suffix}").write_bytes(blob.data)
    if saved == 0:
        raise RuntimeError("No final image returned; inspect the response.")

first_turn = types.Content(
    role="user",
    parts=[types.Part(text="Una foto de estudio de una taza de cerámica azul sobre una mesa blanca.")],
)
response = client.models.generate_content(
    model=MODEL, contents=[first_turn], config=config,
)
save_images(response, "generated")
```

## Envía una referencia

Ejecuta después del ejemplo Python para enviar una nueva edición. Sustituye `reference.jpg` por tu archivo. Para PNG usa `mime_type="image/png"`. Añade más referencias a `parts` del mismo mensaje de usuario.

```python theme={null}
first_turn = types.Content(
    role="user",
    parts=[
        types.Part(text="Cambia la taza a rojo. Conserva su forma y el fondo."),
        types.Part.from_bytes(
            data=Path("reference.jpg").read_bytes(),
            mime_type="image/jpeg",
        ),
    ],
)
response = client.models.generate_content(
    model=MODEL, contents=[first_turn], config=config,
)
save_images(response, "reference-edit")
```

En JSON nativo usa `inlineData: {"mimeType": "image/jpeg", "data": "BASE64_IMAGE_DATA"}`. Codifica los bytes originales sin prefijo `data:image/...;base64,`. No uses multipart, Files ni `fileData`.

## Continúa editando

Ejecuta después de una generación o edición correcta. Conserva todo el `content` de la respuesta, el orden de las partes y `thoughtSignature`. No reconstruyas el historial solo con la imagen ni inventes o alteres firmas.

```python theme={null}
history = [first_turn, response.candidates[0].content]
edit_turn = types.Content(
    role="user",
    parts=[types.Part(text="Añade una pequeña asa dorada. Mantén todo lo demás igual.")],
)
edited = client.models.generate_content(
    model=MODEL, contents=[*history, edit_turn], config=config,
)
save_images(edited, "follow-up")
history.extend([edit_turn, edited.candidates[0].content])
```

Cada turno es otra solicitud de generación. Conserva `history`, añade la instrucción y luego el `content` devuelto. No uses `previous_interaction_id` de Interactions en este endpoint.

## Compatibilidad de la API

* Usa `generateContent` sin streaming. Esta integración no admite Files / `fileData`, Batch, Interactions ni streaming.
* No se admiten `responseMimeType`, `responseSchema` ni formatos de compresión personalizados. En `imageConfig`, usa solo los dos campos indicados arriba.
* Omite `cachedContent`, llamadas a funciones, ejecución de código, búsqueda de archivos, Maps, URL context y ajustes de entrada o generación de audio.

## Si ya usas un cliente OpenAI Chat

Puedes llamar a `POST /v1/chat/completions` con Base URL `https://bettertoken.ai/v1`. Para nuevas integraciones, referencias y edición por turnos, usa la API nativa de Gemini anterior. En Chat, omite `n` o usa el entero `1`.

En HTTP coloca `google.image_config` en la raíz JSON. En Python OpenAI SDK usa `extra_body`, que el SDK combina con la raíz. No uses `generationConfig` aquí.

```bash theme={null}
curl --fail-with-body --max-time 650 \
  "https://bettertoken.ai/v1/chat/completions" \
  -H "Authorization: Bearer $BETTERTOKEN_API_KEY" \
  -H "Content-Type: application/json" \
  -H "X-BetterToken-Max-Images: 1" \
  -d '{
    "model": "nano-banana-2.1",
    "messages": [{"role": "user", "content": "Una taza de cerámica azul sobre una mesa blanca."}],
    "n": 1,
    "stream": false,
    "google": {"image_config": {"image_size": "1K", "aspect_ratio": "1:1"}}
  }'
```

Para el ejemplo Python OpenAI SDK instala `openai==1.66.2` en el mismo entorno virtual:

```python theme={null}
import base64
import os
import re
from pathlib import Path
from openai import OpenAI

client = OpenAI(
    api_key=os.environ["BETTERTOKEN_API_KEY"],
    base_url="https://bettertoken.ai/v1",
    max_retries=0,
    timeout=650,
)
response = client.chat.completions.create(
    model="nano-banana-2.1",
    messages=[{"role": "user", "content": "Una taza de cerámica azul sobre una mesa blanca."}],
    n=1,
    stream=False,
    extra_headers={"X-BetterToken-Max-Images": "1"},
    extra_body={
        "google": {"image_config": {"image_size": "1K", "aspect_ratio": "1:1"}}
    },
)
content = response.choices[0].message.content or ""
images = re.findall(r"data:(image/(?:png|jpeg));base64,([A-Za-z0-9+/=]+)", content)
if not images:
    raise RuntimeError("No inline image in Chat response.")
for index, (mime, data) in enumerate(images, 1):
    suffix = ".png" if mime == "image/png" else ".jpg"
    Path(f"chat-{index}{suffix}").write_bytes(base64.b64decode(data, validate=True))
```

Chat devuelve Markdown Data URL en `choices[0].message.content`, no `data[]` de Images API. No reconstruyas firmas desde ese texto; usa edición nativa.

## Errores habituales

| Problema | Acción |
| - | - |
| `401` / Key inválida | Usa BetterToken API Key, no Google. Revisa variable y cabecera. |
| `404` / URL | Base URL raíz, `api_version="v1beta"` y uno de los tres Model IDs públicos. |
| `400 unsupported_endpoint` | Sustituye `/v1/images/*` por `:generateContent`. |
| `400` / parámetros | Comprueba los niveles, las mayúsculas de `1K/2K/4K` y las proporciones del modelo. Omite `candidateCount` o usa el entero `1`; no añadas herramientas de búsqueda. |
| Sin imagen final | Revisa errores, seguridad, texto y `finishReason`. No guardes imágenes thought como finales. |
| Archivo ilegible | Decodifica base64 y usa la extensión MIME, no guardes todo el JSON o Markdown. |
| Timeout o desconexión | Revisa consumo y conserva el ID de solicitud. Confirma el resultado con soporte antes de reintentar para evitar duplicados y cobros. |

La imagen llega en la respuesta actual. No hay recuperación en segundo plano ni consulta de tarea para este flujo. Guarda los resultados, permite suficiente tiempo en SDK y proxy y desactiva reintentos automáticos.

## Fuentes oficiales

* [Modelo Nano Banana Pro](https://ai.google.dev/gemini-api/docs/models/gemini-3-pro-image)
* [Modelo Nano Banana 2](https://ai.google.dev/gemini-api/docs/models/gemini-3.1-flash-image)
* [Modelo Nano Banana 2.1](https://ai.google.dev/gemini-api/docs/models/gemini-nano-banana-2.1)
* [Guía de imágenes de Google](https://ai.google.dev/gemini-api/docs/generate-content/image-generation)
* [Notas de versiones de Google](https://ai.google.dev/gemini-api/docs/changelog)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.