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

Gemini 3 Pro Image Batch API: скидка 50%, расчет стоимости и получение изображений

В официальном Batch API выходное изображение Gemini 3 Pro Image стоит 0,067 USD для 1K/2K или 0,12 USD для 4K по округленной таблице Google. Полный бюджет также включает входные данные, текст и thinking; результаты нужно сопоставлять с запросами по ключам.

LaoZhang AI TeamОпубликованоОбновлено 9 мин чтения
Содержание
Асинхронная Batch-генерация Gemini 3 Pro Image: половина тарифа, полный бюджет и получение пригодных результатов по ключам

Официальный Batch API позволяет платить 50 % от стандартных тарифов Gemini 3 Pro Image, если изображения можно подготовить асинхронно. В таблице Google указаны 0,067 USD за выходное изображение 1K/2K и 0,12 USD за 4K. Это стоимость изображения на выходе, а не всего запроса: отдельно учитываются ввод, текстовый ответ и токены рассуждения. Целевое время выполнения — 24 часа; срок зависит от нагрузки и размера задания. Цены Google, условия Batch API.

Для каталога товаров, иллюстраций к запланированным публикациям или набора вариантов дизайна такой обмен цены на ожидание обычно уместен. Для кнопки «создать сейчас», после которой пользователь ждет картинку на экране, нужен другой режим работы. Ниже — расчет бюджета и последовательность от JSONL-файла до сохраненных изображений, с восстановлением по имени задания вместо повторной отправки всего пакета.

Скидка на Batch API для Gemini 3 Pro Image: как это работает

Batch — способ отправки запросов к модели, а не отдельная модель изображений. Текущий стабильный идентификатор — gemini-3-pro-image; Nano Banana Pro — название этой модели. В ее карточке явно указана поддержка Batch. В старых примерах документации еще встречается суффикс -preview: не переносите его автоматически в новую интеграцию.

Что оплачиваетсяStandardBatch
Текстовый ввод, 1 млн токенов2 USD1 USD
Одно входное изображениеоколо 0,0011 USDоколо 0,0006 USD
Текст и thinking на выходе, 1 млн токенов12 USD6 USD
Выходное изображение 1K или 2K0,134 USD0,067 USD
Выходное изображение 4K0,24 USD0,12 USD

Это опубликованные округленные цены Gemini Developer API, проверенные 6 октября 2026 года. Бесплатного тарифа для этой модели в таблице нет. Условия веб-приложения Gemini и Google AI Studio не превращаются в бесплатную квоту для Batch. Официальный тариф.

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

Реальный калькулятор экономии — затраты при любом объеме

Для точного расчета по токенам удобно использовать 0,0672 USD за 1K/2K: Google указывает 1 120 токенов изображения и стандартную ставку 120 USD за миллион, а Batch стоит вдвое дешевле. Для 4K это 2 000 токенов и 0,12 USD. Поэтому 1 000 изображений 2K дают 67,20 USD за выход, хотя умножение округленных 0,067 из таблицы дает 67 USD. Это два способа округления, а не дополнительная скидка. Пояснения к тарифу.

Для пакета без дополнительных инструментов:

Batch, USD =
  токены текстового ввода / 1 000 000 × 1
  + число входных изображений × 0,00056
  + токены текста и thinking на выходе / 1 000 000 × 6
  + число выходных изображений 1K/2K × 0,0672
  + число выходных изображений 4K × 0,12

Ставка 0,00056 получена из 560 токенов на входное изображение и половины стандартного тарифа ввода. Она соответствует округленным 0,0006 USD в таблице. Если вы используете фактическую статистику входных токенов, уже включающую изображения, не прибавляйте их стоимость второй раз.

Рассмотрим расчетный пример, не счет за реальную генерацию: в каждом запросе 200 токенов текста, одно изображение-референс, 500 токенов текста и thinking на выходе и одно итоговое изображение 2K. Дополнительные инструменты, налоги и повторные запросы не учитываем.

