Перейти к основному содержанию

Nano Banana не генерирует изображение в API: как найти причину

Ответ пришёл, а картинки нет: причина в finishReason, promptFeedback или status. Текст вместо изображения исправляет запрос, IMAGE_SAFETY повтором не обойти.

LaoZhang AI TeamОпубликованоОбновлено 11 мин чтения
Содержание
Поля ответа Nano Banana для диагностики отсутствующего изображения

Если вызов Nano Banana завершился без ошибки, но изображения нет, ответ уже содержит причину. В generateContent она записана в promptFeedback.blockReason (запрос отклонён, candidates пуст) или в finishReason кандидата (NO_IMAGE, IMAGE_SAFETY, MAX_TOKENS и другие). В Interactions API смотрите status и шаги steps. От этого значения зависит всё остальное: текстовый ответ вместо картинки исправляется запросом и настройкой формата вывода, отказ IMAGE_SAFETY или PROHIBITED_CONTENT повтором того же запроса не обойти, а отключённый ID модели нужно заменить в коде.

По состоянию на 24 сентября 2026 года семейство Nano Banana в Gemini API — это gemini-3.1-flash-image (Nano Banana 2), gemini-3.1-flash-lite-image (Nano Banana 2 Lite), gemini-3-pro-image (Nano Banana Pro) и исходная gemini-2.5-flash-image, которую Google отключает 2 октября 2026 года (документация Google, график отключений). Всё ниже относится к API, AI Studio, официальным SDK и программам, которые вызывают API. Если картинка не появляется в приложении или на сайте Gemini, вам нужна другая инструкция: Nano Banana не работает в Gemini: 12 исправлений для ошибок изображения.

Что в ответе и что делать

Таблица читается слева направо: найдите в сохранённом JSON строку, которая совпадает с вашим ответом, и выполните одно действие из правой колонки.

Что в ответеЧто это значитСледующее действие
candidates пуст, задан promptFeedback.blockReasonЗапрос отклонён до генерацииИзменить текст или входные изображения; повтор без изменений бесполезен
finishReason: STOP, в parts только текстМодель ответила текстомПрямо попросить изображение и задать responseModalities
finishReason: NO_IMAGEИзображение ожидалось, но не созданоОдна попытка с более прямым запросом, затем упростить сцену
IMAGE_SAFETY, IMAGE_PROHIBITED_CONTENTСозданное изображение заблокировано политикойМенять содержание; настройки безопасности не помогут
SAFETY, PROHIBITED_CONTENT, BLOCKLIST, SPIIОстановлено по безопасности, запрещённым терминам или персональным даннымСмотреть safetyRatings, убрать спорное из запроса
IMAGE_RECITATION, RECITATIONРезультат слишком похож на существующие данныеСделать запрос и референсы уникальнее
IMAGE_OTHERСбой без поясненияСохранить ответ, повторить один раз
MAX_TOKENSНе хватило лимита выводаУбрать или поднять maxOutputTokens
MISSING_THOUGHT_SIGNATUREВ многоходовом диалоге потеряна подписьПередавать ответ модели в историю целиком
Interactions: status: completed, в steps только текстМодель ответила текстомЗадать response_format с типом image
Interactions: incomplete или failedРезультат неполный или ошибкаСмотреть errors и лимит max_output_tokens
HTTP 4xx или 5xx вместо ответаЭто не пустой ответ, а ошибка запросаПроверить ID модели, ключ, квоту
Картинка в JSON есть, но программа её не сохранилаОшибка разбора ответаПеребирать все части и пропускать промежуточные (thought)

Действия при текстовом ответе, NO_IMAGE, SAFETY, IMAGE_SAFETY и ошибках лимита или истории

Значения finishReason и blockReason взяты из справочника generateContent, статусы — из справочника Interactions API. Ниже — подробности по каждой группе и скрипт, который выносит такой вердикт по файлу с ответом.

Сначала исключите отключённый ID модели

Если код работал весной и перестал, первым делом проверьте ID модели, а не промпт. Google отключил preview-версии, и вызов по такому ID возвращает ошибку, а не ответ без картинки. Точный текст этой ошибки в документации не приводится, поэтому ориентируйтесь на сам ID в вашем коде.

