KADR Получить ключ

KADR API

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

Базовый адрес: https://<ваш-домен> (на время разработки — http://127.0.0.1:8777).


Оглавление


Ключ доступа

Выпускается на странице Кабинет → API. Показывается один раз, при выпуске: на сервере хранится не сам ключ, а его отпечаток, поэтому восстановить строку невозможно. Потеряли — выпустите новый и отзовите старый.

Вид ключа: kadr_live_ и 43 символа.

Передавать можно двумя способами, оба равнозначны:

Authorization: Bearer kadr_live_xxxxxxxx
X-API-Key: kadr_live_xxxxxxxx

Что можно делать ключом

ДействиеКлючомТолько после входа в кабинет
Запускать генерациюда
Смотреть состояние своих заданийда
Читать список моделей и балансда
Пополнять балансда
Выпускать и отзывать ключида
Принимать условия использованияда

Сужение намеренное: ключ живёт на чужих серверах, и его утечка не должна давать ничего, кроме траты уже оплаченных кредитов. Максимум ключей на аккаунт — 10.


Кредиты и цены

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

1 USDT = 200 кредитов, и один кредит — одна картинка любой моделью. Минимальное пополнение — 20 кредитов (0.1 USDT).

Модель (model)Кредитов за кадрАпскейл 2×РеференсыСоотношения сторонseed
nbpro — Nano Banana Pro12до 1016:9, 4:3, 1:1, 3:4, 9:16да
nb2 — Nano Banana 212до 10те же пятьда
nb2lite — Nano Banana 2 Lite12до 10те же пятьда
gpt — GPT-Image1до 10выбора нетнет
flower — Flower11 (режим правки)16:9, 1:1, 9:16нет

Апскейл 2× — единственное, что стоит дороже кадра: это вторая операция у поставщика, и она считается за две картинки.

Списание происходит в момент постановки в очередь, за все запрошенные кадры сразу. Если модель не справилась с конкретным кадром, кредиты за него возвращаются на баланс автоматически — в ответе такого кадра появится refunded_credits.

Актуальные значения всегда отдаёт GET /api/v1/models: таблица выше может отстать, ответ сервера — нет.


GET /api/v1/models

Список моделей с ценами и возможностями. Ключ не требуется.

{
  "models": [
    {
      "id": "nbpro",
      "name": "Nano Banana Pro",
      "credits": 1,
      "upscale": true,
      "upscale_credits": 2,
      "seed": true,
      "aspect_ratios": ["16:9", "4:3", "1:1", "3:4", "9:16"],
      "max_references": 10,
      "min_references_with_edit": 0,
      "about": "Лучшее качество. То, чем сделана витрина."
    }
  ]
}

Возможности моделей различаются, и сервер это проверяет: соотношение сторон, которого у модели нет, отклоняется с кодом 400.

С seed иначе, и это важно знать заранее: модели, у которой seed не поддержан, он передаётся молча и просто ни на что не влияет. Отказа не будет. Значит и «зафиксированный» прогон на такой модели даст разные кадры, а программа об этом не узнает. Смотрите seed: true|false в ответе этого метода до отправки заявки — по нему и стройте интерфейс.


GET /api/v1/usage

Баланс аккаунта, которому принадлежит ключ.

{
  "balance": 3691,
  "currency": "credits",
  "rate": {"credits_per_usdt": 200},
  "email": "you@example.com"
}

POST /api/v1/generations

Запускает генерацию. Отвечает сразу, не дожидаясь готовности: код 202 и задание со списком кадров в состоянии queued.

Поля запроса

ПолеТипОбяз.Описание
modelстрокадаидентификатор из /api/v1/models
promptстрокадаописание кадра, до 1200 символов
countцелоенетсколько кадров, 1–25, по умолчанию 1
aspect_ratioстроказависитобязательно, если у модели непустой aspect_ratios
upscaleбулевонетувеличение 2×, удваивает цену; только там, где upscale: true
seedцелоенетфиксированное зерно; к каждому кадру прибавляется его номер
referencesмассив строкнетреференсы как data:image/...;base64,...
heightцелоенетподгон высоты после генерации, 256–2048

Ответ

{
  "id": "c18c58b098c8",
  "status": "running",
  "model": "flower",
  "model_name": "Flower",
  "prompt": "серый бетонный куб",
  "aspect_ratio": "1:1",
  "count": 2,
  "charged_credits": 2,
  "credits_per_image": 1,
  "upscale": false,
  "images": [
    {"id": "67d5f8b8118f", "status": "queued", "url": null,
     "error": null, "refunded_credits": null}
  ],
  "balance": 3691
}

Про референсы

Передаются как строки data: вместе с запросом. Тело запроса ограничено 40 МБ, поэтому изображения стоит уменьшать до 1280 px по длинной стороне — качество подсказки от этого не страдает, а запрос перестаёт быть многомегабайтным.

У flower референс включает другой режим работы модели (правка изображения) и требует ровно одного файла.


GET /api/v1/generations/{id}

Состояние задания. Тело такое же, как у запуска, но с заполненными url.

Значения status у задания:

ЗначениеСмысл
queuedни один кадр ещё не начат
runningчасть кадров считается
succeededвсе кадры готовы
failedвсе кадры не удались, кредиты возвращены
partialчасть готова, часть не удалась

У отдельного кадра — те же значения, кроме partial.