Batch:    0,0002 + 0,00056 + 0,003 + 0,0672 = 0,07096 USD
Standard: 0,0004 + 0,00112 + 0,006 + 0,1344 = 0,14192 USD
Число таких запросовStandard, USDBatch, USDЭкономия, USD
10014,1927,0967,096
1 000141,9270,9670,96
5 000709,60354,80354,80
10 0001 419,20709,60709,60

При 5 000 таких запросов ежемесячно расчетная годовая экономия — 4 257,60 USD. Число 500 токенов здесь выбрано только для арифметики: фактические расходы thinking заранее так не фиксируются. У Pro рассуждение включено постоянно, а его токены оплачиваются и тогда, когда не показываются пользователю. Документация thinking при генерации изображений.

Для бюджета проекта нужен еще один знаменатель: стоимость пригодного изображения = все фактические расходы / число принятых изображений. Если пакет из примера стоил 70,96 USD, а для публикации подошли 900 изображений, получается около 0,07884 USD за пригодное изображение. Это условный сценарий, а не оценка качества модели. Успешный HTTP-ответ, успешное задание и принятый дизайнером результат — разные события. Расходы повторных попыток добавляйте по фактическому учету; общего обещания «ошибки бесплатны» эти источники не дают.

Пошаговое руководство по пакетной генерации изображений

Для первого запуска достаточно небольшой выборки и действующего платного доступа к Gemini Developer API. Выберите JSONL: Google рекомендует файловый способ для генерации изображений. Встроенные запросы подходят, если общий размер меньше 20 МБ; входной JSONL-файл может быть до 2 ГБ. Файловая пакетная генерация.

JSONL с уникальными ключами, сохранение job_name и возобновление того же Batch с проверкой каждого результата

Каждой строке нужен собственный key. Храните соответствие этого ключа товару, публикации или варианту дизайна у себя: порядок строк в результате не должен определять, к какому товару относится картинка. Запрос на изображение обязательно содержит responseModalities; без нужной модальности можно получить только текст.

1. Подготовьте JSONL с ключами и настройками изображений

Этот скрипт работает локально, использует только стандартную библиотеку Python и создает requests.jsonl:

python
import json
from pathlib import Path

tasks = {
    "vase-front": "Предметная фотография белой керамической вазы, вид спереди, серый фон.",
    "vase-detail": "Крупный план фактуры белой керамической вазы, мягкий боковой свет.",
}
with Path("requests.jsonl").open("w", encoding="utf-8") as output:
    for key, prompt in tasks.items():
        row = {
            "key": key,
            "request": {
                "contents": [{"role": "user", "parts": [{"text": prompt}]}],
                "generationConfig": {
                    "responseModalities": ["TEXT", "IMAGE"],
                    "imageConfig": {"imageSize": "2K", "aspectRatio": "1:1"},
                },
            },
        }
        output.write(json.dumps(row, ensure_ascii=False) + "\n")

generationConfig, responseModalities и imageConfig — имена полей сырого JSON запроса GenerateContent. В Python SDK для встроенного запроса настройки передаются через config, например response_modalities, а ключ — через metadata={"key": ...}. Эти две формы нельзя механически смешивать. Для Pro используйте 1K, 2K или 4K; наличие 512 в общей спецификации не означает поддержку этого размера данной моделью. Поля GenerateContent, пример встроенной генерации.

2. Отправьте один раз, затем продолжайте по имени задания

Сохраните следующий пример как batch_job.py. Для его сетевой части нужен установленный пакет google-genai и настроенная переменная GEMINI_API_KEY; ключ не записывается в файл состояния. Рабочая папка должна находиться на постоянном диске, а не во временном каталоге контейнера.

python
import hashlib
import json
import os
import sys
import time
from pathlib import Path
from google import genai
from google.genai import types

source = Path("requests.jsonl")
state_path = Path("batch-state.json")

def store(data, exclusive=False):
    target = state_path if exclusive else state_path.with_suffix(".tmp")
    with target.open("x" if exclusive else "w", encoding="utf-8") as handle:
        json.dump(data, handle, ensure_ascii=False)
        handle.flush()
        os.fsync(handle.fileno())
    if not exclusive:
        os.replace(target, state_path)