ID моделиНазваниеСтатус на 24 сентября 2026 года
gemini-3.1-flash-imageNano Banana 2Стабильная версия с 28 мая 2026 года, дата отключения не объявлена
gemini-3-pro-imageNano Banana ProСтабильная версия с 28 мая 2026 года, дата отключения не объявлена
gemini-3.1-flash-lite-imageNano Banana 2 LiteСтабильная версия с 30 июня 2026 года
gemini-2.5-flash-imageNano Banana (исходная)Отключается 2 октября 2026 года
gemini-3.1-flash-image-previewNano Banana 2 (preview)Отключена 25 июня 2026 года, замена gemini-3.1-flash-image
gemini-3-pro-image-previewNano Banana Pro (preview)Отключена 25 июня 2026 года, замена gemini-3-pro-image
gemini-2.5-flash-image-preview—Отключена 15 января 2026 года
gemini-2.0-flash-preview-image-generation—Отключена 14 ноября 2025 года

Для gemini-2.5-flash-image таблица отключений до сих пор называет заменой уже отключённую gemini-3.1-flash-image-preview. На странице генерации изображений Google советует переходить на Nano Banana 2 Lite, а на странице цен — на gemini-3.1-flash-image или gemini-3.1-flash-lite-image. Берите стабильный ID без -preview; какой из трёх подходит под вашу задачу, разобрано в статье Nano Banana 2 vs Pro vs 2.1: что выбрать, если NB2 устарела.

Второе, что стоит проверить в старом коде, — библиотеку. Текущие примеры Google написаны на SDK google-genai (from google import genai в Python, @google/genai в JavaScript). С июня 2026 года Interactions API вышел в стабильную версию и рекомендован для новых проектов, а generateContent Google называет устаревшим (legacy), но полностью поддерживаемым (обзор Interactions API). Структура ответа у двух API разная, и путать их при разборе нельзя: у generateContent есть candidates, parts и finishReason, у Interactions — status и steps.

Ошибка 429 — отдельный симптом, связанный с квотами, а не с содержанием запроса; он разобран в статье Лимиты Nano Banana 2: сколько изображений в день в Gemini и API.

Разбор ответа generateContent

Успешный вызов возвращает GenerateContentResponse: массив candidates, где у каждого кандидата есть content.parts и finishReason, а также promptFeedback и usageMetadata. Изображение приходит частью с полем inlineData (mimeType вида image/png и данные в base64). Если такой части среди финальных нет, причину показывают поля ниже.

Пустой candidates: запрос отклонён

Справочник API прямо говорит: кандидатов нет только тогда, когда что-то не так с самим запросом, и тогда смотреть нужно promptFeedback. Возможные blockReason:

  • SAFETY — запрос остановлен по категории безопасности, подробности в promptFeedback.safetyRatings;
  • PROHIBITED_CONTENT — запрещённое содержание;
  • BLOCKLIST — в запросе есть термины из списка запрещённых;
  • IMAGE_SAFETY — заблокирована генерация небезопасного изображения;
  • OTHER — причина не раскрывается.

Повторять тот же запрос без изменений нет смысла: решение принимается по его содержанию, включая входные изображения. Случаю blockReason: OTHER посвящено отдельное руководство — Nano Banana 2 blockReason OTHER: что значит ошибка и что делать.

STOP, но в ответе только текст

По документации модель по умолчанию возвращает и текст, и изображение, поэтому ответ из одной реплики вроде «Опишите, какой стиль вам нужен» формально корректен: finishReason равен STOP, ошибки нет. Два шага, которые стоит сделать:

  1. Сформулировать запрос как команду на создание картинки: «Сгенерируй изображение: …», а не «придумай» или «опиши». Русский (ru-RU) входит в список языков, на которых Google рекомендует работать с моделью, так что сам язык запроса причиной быть не должен.
  2. Задать формат вывода. В generateContent это generationConfig.responseModalities: ["IMAGE"], если текст не нужен, или ["TEXT", "IMAGE"]. По справочнику значение должно точно совпадать с модальностями ответа, а неподдерживаемая комбинация вернёт ошибку.
python
from google import genai
from google.genai import types

client = genai.Client()
response = client.models.generate_content(
    model="gemini-3.1-flash-image",
    contents="Сгенерируй изображение: керамическая чашка на деревянном столе, мягкий утренний свет",
    config=types.GenerateContentConfig(response_modalities=["IMAGE"]),
)

