> ## 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.

# Nano Banana API: генерация, референсы и последовательное редактирование

> Подключите Nano Banana Pro, 2 и 2.1 к BetterToken: авторизация, разрешения, Python, референсы, сохранение изображений, повторное редактирование и ограничения.

Один нативный интерфейс Gemini подходит для генерации по тексту, редактирования по референсу и последующих правок. В примерах используется `nano-banana-2.1`; для другой модели замените её ID в URL или вызове SDK.

* [Gemini API: генерация по тексту](/api-reference/gemini-images-generations)
* [Gemini API: редактирование изображений](/api-reference/gemini-images-edits)

<Warning>
  BetterToken API не поддерживает генерацию с поиском, включая Google Web Search и Image Search grounding. Не добавляйте поисковые инструменты.
</Warning>

## Параметры подключения

| Поле | Значение |
| - | - |
| Base URL | `https://bettertoken.ai` |
| Запрос | `POST /v1beta/models/{model}:generateContent` |
| Авторизация | `x-goog-api-key: YOUR_API_KEY` или `Authorization: Bearer YOUR_API_KEY`. Выберите один вариант. |
| Тело запроса | `application/json` |
| Model ID | `nano-banana-pro`, `nano-banana-2`, `nano-banana-2.1` |

<a href={"https://bettertoken.ai/register"}>Создать BetterToken API Key</a> · <a href={"https://bettertoken.ai/pricing"}>каталог моделей</a> · [Обзор моделей Nano Banana](/model-updates/nano-banana)

<Warning>
  Эти три модели Nano Banana не поддерживают `POST /v1/images/generations` и `POST /v1/images/edits`: запрос вернёт `400 unsupported_endpoint`. Не переносите примеры GPT Image с multipart, `image[]` или `data[].b64_json`.
</Warning>

## Создайте первое изображение

Задайте `BETTERTOKEN_API_KEY` в терминале и выполните запрос. Он дождётся генерации и сохранит полный JSON-ответ в `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": "Студийная фотография синей керамической кружки на белом столе."}]
        }],
        "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 = "Студийная фотография синей керамической кружки на белом столе." })
        })
        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>

Для большего разрешения замените `imageSize` на `2K` или `4K`. Начните с `1K`, `1:1` и одного изображения. Не включайте автоматический повтор генерации через `--retry`.

## Основные параметры

Для предметной съёмки опишите объект, материал, свет и фон. Для постера укажите точный текст и расположение элементов. При редактировании перечислите, что изменить и что сохранить.

В нативном HTTP-запросе используйте поля ниже; Python SDK использует snake\_case. Настройки изображения размещайте внутри `generationConfig.imageConfig`.

| Поле | Значение |
| - | - |
| `contents[].parts[].text` | Опишите предмет, фон, стиль и требуемые изменения. |
| `contents[].parts[].inlineData` | `mimeType` референса и base64 исходного файла, а не URL изображения. |
| `generationConfig.responseModalities` | Явно задайте `["IMAGE"]`. |
| `generationConfig.imageConfig.imageSize` | `1K`, `2K` или `4K` с учётом регистра; по умолчанию `1K`. |
| `generationConfig.imageConfig.aspectRatio` | Выберите пропорции для модели из таблицы ниже; в примерах используется `1:1`. |
| `generationConfig.candidateCount` | Пропустите или задайте целое `1`; корневой `n` в нативном запросе запрещён. |
| `X-BetterToken-Max-Images` | Необязательный заголовок: целое от `1` до `4`, по умолчанию `1`. Максимум изображений, разрешённых для выдачи и оплаты. |

<span id="aspect-ratios" />

### Пропорции, поддерживаемые Google

Nano Banana 2 и 2.1 поддерживают 14 пропорций, Pro — 10. Для первого запроса выберите `1:1`.

| Модель | Разрешение | Пропорции |
| - | - | - |
| `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` |

[Пропорции и размеры изображений Google](https://ai.google.dev/gemini-api/docs/image-generation#aspect-ratios-and-image-size)

Этот максимум не гарантирует число изображений. Даже по запросу нескольких модель может вернуть одно. Для фиксированного количества отправляйте отдельные запросы.

## Прочитайте и сохраните изображение

Изображения находятся в `candidates[].content.parts[].inlineData`. Ниже показана структура; `BASE64_IMAGE_DATA` — заглушка. Обрабатывайте все итоговые части с изображениями и пропускайте промежуточные части с `thought: true`.

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

Этот код Python сохраняет результат curl. Выбирайте расширение по `mimeType`: ответ может содержать JPEG, поэтому не сохраняйте всё как `.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.")
```

## Полный пример Python SDK

Используйте Python 3.10 или новее. Пример рассчитан на `google-genai==2.30.1`. Установите пакет в виртуальное окружение проекта: старые версии SDK могут не передавать `image_size`.

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

Base URL SDK — корень домена без `/v1` и `/v1beta`. `timeout` задаётся в миллисекундах; `attempts=1` отключает автоматические повторы. Функция `save_images` сохраняет все итоговые изображения.

```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="Студийная фотография синей керамической кружки на белом столе.")],
)
response = client.models.generate_content(
    model=MODEL, contents=[first_turn], config=config,
)
save_images(response, "generated")
```

## Передайте референс

Выполните код после полного примера Python: он отправит новый запрос на редактирование. Замените `reference.jpg` локальным файлом. Для PNG укажите `mime_type="image/png"`. Дополнительные референсы добавляйте в `parts` того же сообщения пользователя.

```python theme={null}
first_turn = types.Content(
    role="user",
    parts=[
        types.Part(text="Сделайте кружку красной. Сохраните её форму и фон."),
        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")
```

В нативном JSON используется `inlineData: {"mimeType": "image/jpeg", "data": "BASE64_IMAGE_DATA"}`. Кодируйте исходные байты без префикса `data:image/...;base64,`. Здесь не используются multipart, загрузка Files или `fileData`.

## Продолжите редактирование

Выполните код после успешной генерации или правки по референсу. Сохраните весь `content` ответа модели, порядок частей и `thoughtSignature`. Не собирайте историю только из изображения, не придумывайте и не изменяйте подписи.

```python theme={null}
history = [first_turn, response.candidates[0].content]
edit_turn = types.Content(
    role="user",
    parts=[types.Part(text="Добавьте небольшую золотую ручку. Всё остальное оставьте без изменений.")],
)
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])
```

Каждая правка — новый запрос генерации. Для продолжения сохраняйте полный `history`, добавляйте новую инструкцию пользователя, затем полученный `content` модели. Поле Interactions `previous_interaction_id` здесь не используется.

## Поддержка API

* Используйте нестриминговый `generateContent`. Files / `fileData`, Batch, Interactions и стриминг в этом подключении не поддерживаются.
* `responseMimeType`, `responseSchema` и выбор формата сжатия изображений не поддерживаются. В `imageConfig` используйте только два поля, перечисленные выше.
* Не добавляйте `cachedContent`, вызов функций, выполнение кода, поиск файлов, Maps, URL context и настройки входного или выходного аудио.

## Если уже есть клиент OpenAI Chat

Можно вызвать `POST /v1/chat/completions` с Base URL `https://bettertoken.ai/v1`. Для нового подключения, референсов и последовательных правок используйте нативный интерфейс Gemini выше. В запросе Chat пропустите `n` или задайте целое `1`.