mode = sys.argv[1] if len(sys.argv) == 2 else ""
if mode not in {"submit", "resume"}:
    raise SystemExit("Использование: python batch_job.py submit|resume")

if mode == "submit":
    rows = [json.loads(line) for line in source.read_text("utf-8").splitlines() if line.strip()]
    keys = [row["key"] for row in rows]
    if not keys or len(keys) != len(set(keys)):
        raise SystemExit("Нужны непустой пакет и уникальные ключи")
    state = {
        "phase": "uploading",
        "submitted_at_unix": time.time(),
        "source_sha256": hashlib.sha256(source.read_bytes()).hexdigest(),
        "keys": keys,
    }
    # Существующий файл запрещает повторную отправку этим скриптом.
    store(state, exclusive=True)
    client = genai.Client()
    uploaded = client.files.upload(
        file=str(source),
        config=types.UploadFileConfig(mime_type="jsonl"),
    )
    state.update(phase="creating", input_name=uploaded.name)
    store(state)
    job = client.batches.create(
        model="gemini-3-pro-image",
        src=uploaded.name,
        config={"display_name": "vase-images-ru"},
    )
    state.update(phase="submitted", job_name=job.name)
    store(state)
else:
    state = json.loads(state_path.read_text("utf-8"))
    if not state.get("job_name"):
        raise SystemExit("Нет сохраненного job_name: сначала выясните результат отправки")
    client = genai.Client()

terminal = {
    "JOB_STATE_SUCCEEDED", "JOB_STATE_FAILED",
    "JOB_STATE_CANCELLED", "JOB_STATE_EXPIRED",
}
deadline = time.monotonic() + 15 * 60
while True:
    job = client.batches.get(name=state["job_name"])
    status = job.state.name
    print(state["job_name"], status)
    if status in terminal:
        break
    if time.monotonic() >= deadline:
        raise SystemExit("Ожидание остановлено; продолжите командой resume")
    time.sleep(min(60, max(0, deadline - time.monotonic())))

state.update(phase="terminal", status=status)
store(state)
if status != "JOB_STATE_SUCCEEDED":
    raise SystemExit(f"Задание завершилось без успеха: {status}; ошибка: {job.error}")
if not job.dest or not job.dest.file_name:
    raise SystemExit("Нет выходного файла: проверьте ответ задания")
data = client.files.download(file=job.dest.file_name)
Path("results.jsonl").write_bytes(data)
print("Сохранен results.jsonl; далее проверьте отдельные результаты")

Первый запуск — python batch_job.py submit. После остановки процесса или истечения локальных 15 минут — python batch_job.py resume в той же папке. Установленный в примере предел ограничивает время ожидания процесса; облачное задание продолжает работать. Поля uploaded.name, job.dest.file_name и возвращаемые при скачивании байты соответствуют файловому примеру SDK.

Создание задания не идемпотентно. Если соединение оборвалось во время create, сервер мог уже принять пакет. Поэтому файл состояния создается до сетевых действий, а повторный submit блокируется. При phase="creating" без job_name сначала сопоставьте задания своего проекта с сохраненным входным файлом и временем отправки; найдите созданное задание и сохраните его имя. Не удаляйте файл состояния только ради нового запуска. Локальная запись снижает риск дубля, но не делает сетевую отправку и запись на диск одной атомарной операцией. Свойства создания Batch.

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

3. Извлеките изображения и сохраните отчет по каждому ключу

Теперь сохраните этот независимый локальный обработчик как extract_images.py. Он читает results.jsonl как UTF-8, декодирует Base64 из сырого JSON и не обращается к API. Имена файлов получаются из хеша ключа, поэтому произвольный ключ не превращается в путь на диске.

python
import base64
import hashlib
import json
from pathlib import Path

state = json.loads(Path("batch-state.json").read_text("utf-8"))
expected = set(state["keys"])
seen = set()
output = Path("images")
output.mkdir(exist_ok=True)
report = []
extensions = {"image/png": ".png", "image/jpeg": ".jpg", "image/webp": ".webp"}

