Генерация картинок

POST/v1/images/generations

Картинки приходят в base64 тем же ответом; долгая генерация возвращает джоб с id (202).

Запрос

modelstringbodyобязательно
id модели картинок из каталога
promptstringbodyобязательно
что нарисовать; шлюз проверяет наличие, длину — модель (EMPTY_PROMPT)
nintegerbody= 1
сколько картинок, 1–10; задаёт резерв, списание — по факту
aspect_ratiostringbody= 1:1
пропорции; набор зависит от модели
1:1 · 16:9 · 9:16 · 4:3 · 3:4
resolutionstringbody= 1K
разрешение; есть не у всех моделей (у nano-banana-pro есть)
1K · 2K · 4K
orientationstringbody= square
ориентация — у моделей, которые не принимают aspect_ratio
landscape · portrait · square
imagestring|arraybody
референс: URL, data-URL или голый base64; массив — несколько
response_formatstringbody
принимается ради совместимости и игнорируется

Ответы

200

{
  "created": 0,
  "cost_usd": "0.027",
  "data": [ { "b64_json": "iVBORw0KGgoAAAANSUhEUgAA…" } ]
}

202

{ "id": "img_9f2c1b7e…", "status": "processing", "estimated_cost_usd": "0.027" }

тело не JSON, нет prompt, модель/значение/n вне набора, референс > 80 МБ

FILE_DOWNLOAD_FAILED — сабмит отклонён: 4xx провайдера отдаём его статусом и кодом, резерв освобождаем

ключа нет в заголовке или он не наш

остатка не хватает на худшую цену этого запроса

502

{
  "error": { "message": "The request was blocked by the content safety filter.",
             "type": "generation_error", "code": "GEMINI_RAI_MEDIA_FILTERED" },
  "cost_usd": "0"
}

провайдер взял джобу и провалил генерацию; его код — в error.code

сабмит упал по 5xx или сети: принята джоба или нет — неизвестно

Подробности

Нужен ключ в заголовке Authorization: Bearer или x-api-key.

Шлюз создаёт джобу, резервирует под неё деньги (под per-user локом, поэтому два одновременных запроса не пройдут по одному остатку), отправляет её провайдеру и сам опрашивает его до 150 с. Успел — приходит 200 с картинками в b64_json; не успел — 202 с { id, status, estimated_cost_usd }, и результат забирается через GET /v1/images/jobs/{job_id}. Резерв при этом остаётся на месте, так что деньги не «освобождаются» на время генерации.

Ссылок на CDN провайдера в ответе не бывает никогда: если провайдер вернул URL, шлюз скачивает картинку сам и отдаёт base64. Поэтому response_format принимается и игнорируется — результат всегда b64_json. Неизвестные поля тела тоже молча отбрасываются: наружу уезжает только то, что есть в аллоулисте модели.

Про поля. Неизвестная или выключенная админом модель, отсутствующий prompt, нецелое или выходящее за 1–10 n и значение параметра вне набора своей модели — всё это 400 у нас, до провайдера. Набор aspect_ratio зависит от модели: у части он шире (плюс 3:2, 2:3, 21:9), а часть моделей вместо aspect_ratio принимает orientation; поле, которого у модели нет, молча отбрасывается. На цену не влияет ни resolution, ни то, чем задан кадр: тариф — за картинку. n провайдеру не уезжает вовсе — он задаёт размер резерва, а списывается по числу вернувшихся картинок (не вернулось ни одной — по n). Отказ провайдера на сабмите по 5xx или сети оставляет деньги в резерве и приносит id в теле ошибки: джобу добивает реконсайлер, он же освободит резерв, если провайдер её так и не взял. А вот 4xx провайдера — окончательный отказ: джоба проваливается сразу, и резерв освобождается, не дожидаясь реконсайлера.