Гарантии, что с ["IMAGE"] картинка будет всегда, документация не даёт: настройка ограничивает, в каком виде может прийти ответ, но вызов всё равно может закончиться NO_IMAGE или блокировкой. Если вы просили несколько изображений и получили одно, это описанное ограничение: модель не всегда выдаёт ровно столько картинок, сколько запрошено.

NO_IMAGE, IMAGE_OTHER и IMAGE_RECITATION

NO_IMAGE означает, что модель должна была создать изображение, но не создала. Причину справочник не уточняет. Разумно один раз повторить запрос в более прямой формулировке, а если ответ повторится — сократить сцену: меньше объектов, меньше референсов. У моделей есть пределы по входным изображениям: gemini-2.5-flash-image лучше всего работает максимум с тремя, gemini-3-pro-image — с пятью в высокой точности и до 14 всего, gemini-3.1-flash-image удерживает сходство до четырёх персонажей и до десяти объектов в одном сценарии.

IMAGE_OTHER — «прочая проблема» без подробностей. Сохраните ответ целиком, повторите вызов один раз; если результат тот же, упрощайте запрос так же, как при NO_IMAGE.

IMAGE_RECITATION значит, что генерация остановлена из-за сходства с существующими данными. Для обычного RECITATION в руководстве по устранению неполадок Google советует сделать промпт и контекст как можно более уникальными и поднять температуру. Для изображений логично начать с референсов: убрать узнаваемые чужие работы и описать собственную сцену.

Если finishMessage заполнено, прочитайте его: это поле поясняет причину остановки, но какой именно элемент запроса сработал, оно обычно не сообщает.

IMAGE_SAFETY и IMAGE_PROHIBITED_CONTENT: почему настройки безопасности не помогут

IMAGE_SAFETY означает, что созданное изображение нарушает правила безопасности, а IMAGE_PROHIBITED_CONTENT — что в нём другое запрещённое содержание. Снять это через safetySettings нельзя, и причина видна в документации по настройкам безопасности:

  • настраиваются только четыре категории: домогательства (harassment), язык вражды (hate speech), откровенно сексуальное содержание (sexually explicit) и опасный контент (dangerous);
  • если порог не задан, для моделей Gemini 2.5 и 3 он уже выключен (OFF), так что BLOCK_NONE ничего не ослабляет;
  • защита от основных видов вреда, например угроз безопасности детей, работает всегда и не настраивается;
  • в Interactions API пользовательских настроек безопасности нет вообще.

Настройки имеют значение в одном случае: если вы сами поставили более строгий порог (например, BLOCK_LOW_AND_ABOVE), и ответ остановлен с SAFETY. Тогда safetyRatings покажут категорию и вероятность, а возврат порога к значению по умолчанию уберёт эту блокировку.

При IMAGE_SAFETY и IMAGE_PROHIBITED_CONTENT одинаковый запрос даёт одинаковый исход, поэтому циклы повторов в коде только тратят время и токены. Менять нужно то, что изображается, а не подбирать синонимы к тому же сюжету. В AI Studio такой отказ выглядит как надпись Content blocked; если навести на неё курсор, появятся категория и вероятность.

MAX_TOKENS и MISSING_THOUGHT_SIGNATURE

Изображение тоже расходует токены вывода. По прайсу Google картинка Nano Banana 2 в 1K занимает 1 120 токенов, в 4K — 2 520. Если в generationConfig задан небольшой maxOutputTokens, изображение может в него не поместиться, и ответ закончится MAX_TOKENS. Уберите ограничение или поднимите его с запасом.

MISSING_THOUGHT_SIGNATURE появляется при многоходовом редактировании, когда в историю диалога не вернули подписи thought_signature из прошлых ответов. Официальные SDK в режиме чата передают их сами; если историю вы собираете вручную, добавляйте ответ модели в неё целиком. Подробный разбор — в статье Ошибка Thought Signature в Nano Banana 2: полное руководство по исправлению (2026).

Разбор ответа Interactions API

У Interactions API нет candidates и finishReason. Ответ — объект interaction с полем status и массивом steps. Значения status:

statusЧто означает
completedInteraction завершён
incompleteЗавершён, но результат неполный (например, достигнут max_tokens)
failedЗавершился ошибкой
in_progress, queuedЕщё выполняется или ждёт очереди
requires_actionТребуется действие или ввод со стороны пользователя
cancelledОтменён

Финальные изображения лежат в шагах type: "model_output" как блоки content с type: "image", data и mime_type. Шаги type: "thought" содержат промежуточные наброски в поле summary. Удобное свойство interaction.output_image из SDK возвращает только последний блок изображения; для ответов, где текст и картинки чередуются, Google прямо рекомендует перебирать steps вручную.

Если status равен completed, а изображений в model_output нет, это тот же текстовый ответ, что и выше. Формат вывода задаётся в response_format: объект {"type": "image"} оставляет только изображение (там же можно указать aspect_ratio и image_size), массив из {"type": "text"} и {"type": "image"} запрашивает и текст, и картинку.

python
from google import genai

client = genai.Client()
interaction = client.interactions.create(
    model="gemini-3.1-flash-image",
    input="Сгенерируй изображение: керамическая чашка на деревянном столе, мягкий утренний свет",
    response_format={"type": "image", "aspect_ratio": "1:1"},
)
print(interaction.status)

При failed смотрите массив errors (в каждом элементе есть code и message). Как в Interactions API выглядит именно блокировка изображения политикой, в опубликованной документации не описано. Поэтому сохраняйте весь объект interaction, а если нужен конкретный finishReason, отправьте тот же запрос через generateContent: он по-прежнему полностью поддерживается и возвращает причину остановки в явном виде.

Картинка в ответе есть, а программа её не видит

Модели Gemini 3 для изображений всегда «думают», и в API это не отключается. По документации модель создаёт до двух промежуточных изображений, чтобы проверить композицию; последнее из них совпадает с финальной картинкой. В generateContent такие части помечены thought: true и возвращаются, только если включён includeThoughts. В Interactions API они лежат в шагах thought.

Финальные изображения и промежуточные thought-части в generateContent и Interactions API

Из этого следует несколько типичных ошибок разбора (это вывод из структуры ответа, а не описанные Google сбои):

  • код читает только parts[0], а там текст — и сообщает «картинки нет», хотя она в следующей части;
  • код берёт первое найденное изображение и при включённом includeThoughts сохраняет набросок, а не финальный результат;
  • код полагается на output_image в ответе, где картинок несколько, и теряет все, кроме последней;
  • программа поверх API показывает только текст ответа и не выводит изображение, хотя оно в JSON есть.

Правило простое: прежде чем решить, что изображения нет, пройдите по всем частям всех кандидатов (или по всем шагам interaction), пропустите помеченные как мысли и только потом делайте вывод. Именно так устроен скрипт ниже.

Скрипт: вердикт по сохранённому ответу

Скрипт на Python 3 без внешних зависимостей читает JSON-файл с ответом, определяет, какой это API, и печатает вердикт, подсказку, число финальных и промежуточных изображений и поля расхода токенов. Если передать второй аргумент — папку, он сохранит найденные финальные изображения.

Как получить файл с ответом:

  • REST: добавьте к вызову curl параметр -o response.json;
  • Python SDK google-genai: open("response.json", "w").write(response.model_dump_json(exclude_none=True)). SDK выгружает поля в snake_case (finish_reason, inline_data), а байты изображения — в URL-safe base64; скрипт понимает оба варианта.
python
#!/usr/bin/env python3
"""Разбор сохранённого ответа Nano Banana: есть ли изображение и что делать дальше.

Вход: JSON-файл с ответом generateContent или Interactions API
(сырой REST или выгрузка объекта SDK, camelCase и snake_case).
Выход: вердикт, подсказка, число изображений, поля usage.
Код возврата: 0 — финальное изображение есть, 1 — нет, 2 — файл не разобран.
"""
import base64
import json
import pathlib
import re
import sys