for number, line in enumerate(Path("results.jsonl").read_text("utf-8").splitlines(), 1):
    if not line.strip():
        continue
    try:
        row = json.loads(line)
        if not isinstance(row, dict):
            raise ValueError("Строка не является объектом")
    except (ValueError, TypeError) as error:
        report.append({"line": number, "status": "invalid_json", "detail": str(error)})
        continue
    key = row.get("key")
    if not isinstance(key, str) or key not in expected or key in seen:
        report.append({"line": number, "key": key, "status": "unknown_or_duplicate_key"})
        continue
    seen.add(key)
    if row.get("error") or row.get("status"):
        report.append({"key": key, "status": "request_error", "detail": row})
        continue
    saved = []
    problems = []
    response = row.get("response") or {}
    for candidate in response.get("candidates") or []:
        content = candidate.get("content") or {}
        for part in content.get("parts") or []:
            image = part.get("inlineData")
            if part.get("thought") or not image:
                continue
            suffix = extensions.get(image.get("mimeType"))
            if not suffix:
                problems.append("unsupported_mime")
                continue
            try:
                payload = base64.b64decode(image["data"], validate=True)
                if not payload:
                    raise ValueError("Пустые данные изображения")
            except (ValueError, KeyError, TypeError) as error:
                problems.append(str(error))
                continue
            stem = hashlib.sha256(key.encode("utf-8")).hexdigest()
            path = output / f"{stem}-{len(saved)}{suffix}"
            path.write_bytes(payload)
            saved.append(str(path))
    report.append({
        "key": key, "status": "saved" if saved else "no_final_image",
        "files": saved, "problems": problems, "response": response,
    })

for key in sorted(expected - seen):
    report.append({"key": key, "status": "missing_result"})
Path("result-report.json").write_text(
    json.dumps(report, ensure_ascii=False, indent=2), encoding="utf-8"
)
print("Проверьте images/ и result-report.json")

Запуск: python extract_images.py. Проверьте не только число сохраненных файлов, но и каждый результат: открывается ли изображение, подходит ли размер, соответствует ли оно задаче. saved означает, что байты записаны на диск, а не что картинка прошла проверку качества.

Сырой JSON содержит Base64 в inlineData.data. Для встроенных результатов Python SDK другой путь: job.dest.inlined_responses, затем response или error, а изображение можно получить через part.as_image(). Не декодируйте полученный через SDK объект изображения повторно как Base64. Два способа получения изображений.

Примеры подготовлены по официальной документации. Локально проверены синтаксис, арифметика и обработчик на искусственных JSONL-ответах; реальная генерация, прием запроса сервером, сроки выполнения и списание средств в рамках этой проверки не тестировались.

Продвинутая пакетная обработка — мониторинг, ошибки и оптимизация

Выбор синхронной генерации или Batch по сроку задачи, сверка неизвестной отправки и проверка отдельных ключей

Успешное задание не означает, что все изображения готовы

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

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

Тайм-аут, отмена и истечение срока — разные события

Состояние или событиеЧто делать
JOB_STATE_PENDING / JOB_STATE_RUNNINGПродолжать наблюдение по сохраненному имени задания
Локальный тайм-аут или обрыв связиВозобновить проверку; не считать задание отмененным
JOB_STATE_SUCCEEDEDСкачать результаты и разобрать каждую строку
JOB_STATE_FAILEDСохранить ошибку задания и определить, что нужно исправить
JOB_STATE_CANCELLEDПроверить окончательный статус и доступные сведения; не обещать нулевую оплату
JOB_STATE_EXPIREDЗадание истекло без результатов; проверить причину и бюджет нового запуска

По документации задания истекают после 48 часов в ожидании или выполнении, а результаты успешного задания доступны для скачивания шесть недель. Отмена прекращает обработку новых запросов; из этого не следует возврат всех уже понесенных расходов. Статусы и хранение результатов.

Если генерация входит в более крупный конвейер, заранее резервируйте бюджет для пакета и не освобождайте его только потому, что клиент перестал ждать. Общий принцип учета неизвестных списаний разобран в руководстве по ограничению расходов LLM-агентов.