Деньги: у завершённой генерации в ответе cost_usd — ровно то, что списано, у идущей estimated_cost_usd — размер резерва. Провалившаяся генерация не тарифицируется и приходит с "cost_usd": "0". Счёт за медиа ведём мы сами, отдельно от текста. Ошибки приходят одной формой; code в ней есть там, где его дал провайдер, а у отказа на сабмите добавляется id джобы: { "error": { "message": "...", "type": "...", "code": "PROVIDER_CODE" } }.

Референс-картинки (image-to-image, тот же персонаж или стиль) передаются в теле как image — одна строка или массив; принимаются также images, image_url, image_urls, input_image, init_image, ref_images. Значением может быть публичный URL, data-URL или голый base64, и мешать формы в одном запросе можно: URL шлюз сам не скачивает и передаёт провайдеру ссылкой, а base64 декодирует и передаёт байтами. Байты шлюз узнаёт по сигнатуре (PNG, JPEG, GIF, WebP) и режет на 80 МБ на каждый вход — больше даёт 400 ещё до провайдера. Вход, который прошёл наш предел, может отклонить уже модель по своим правилам: тогда её код приходит в error.codeFILE_TOO_LARGE по размеру, FILE_TYPE_NOT_ALLOWED по типу. Количество референсов шлюз не ограничивает; сколько из них учтёт модель — на её стороне. Значение, которое не разобралось ни как URL, ни как base64, молча отбрасывается: запрос уйдёт без этого референса и без ошибки.

Срок жизни результата — 7 дней. Дальше байты уезжают из базы в архив, result_url очищается, и тот же GET /v1/images/jobs/{job_id} отвечает { "status": "completed", "archived": true, "data": [] }. Это «срок вышел», а не «генерация ничего не дала» — сохраняй картинки у себя сразу, как получил. Строка джобы и её деньги не удаляются никогда: ретеншен уносит только payload.

Примеры кода
curl https://api.teamtoken.store/v1/images/generations \
  -H "Authorization: Bearer sk-…" \
  -H "Content-Type: application/json" \
  -d '{ "model": "nano-banana-pro", "prompt": "a red cube on white", "aspect_ratio": "1:1", "resolution": "1K" }'
curl https://api.teamtoken.store/v1/images/generations \
  -H "Authorization: Bearer sk-…" \
  -H "Content-Type: application/json" \
  -d '{ "model": "nano-banana-pro",
        "prompt": "the same person, in a forest, golden hour",
        "images": ["https://example.com/ref1.jpg", "data:image/jpeg;base64,/9j/4AAQ..."],
        "aspect_ratio": "1:1" }'
import requests

r = requests.post(
    "https://api.teamtoken.store/v1/images/generations",
    headers={"Authorization": "Bearer sk-…"},
    json={"model": "nano-banana-pro", "prompt": "a red cube on white",
          "aspect_ratio": "1:1", "resolution": "1K"},
    timeout=180,  # the gateway polls the provider for up to 150s
)
body = r.json()

if r.status_code == 202:
    # too slow for one request: the job keeps running and the money stays reserved
    print(body["id"], body["estimated_cost_usd"])
else:
    print(body["cost_usd"], body["data"][0]["b64_json"][:32])
Запрос
https://api.teamtoken.store/v1

Панель отправляет запрос с этого домена; в своём коде используйте адрес выше.

Ключ никуда не сохраняется: он живёт в этой вкладке до перезагрузки страницы.

id модели картинок из каталога

что нарисовать; шлюз проверяет наличие, длину — модель (EMPTY_PROMPT)

сколько картинок, 1–10; задаёт резерв, списание — по факту

пропорции; набор зависит от модели

разрешение; есть не у всех моделей (у nano-banana-pro есть)

ориентация — у моделей, которые не принимают aspect_ratio

референс: URL, data-URL или голый base64; массив — несколько

принимается ради совместимости и игнорируется

Запрос уйдёт по-настоящему. Генерация тарифицируется, и деньги резервируются сразу при постановке в очередь.

Ответ

Нажмите «Отправить запрос» выше — и здесь появится ответ.