HINTS = {
    "TEXT_ONLY": "Модель ответила текстом. Попросите изображение явно и ограничьте вывод форматом IMAGE.",
    "NO_IMAGE": "Изображение ожидалось, но не создано. Одна попытка с более прямым запросом, затем упростите сцену.",
    "IMAGE_SAFETY": "Политика Google заблокировала сгенерированное изображение. Меняйте содержание, повтор не поможет.",
    "IMAGE_PROHIBITED_CONTENT": "Запрещённое содержание в изображении. Меняйте тему, повтор не поможет.",
    "IMAGE_RECITATION": "Результат слишком похож на существующие данные. Сделайте запрос и референсы уникальнее.",
    "IMAGE_OTHER": "Сбой без уточнения. Сохраните ответ, повторите один раз, затем упростите запрос.",
    "SAFETY": "Блокировка по категории безопасности. Смотрите safetyRatings и свои safetySettings.",
    "PROHIBITED_CONTENT": "Запрещённое содержание. Меняйте тему, повтор не поможет.",
    "BLOCKLIST": "Сработал список запрещённых терминов. Уберите их из запроса.",
    "SPII": "В ответе могли оказаться персональные данные. Уберите их из запроса.",
    "RECITATION": "Результат похож на существующие данные. Сделайте запрос уникальнее.",
    "MAX_TOKENS": "Не хватило лимита токенов. Уберите или увеличьте maxOutputTokens.",
    "MISSING_THOUGHT_SIGNATURE": "В истории диалога потеряны thought signature. Передавайте ответ модели целиком.",
    "PROMPT_BLOCKED": "Запрос отклонён до генерации, кандидатов нет. Меняйте текст или входные изображения.",
    "EMPTY": "Ни изображения, ни причины. Сохраните сырой ответ и повторите один раз.",
    "FAILED": "Interaction завершился ошибкой. Смотрите errors и шаги.",
    "INCOMPLETE": "Результат неполный (например, лимит токенов). Проверьте max_output_tokens.",
    "NOT_FINISHED": "Interaction ещё не завершён. Дождитесь статуса completed.",
    "HTTP_ERROR": "Запрос не выполнен. Проверьте ID модели, ключ и квоту.",
}


def snake(key):
    return re.sub(r"(?<!^)(?=[A-Z])", "_", key).lower()


def norm(obj):
    """Приводит ключи к snake_case, чтобы REST и SDK читались одинаково."""
    if isinstance(obj, dict):
        return {snake(k): norm(v) for k, v in obj.items()}
    if isinstance(obj, list):
        return [norm(v) for v in obj]
    return obj


def check_generate_content(r):
    usage = r.get("usage_metadata", {})
    cands = r.get("candidates") or []
    if not cands:
        reason = (r.get("prompt_feedback") or {}).get("block_reason")
        return ("PROMPT_BLOCKED", reason) if reason else ("EMPTY", None), [], 0, usage
    final, thought, has_text = [], 0, False
    finish = message = None
    for c in cands:
        finish = finish or c.get("finish_reason")
        message = message or c.get("finish_message")
        for p in (c.get("content") or {}).get("parts") or []:
            blob = p.get("inline_data") or {}
            if str(blob.get("mime_type", "")).startswith("image/"):
                if p.get("thought"):
                    thought += 1
                else:
                    final.append(blob)
            elif p.get("text") and not p.get("thought"):
                has_text = True
    if final:
        return ("OK", finish), final, thought, usage
    if finish in (None, "STOP"):
        return ("TEXT_ONLY" if has_text else "EMPTY", finish), [], thought, usage
    return (finish, message), [], thought, usage


def check_interaction(r):
    usage = r.get("usage", {})
    status = r.get("status")
    final, thought, has_text = [], 0, False
    for step in r.get("steps") or []:
        if step.get("type") == "model_output":
            for block in step.get("content") or []:
                if block.get("type") == "image":
                    final.append(block)
                elif block.get("type") == "text":
                    has_text = True
        elif step.get("type") == "thought":
            thought += sum(1 for b in step.get("summary") or [] if b.get("type") == "image")
    if final:
        return ("OK", status), final, thought, usage
    errors = "; ".join(e.get("message", "") for e in r.get("errors") or [])
    if status == "failed":
        return ("FAILED", errors or None), [], thought, usage
    if status == "incomplete":
        return ("INCOMPLETE", None), [], thought, usage
    if status in ("in_progress", "queued", "requires_action"):
        return ("NOT_FINISHED", status), [], thought, usage
    return ("TEXT_ONLY" if has_text else "EMPTY", status), [], thought, usage