Планируйте объем по квотам проекта и сроку задачи

У Batch собственные ограничения: до 100 одновременно выполняемых заданий, входной файл до 2 ГБ, файловое хранилище до 20 ГБ и лимит токенов в очереди для каждой модели, суммарно по активным заданиям. Фактические квоты зависят от проекта и уровня доступа. Таблица, в которой еще указана preview-версия модели, не гарантирует те же значения для стабильного ID в вашем проекте. Несколько API-ключей одного проекта не создают независимые квоты. Лимиты Batch API.

Делите задания так, чтобы успевать скачивать результаты, проверять отдельные ключи и повторять только нужные запросы. Универсального оптимального пакета «1 000–5 000 картинок» из этих условий не следует. Начните с небольшого набора, измерьте свои сроки и долю принятых изображений, затем увеличивайте объем в пределах квот. Для жесткого срока заложите время на проверку и повторные попытки сверх целевых 24 часов.

Можно ли снизить расходы еще сильнее?

Можно уменьшить саму задачу: убрать ненужные референсы, не генерировать варианты, которые никто не проверит, и выбирать 2K вместо 4K, если 2K достаточно для конечного макета. По точной стоимости выхода переход с 4K на 2K снижает эту часть расходов с 0,12 до 0,0672 USD, то есть на 44 %. Это смена разрешения, а не дополнительная скидка за тот же результат. Между 1K и 2K официальная цена выхода одинакова. Тарифы по размерам.

Кэширование, Flex и Priority для gemini-3-pro-image в карточке модели отмечены как неподдерживаемые. Поэтому строки Flex/Priority в общей таблице цен не служат основанием включать эти режимы, а обещание «Batch плюс кэширование дает 80 %» для этой модели использовать нельзя.

Сторонний сервис — отдельный маршрут со своими ценами, условиями списания и обработкой данных. Его тариф не складывается со скидкой официального Batch. Если распределять работу между маршрутами, считайте расходы каждой части отдельно: средняя цена при фиксированных ставках не может стать ниже самой дешевой из них без дополнительного, отдельно подтвержденного условия.

Часто задаваемые вопросы

Сколько стоит одно изображение Gemini 3 Pro Image через Batch API?

В округленной таблице Google выход 1K/2K стоит 0,067 USD, 4K — 0,12 USD. Полная стоимость запроса включает ввод, текст и thinking; для расчета по токенам выход 1K/2K составляет 0,0672 USD. Повторные генерации увеличивают цену пригодного результата.

Batch API гарантирует готовые изображения через 24 часа?

Нет. Google указывает целевой срок 24 часа, зависящий от нагрузки и размера задания. Это не обещание завершения к точному времени. Для интерактивной генерации такой режим неудобен; для запланированной работы оставьте время на проверку результатов.

Можно ли использовать изображения-референсы?

Да, модель принимает изображения и текст, а файловые Batch-запросы поддерживают мультимодальный ввод. Передавайте все данные, нужные конкретному запросу: строка JSONL не продолжает автоматически предыдущую строку. Референсы увеличивают входные расходы. Карточка модели, файловый ввод Batch.

Почему задание завершилось успешно, но картинок меньше, чем запросов?

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

Можно ли повторить отправку после сетевого тайм-аута?

Не повторяйте create вслепую: создание не идемпотентно. Если имя задания сохранено, используйте его для продолжения. Если ответ на создание потерян, сначала выясните, был ли пакет принят сервером. Тайм-аут клиента сам по себе не доказывает отсутствие задания или списания.

Источники6

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

  1. 1.Цены Googleai.google.dev/gemini-api/docs/pricing
  2. 2.условия Batch APIai.google.dev/gemini-api/docs/batch-api
  3. 3.карточкеai.google.dev/gemini-api/docs/models/gemini-3-pro-image
  4. 4.Документация thinking при генерации изображенийai.google.dev/gemini-api/docs/image-generation
  5. 5.Поля GenerateContentai.google.dev/api/generate-content
  6. 6.Лимиты Batch APIai.google.dev/gemini-api/docs/rate-limits