В HTTP-запросе `google.image_config` находится в корне JSON. В Python OpenAI SDK передайте его через `extra_body`: SDK объединит поля с корнем. Не используйте здесь `generationConfig`.

```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": "Синяя керамическая кружка на белом столе."}],
    "n": 1,
    "stream": false,
    "google": {"image_config": {"image_size": "1K", "aspect_ratio": "1:1"}}
  }'
```

Для примера Python OpenAI SDK установите в том же виртуальном окружении `openai==1.66.2`, затем выполните:

```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": "Синяя керамическая кружка на белом столе."}],
    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 возвращает изображения как Markdown Data URL в `choices[0].message.content`, а не в `data[]` Images API. Не восстанавливайте из этого текста историю с подписями; для дальнейших правок используйте нативный пример.

## Частые ошибки

| Проблема | Что сделать |
| - | - |
| `401` / неверный Key | Используйте BetterToken API Key, а не ключ Google. Проверьте переменную окружения и заголовок. |
| `404` / неверный URL | Base URL SDK — корень домена, `api_version="v1beta"`; Model ID — один из трёх публичных ID. |
| `400 unsupported_endpoint` | Замените `/v1/images/*` адресом `:generateContent` выше. |
| `400` / параметры | Проверьте вложенность, регистр `1K/2K/4K` и пропорции выбранной модели. Пропустите `candidateCount` или задайте целое `1`; не добавляйте поисковые инструменты. |
| Ответ без итогового изображения | Проверьте ошибки, ограничения безопасности, текст и `finishReason` полного ответа. Не сохраняйте thought-изображения как результат. |
| Файл не открывается | Выберите расширение по MIME и декодируйте base64. Не сохраняйте JSON или Markdown как изображение. |
| Тайм-аут или разрыв соединения | Проверьте историю расходов и сохраните ID запроса из ответа. Уточните результат у поддержки до повтора, чтобы избежать повторной генерации и оплаты. |

Изображения возвращаются в текущем ответе. Здесь нет фонового получения файла или опроса задачи. Сохраняйте успешные результаты сразу, задавайте достаточное время ожидания в SDK и прокси и отключайте автоматические повторы.

## Официальные источники

* [Модель Nano Banana Pro](https://ai.google.dev/gemini-api/docs/models/gemini-3-pro-image)
* [Модель Nano Banana 2](https://ai.google.dev/gemini-api/docs/models/gemini-3.1-flash-image)
* [Модель Nano Banana 2.1](https://ai.google.dev/gemini-api/docs/models/gemini-nano-banana-2.1)
* [Генерация изображений Google](https://ai.google.dev/gemini-api/docs/generate-content/image-generation)
* [История выпусков Google](https://ai.google.dev/gemini-api/docs/changelog)


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