04Начало работы
Запросы в формате Gemini APIНовое
Модели Nano Banana принимают запросы в формате Gemini API от Google: те же маршруты, то же тело запроса и тот же вид ответа. Меняются только адрес и ключ.
Что это и какие есть маршруты
Это отдельный путь для моделей Nano Banana. Он принимает запросы в формате Gemini API от Google на корне хоста https:// и передаёт их тому же приёму, что и обычный API (/api/): проверки, цены, лимиты и баланс те же.
| Маршрут | Что делает |
|---|---|
POST /v1beta/ | Создаёт картинку и возвращает один ответ. Рекомендуемый маршрут. |
POST /v1beta/ | То же, но ответом-потоком. С параметром ?alt=sse приходят события SSE, без него — JSON-массив Google, в котором один ответ. |
GET /v1beta/ | Список моделей, доступных вашему ключу. |
GET /v1beta/ | Одна модель. |
Под /v1 работают только два маршрута POST: POST /v1/ и POST /v1/. Остальные пути /v1 отвечают как неизвестный путь.
Ключ
Ключ Rendergrid можно передать тремя способами: в заголовке x-goog-api-key, в заголовке Authorization: Bearer или в параметре адреса ?key=. Сервер берёт первый непустой в этом порядке. Ключ не попадает в наши журналы.
Отозванный, истёкший, выключенный или заблокированный ключ ведёт себя так же, как в обычном API: см. «Ключи и вход».
Примеры
Ключ в примерах читается из переменной окружения RENDERGRID_API_KEY. Картинка приходит в ответе в base64, в поле inlineData.data.
Для Python и Node.js взята библиотека Google Gen AI SDK: pip install google-genai и npm install @google/genai. Клиент создаётся с нашим адресом https:// и ключом Rendergrid. Пример для Node.js — модуль ES (файл .mjs): в нём await стоит на верхнем уровне.
Один ответ — 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")); } }
В Python для долгих генераций рекомендуем именно generate_content, без потока.
Поток — streamGenerateContent с параметром alt=sse (curl и 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")); } } }
Тайм-аут клиента в примерах — 620 секунд, по правилу из раздела «Ожидание ответа».
Остальные маршруты. Список моделей и одна модель:
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"
Под /v1 тело запроса лежит в файле request.json: это тело из первого примера.
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
Модели
Вызывать можно идентификаторы из таблицы. Идентификаторы Google и наши ведут к одной и той же модели Rendergrid; цена и баланс — как у этой модели в обычном API.
| Идентификатор в запросе | Модель 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/ показывает только то, что можно вызвать вашим ключом: сначала идентификатор Google, затем наш. Варианты с -preview принимаются, но в список не входят.
Если у аккаунта нет права на модель, ответ — 403. Неизвестная или не открытая модель — 404. Других моделей на этом пути нет: модели GPT отвечают 404.
Запрос
Тело запроса — тело Google generateContent. Что из него учитывается:
- Части. Часть — это
text,inlineData(mimeTypeиdataв base64) илиfileData(fileUriи, по желанию,mimeType). АдресfileUri— только http или https; его проверяют так же, как адреса образцов в обычном API (см. «Образцы»). - Текст запроса. Это текстовые части последней реплики пользователя, склеенные переводом строки; перед ними через пустую строку идёт текст
systemInstruction. - Образцы. Образцы — все картинки всех реплик, и пользователя, и модели, по порядку. Встроенные картинки — только PNG, JPEG и WebP; каждая загружается один раз и передаётся дальше ссылкой, как любая загрузка. Встроенные картинки и
fileUriв одном запросе вместе слать нельзя: ответ400. - Формат кадра.
generationConfig.imageConfig.aspectRatio. Допустимые значения те же, что уaspect_ratioв обычном API (см. «Модели и параметры»). Без него —auto, если в запросе есть образцы, и1:1, если их нет; уnano-banana-2-liteрежимаautoнет, и кадр получается1:1. - Размер.
generationConfig.imageConfig.imageSize:1K,2Kили4K. Без него —1K, а не2K, как в обычном API. Размер, которого у модели нет (см. таблицу в разделе «Ответ»), обычный приём отклоняет:400. - Формат файла.
generationConfig.imageConfig.imageOutputOptions.mimeType:image/pngилиimage/jpeg. Без него файл приходит в формате модели, без перекодирования. Другое значение —400. - Варианты и модальности.
responseModalitiesдолжен содержатьIMAGE(толькоTEXT—400);candidateCountможет быть только 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" }
}
}Эти поля принимаются и игнорируются: tools, safetySettings, thinkingConfig, temperature, topP, topK, seed, thoughtSignature, cachedContent, labels. Они не влияют на результат и ошибки не вызывают.
Пределы. Текст запроса — не больше 20 000 символов; образцов — не больше 14; встроенная картинка — не больше 25 МиБ после декодирования, как загрузка файла (см. «Образцы»); тело запроса — не больше 64 МиБ. Превышение — 400 INVALID_ARGUMENT, для тела — 413 INVALID_ARGUMENT. Тело такого размера может остановить сервер входа раньше приёма: тогда ответ — его собственная страница, а не конверт Google.
Ответ
Успешный ответ — GenerateContentResponse Google: картинка лежит в candidates[0].content.parts[0].inlineData, а finishReason равен STOP. Тип картинки, mimeType, определяется по самим байтам файла.
{
"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 — идентификатор модели из вашего запроса, с -preview, если вы его указали. responseId — id запроса, он же в заголовке x-rendergrid-request-id. Если приём не успел ответить за 90 секунд и ответ 200 ушёл раньше, чем появился id, заголовка нет, и id только в responseId.
usageMetadata: promptTokenCount — оценка, а не счёт токенов модели: длина запроса в символах, делённая на 4, с округлением вверх. candidatesTokenCount и totalTokenCount считаются по таблице ниже; candidatesTokensDetails несёт те же токены картинки. Токены нужны клиентам Gemini для учёта, цена запроса от них не зависит. Прочерк в таблице — размера у модели нет.
| Модель | 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 | — | — |
Ожидание ответа
Картинка создаётся не мгновенно, и соединение нужно держать открытым. Сроки:
| Что | Значение |
|---|---|
| Сервер молчит до первого байта | 90 секунд |
| Сигнал «жив» после этого | каждые 15 секунд |
| Предел ожидания | 600 секунд от приёма запроса |
- Первые 90 секунд сервер ничего не отправляет: ответ, готовый раньше, приходит со своим кодом HTTP, ошибка тоже.
- Если к 90-й секунде картинки нет, сервер отвечает
200и каждые 15 секунд шлёт keep-alive — знак, что запрос жив, а затем ответ. - После такого
200ошибка приходит в теле: объектом ошибки Google, в SSE — событиемdata:. - Предел — 600 секунд от приёма запроса: тогда в теле приходит
504 DEADLINE_EXCEEDEDс текстомGeneration timed out., и сумма возвращается. Кодами HTTP 502 и 504 этот путь не отвечает; генерация, прерванная по тайм-ауту до200, отвечает503 UNAVAILABLE. - Ставьте в клиенте тайм-аут чтения больше 90 секунд, а общий — не меньше 610: предел сервера — 600 секунд, и ответ об ошибке по пределу должен успеть дойти. Запрос, оборванный клиентом, оплачен.
Деньги
- Сумма списывается при приёме запроса, до начала работы, — как у такого же запроса к API и по той же цене.
- Картинка оплачивается, как только она создана, — как у Google.
- Если соединение оборвалось на вашей стороне — пока запрос ждал или пока шёл ответ, — списание остаётся: картинка создаётся до конца, она доступна по id запроса (
GET /api/) и в истории аккаунта.public/ v1/ creations/ {id} - Если картинку не доставили по нашей вине — истёк предел в 600 секунд, ответ сорвался на нашей стороне или сервер останавливался, — сумма возвращается на баланс автоматически, один раз.
- Неудачный запрос и отказ модерации бесплатны, как в обычном API: сумма возвращается на баланс.
- Повтор запроса — новый запрос и новое списание: ключа идемпотентности на этом пути нет.
- Если на балансе не хватает денег, ответ —
429 RESOURCE_EXHAUSTED(в обычном API —402).
Ошибки
Ошибка приходит в конверте Google: error.code, error.message и error.status.
{
"error": {
"code": 404,
"message": "The requested model was not found.",
"status": "NOT_FOUND"
}
}| Код HTTP | Статус Google | Когда |
|---|---|---|
400 | INVALID_ARGUMENT | Тело, поля или образцы не прошли проверку (см. «Запрос»), либо запрос отклонил обычный приём. |
401 | UNAUTHENTICATED | Ключа нет или он недействителен; текст — как в обычном API. |
403 | PERMISSION_DENIED | Аккаунт заблокирован, у ключа нет права на эту модель или путь закрыт для аккаунта. |
404 | NOT_FOUND | Неизвестная модель или путь; моделей GPT на этом пути нет. |
413 | INVALID_ARGUMENT | Тело запроса больше 64 МиБ. |
429 | RESOURCE_EXHAUSTED | Лимит частоты, лимит заданий в работе, блокировка при ошибках доступа или не хватает денег на балансе; текст ответа говорит, что именно. Пауза до повтора — в заголовке Retry-After. |
500 | INTERNAL | Внутренняя ошибка или неудачная генерация; деньги за неудачу возвращаются. |
503 | UNAVAILABLE | Сервис временно недоступен, генерация не уложилась по времени до ответа 200 или встроенные картинки не успели загрузиться. В последнем случае приходит Retry-After: 5, и деньги не списаны. |
504 | DEADLINE_EXCEEDED | Только в теле ответа: после 200, когда достигнут предел в 600 секунд. Сумма возвращается. |
Ответы 502 и 504 этот путь как код HTTP не отдаёт. Предел в 600 секунд приходит как 504 внутри тела уже начатого ответа 200:
{
"error": {
"code": 504,
"message": "Generation timed out.",
"status": "DEADLINE_EXCEEDED"
}
}Тексты ошибок, которые даёт сам приём (401, 429, отказы проверки), — как в обычном API: см. «Коды ошибок».
Отказы модерации
Отказ модерации приходит как 200, как у Google:
finishReason: "IMAGE_SAFETY"и ни одной части — модель отказала по результату;promptFeedback.blockReason: "SAFETY"— отказ по запросу или по образцу;finishReason: "NO_IMAGE"— картинки нет.
{
"candidates": [{ "finishReason": "IMAGE_SAFETY", "index": 0 }],
"usageMetadata": { "promptTokenCount": 11, "totalTokenCount": 11 },
"modelVersion": "gemini-3-pro-image",
"responseId": "5b0214ae-9e39-460a-820a-f6ae3e24400b"
}Такие ответы бесплатны, как в обычном API: сумма возвращается на баланс.
Лимиты
Действуют лимиты обычного API: частота запросов, защита от всплесков и число заданий в работе (см. «Лимиты»). Каждая встроенная картинка — отдельная загрузка: одна единица частоты и одна единица лимита загрузок. Место в лимите заданий занято, пока идёт генерация, а не пока открыто соединение.