def main(path, save_dir=None):
    try:
        r = norm(json.loads(pathlib.Path(path).read_text(encoding="utf-8")))
    except (OSError, ValueError) as exc:
        print(f"Не удалось прочитать JSON: {exc}")
        return 2
    if "error" in r:
        e = r["error"]
        print(f"verdict: HTTP_ERROR ({e.get('code')} {e.get('status', '')})")
        print(f"hint: {HINTS['HTTP_ERROR']}")
        return 1
    if "steps" in r or r.get("object") == "interaction":
        api, check = "interactions", check_interaction
    elif "candidates" in r or "prompt_feedback" in r:
        api, check = "generateContent", check_generate_content
    else:
        print("Это не похоже на ответ generateContent или Interactions API.")
        return 2
    (verdict, detail), final, thought, usage = check(r)
    print(f"api: {api}")
    print(f"verdict: {verdict}" + (f" ({detail})" if detail else ""))
    if verdict != "OK":
        print(f"hint: {HINTS.get(verdict, 'Новое значение: сохраните ответ и сверьтесь со справочником API.')}")
    print(f"final images: {len(final)}, thought images: {thought}")
    print(f"usage: {json.dumps(usage, ensure_ascii=False)}")
    if save_dir and final:
        out = pathlib.Path(save_dir)
        out.mkdir(parents=True, exist_ok=True)
        for i, img in enumerate(final, 1):
            ext = str(img.get("mime_type", "image/png")).split("/")[-1]
            data = img.get("data", "").replace("-", "+").replace("_", "/")  # SDK пишет URL-safe base64
            (out / f"image_{i}.{ext}").write_bytes(base64.b64decode(data))
        print(f"saved: {len(final)} -> {out}")
    return 0 if final else 1


if __name__ == "__main__":
    if len(sys.argv) not in (2, 3):
        print("Использование: python3 check_nb_response.py response.json [папка_для_картинок]")
        sys.exit(2)
    sys.exit(main(*sys.argv[1:]))

Запуск и пример вывода для ответа, где изображение заблокировано:

bash
python3 check_nb_response.py response.json ./out
api: generateContent
verdict: IMAGE_SAFETY (пример finishMessage)
hint: Политика Google заблокировала сгенерированное изображение. Меняйте содержание, повтор не поможет.
final images: 0, thought images: 0
usage: {"prompt_token_count": 18, "candidates_token_count": 12, "thoughts_token_count": 240, "total_token_count": 270, "candidates_tokens_details": [{"modality": "TEXT", "token_count": 12}]}

Код возврата удобно использовать в пайплайне: 0 — финальное изображение найдено (и сохранено, если указана папка), 1 — изображения нет, 2 — файл не разобран. Неизвестные значения finishReason скрипт не пытается угадать, а печатает как есть: список причин в API пополняется.

Скрипт проверен офлайн на 16 синтетических JSON-файлах, а не на живых запросах к Google. В набор вошли ответы generateContent в формате REST (camelCase) и в выгрузке SDK google-genai 1.65.0 (snake_case), ответы Interactions API со статусами completed, failed и incomplete, тело ошибки 404 и повреждённый файл. Все 16 случаев дали ожидаемый вердикт и код возврата, изображения из ответов с картинкой сохранились как корректные PNG. Структура файлов повторяет схемы из справочников Google, но значения в них, включая finishMessage и числа токенов, придуманы для теста — в реальных ответах тексты будут другими.

Сколько стоит вызов без изображения

Google публикует цены за токены по типу вывода, но не пишет, как тарифицируется ответ, в котором изображение заблокировано или не создано. Ни правила «платите полную цену картинки», ни правила «такой вызов бесплатен» в прайсе Gemini API нет. Проверяемая опора — поля расхода в самом ответе: usageMetadata в generateContent и usage в Interactions API.

Тариф Standard, за 1 млн токеновNano Banana 2 (gemini-3.1-flash-image)Nano Banana Pro (gemini-3-pro-image)
Ввод (текст и изображения)$0,50$2
Вывод: текст и мышление$3$12
Вывод: изображения$60$120
Готовое изображение$0,045 (0,5K), $0,067 (1K), $0,101 (2K), $0,151 (4K)$0,134 (1K и 2K), $0,24 (4K)

Бесплатного уровня для вывода изображений у этих моделей нет. Оценка по полям ответа generateContent для Nano Banana 2:

ввод        = promptTokenCount × 0,50 / 1 000 000
мышление    = thoughtsTokenCount × 3 / 1 000 000
текст       = (candidatesTokenCount − токены IMAGE) × 3 / 1 000 000
изображение = токены IMAGE из candidatesTokensDetails × 60 / 1 000 000

Формулу легко проверить на успешном вызове: картинка 1K — это 1 120 токенов IMAGE, 1 120 × 60 / 1 000 000 = 0,0672, то есть те самые $0,067 из прайса. В ответе без изображения токенов IMAGE в candidatesTokensDetails нет или они равны нулю, поэтому по формуле остаются ввод, мышление и текст. Мышление при этом учитывается всегда: по документации токены мышления тарифицируются независимо от includeThoughts, потому что процесс идёт в любом случае. В Interactions API те же величины называются total_input_tokens, total_thought_tokens и output_tokens_by_modality.

Это оценка по опубликованным ставкам, а не счёт. Итоговую сумму показывает биллинг вашего проекта; если вы разбираете крупный расход, сопоставьте её с сохранёнными usage неудачных вызовов. Пересчитать бюджет по разрешениям для успешных генераций поможет статья Цены Nano Banana 2 API: 512, 1K, 2K, 4K и расчёт бюджета.

Частые вопросы

Почему API отвечает без ошибки, если изображения нет?

Потому что запрос принят и обработан, а результат обработки, включая отказ, записан в поля ответа: finishReason, promptFeedback.blockReason или status. Отказ по содержанию — это не сбой соединения, а исход генерации. Проверка «ответ пришёл, значит картинка есть» поэтому ненадёжна: наличие изображения нужно проверять по частям ответа.

Поможет ли BLOCK_NONE при IMAGE_SAFETY?

Нет. Для моделей Gemini 2.5 и 3 настраиваемые фильтры и так выключены по умолчанию, защита от основных видов вреда не настраивается, а в Interactions API пользовательских настроек безопасности нет. Порог важен, только если вы сами сделали его строже и получаете SAFETY.

Поможет ли другой ключ, VPN или сервис-посредник?

Если запрос в итоге обрабатывает та же модель Google, решение о блокировке принимается по содержанию запроса, а не по тому, откуда он пришёл. Смена ключа или канала не превращает IMAGE_SAFETY в картинку. VPN здесь тоже ни при чём: ни одно значение finishReason или blockReason не связано с регионом.

Почему в AI Studio картинка не появилась, а в коде появляется (или наоборот)?

Сравните модель и параметры: в AI Studio могут быть выбраны другой ID модели, формат вывода или пороги безопасности в Run settings. Самый надёжный способ — сохранить JSON из кода и прогнать его через скрипт выше: вердикт покажет, есть ли изображение и почему его нет.

Сколько раз имеет смысл повторять запрос?

При IMAGE_OTHER, NO_IMAGE и пустом ответе без причины — один раз, затем менять запрос. При IMAGE_SAFETY, IMAGE_PROHIBITED_CONTENT, PROHIBITED_CONTENT и blockReason — ноль: сначала меняется содержание. Ошибки 429 и 5xx — отдельный случай: руководство Google по устранению неполадок советует повторять их с экспоненциальной задержкой, а официальные SDK делают это сами.

Официальная документация

Параметры запросов, цены и правила списания средств описаны в документации LaoZhang API для разработчиков: биллинг и ошибки генерации изображений.

Примечание: laozhang.ai — наш собственный сервис; его цены — наши тарифы, а не независимое сравнение.

Источники7

Внешние страницы, на которые ссылается это руководство, в порядке упоминания. Последнее обновление: 5 окт. 2026 г..

  1. 1.документация Googleai.google.dev/gemini-api/docs/image-generation
  2. 2.график отключенийai.google.dev/gemini-api/docs/deprecations
  3. 3.справочника generateContentai.google.dev/api/generate-content
  4. 4.справочника Interactions APIai.google.dev/api/interactions
  5. 5.обзор Interactions APIai.google.dev/gemini-api/docs/interactions-overview
  6. 6.документации по настройкам безопасностиai.google.dev/gemini-api/docs/safety-settings
  7. 7.прайсе Gemini APIai.google.dev/gemini-api/docs/pricing