Ссылка url абсолютная и ведёт на файл .jpg.

Кадр, сделанный по ключу API, живёт 15 минут. После этого файл удаляется с диска, ссылка отдаёт 404, и восстановить его нельзя. Скачивайте сразу, как получили succeeded, — на нашем хранилище ничего строить нельзя.

Пятнадцать минут — не оговорка и не экономия на диске. Кадры, сделанные через API, забирает программа, и она забирает их сразу; человек их руками не делал и в галерее не ждёт. Кадры, сделанные в кабинете руками, живут 3 часа — это другой срок и другой сценарий.

(До 03.09.2026 здесь было написано «Файлы хранятся у нас и не протухают». Это было неправдой уже тогда: интегратор, построивший хранилище на наших ссылках, терял всё через четверть часа.)


Ошибки

Все ошибки приходят одинаково:

{"error": {"code": "insufficient_credits",
           "message": "Не хватает кредитов: нужно 20, на балансе 12"}}

code — для кода, message — для человека, по-русски.

HTTPcodeКогда
400bad_requestзаявка не прошла проверку: нет промпта, чужое соотношение сторон, seed там, где он не поддержан
400bad_jsonтело запроса не разобралось
400bad_countcount не целое число
401no_keyключ не передан
401bad_keyключ неизвестен или отозван
402insufficient_creditsне хватает кредитов; списания не было
403account_disabledаккаунт ключа недоступен
404not_foundзадания с таким id у этого аккаунта нет
413payload_too_largeтело больше 40 МБ
422rejected_by_moderationзапрос отклонён правилами; кредиты не списаны
429too_many_requestsпревышен предел частоты, см. ниже; списания не было

Отказ модерации и нехватка кредитов не стоят ничего: проверка идёт до списания.


Как правильно опрашивать

Генерация занимает от нескольких секунд до пары минут в зависимости от модели. Правильный порядок:

  1. POST /api/v1/generations — получить id;
  2. опрашивать GET /api/v1/generations/{id} раз в 3–5 секунд;
  3. остановиться, когда status стал succeeded, failed или partial.

Чаще раза в секунду опрашивать бессмысленно: состояние меняется реже.

Параллельные задания разрешены — сервис сам распределяет их по свободным потокам генерации и ставит в очередь то, что не поместилось. Если ёмкость занята, кадр честно подождёт в состоянии queued, а не получит отказ.

Предел частоты

120 запросов в минуту на все /api/v1/* вместе, считая опросы статуса. Сверх предела приходит 429 с телом обычного вида:

{"error": {"code": "too_many_requests",
           "message": "Слишком часто. Подождите минуту."}}

Кредиты при этом не списываются. Опрос раз в 3–5 секунд на десяток одновременных заданий в предел укладывается с запасом; если у вас их сотни — опрашивайте реже, а не чаще.


Примеры

curl

curl -X POST https://ваш-домен/api/v1/generations \
  -H "Authorization: Bearer kadr_live_xxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{"model":"nbpro","prompt":"маяк в шторм, плёночное зерно","count":4,"aspect_ratio":"16:9"}'

Python

import time, requests

КЛЮЧ = "kadr_live_xxxxxxxx"
БАЗА = "https://ваш-домен"
шапка = {"Authorization": "Bearer " + КЛЮЧ}

задание = requests.post(БАЗА + "/api/v1/generations", headers=шапка, json={
    "model": "nbpro",
    "prompt": "маяк в шторм, плёночное зерно",
    "count": 4,
    "aspect_ratio": "16:9",
}).json()

if "error" in задание:
    raise SystemExit(задание["error"]["message"])

пока = задание
while пока["status"] in ("queued", "running"):
    time.sleep(4)
    пока = requests.get(
        "%s/api/v1/generations/%s" % (БАЗА, задание["id"]),
        headers=шапка).json()

for кадр in пока["images"]:
    if кадр["status"] == "succeeded":
        имя = кадр["id"] + ".jpg"
        open(имя, "wb").write(requests.get(кадр["url"]).content)
        print("скачал", имя)
    else:
        print("не вышел кадр:", кадр["error"])

JavaScript

const КЛЮЧ = 'kadr_live_xxxxxxxx';
const БАЗА = 'https://ваш-домен';
const шапка = {
  'Authorization': 'Bearer ' + КЛЮЧ,
  'Content-Type': 'application/json',
};

async function сгенерировать(промпт, сколько = 1) {
  const ответ = await fetch(БАЗА + '/api/v1/generations', {
    method: 'POST',
    headers: шапка,
    body: JSON.stringify({
      model: 'flower', prompt: промпт, count: сколько, aspect_ratio: '1:1',
    }),
  });
  let задание = await ответ.json();
  if (задание.error) throw new Error(задание.error.message);

  while (задание.status === 'queued' || задание.status === 'running') {
    await new Promise(r => setTimeout(r, 4000));
    задание = await (await fetch(
      `${БАЗА}/api/v1/generations/${задание.id}`, { headers: шапка }
    )).json();
  }
  return задание.images.filter(к => к.status === 'succeeded').map(к => к.url);
}

Что API намеренно не делает

небыстрая, держать соединение открытым минуту — плохой способ: рвётся связь, срабатывают таймауты прокси. Поэтому задание и опрос.

понадобятся, скажите — это следующий разумный шаг.

меняют историю аккаунта, и делать их из встроенного ключа опаснее, чем полезнее.