Генерация изображений

OpenRouter- и OpenAI-совместимый Images API, параметры запросов и обработка ответов

Для image-only моделей используйте отдельный Images API. По умолчанию генерация выполняется синхронно, а готовое изображение возвращается как base64 в поле data[].b64_json.

Endpoint

POST
/v1/images

Канонический OpenRouter-совместимый путь

GET
/v1/images/models

OpenRouter-совместимый каталог image-моделей

POST
/v1/images/generations

OpenAI-совместимый путь

POST
/images/generations

Совместимый путь без префикса /v1

POST
/images

OpenRouter-совместимый путь без префикса /v1

Нужный scope
API-ключ должен иметь scope gateway:write или admin:*. Если у ключа задан allowlist моделей, добавьте в него точный ID image-модели.

Для долгой генерации используйте асинхронный режим: ответ 202, опрос задания и отдельное скачивание. Без Prefer: respond-async контракт не меняется.

Пример синхронного запроса

bash
curl -X POST "https://api.kodikrouter.ru/v1/images/generations" \
  -H "Authorization: Bearer $KODIK_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "qwen/qwen-image-3-pro",
    "prompt": "Кинематографичный вид Москвы в синий час",
    "n": 1,
    "size": "1024x1024",
    "response_format": "b64_json"
  }'

Параметры

modelreq
string
Точный ID модели с API surface images
promptreq
string
Описание изображения, до 100 000 символов
n
integer
Количество изображений, от 1 до 10
size
string
Размер, поддерживаемый моделью, например 1024x1024
aspect_ratio
string
Соотношение сторон, например 1:1 или 16:9
response_format
b64_json
Можно не передавать; поддерживается только b64_json
seed
integer
Seed для воспроизводимости, если модель его поддерживает
quality
string
Качество генерации; допустимые значения зависят от модели

Дополнительно поддерживаются resolution, output_format, background, output_compression, moderation, input_references и provider. Конкретные возможности зависят от выбранной модели.

Формат ответа

json
{
  "created": 1748372400,
  "data": [
    { "b64_json": "<base64-encoded-image>" }
  ],
  "usage": {
    "prompt_tokens": 0,
    "completion_tokens": 4175,
    "total_tokens": 4175,
    "cost": 0.04
  }
}
Текущие ограничения
Формат ответа через URL и streaming-превью пока не поддерживаются. Используйте b64_json или не передавайте response_format — результат всё равно придёт в base64.

Асинхронная генерация: 202 → polling → результат

Для долгих запросов добавьте Prefer: respond-async. Сервер сохранит задание и вернёт 202 Accepted с JSON, а не готовый результат. Клиент должен опрашивать статус и скачивать результат после завершения. Закрытие соединения не отменяет сохранённое задание.

Совместимость с синхронным API
Без заголовка сохраняется прежний синхронный ответ: JSON для изображений и аудиобайты для речи. Такой запрос всё ещё зависит от таймаутов сети и прокси. Async доступен только для POST /v1/images, /v1/images/generations и /v1/audio/speech, включая пути без /v1. Он не включает async для /v1/chat/completions или транскрипции и не является SSE-стримингом.

Пример использует Bash, curl и jq. Выберите доступную вашему ключу модель; для речи также проверьте голос через /v1/audio/voices. Сохраните Idempotency-Key и тело запроса до отправки. Для нового намеренного задания задайте новый ключ; для повтора того же запроса оставьте прежний.

bash
export BASE_URL="https://api.kodikrouter.ru"
export IDEMPOTENCY_KEY="images-request-001"
# KODIK_API_KEY должен быть задан в окружении.
(
  set -e
  HTTP_CODE=$(curl --fail-with-body -sS --max-time 30 \
    "$BASE_URL/v1/images/generations" \
    -H "Authorization: Bearer $KODIK_API_KEY" \
    -H "Content-Type: application/json" \
    -H "Prefer: respond-async" \
    -H "Idempotency-Key: $IDEMPOTENCY_KEY" \
    --data '{"model":"google/gemini-2.5-flash-image","prompt":"A coastal town at sunrise","response_format":"b64_json"}' \
    --output job.json --write-out '%{http_code}')
  test "$HTTP_CODE" = "202" || { cat job.json; exit 1; }
  jq -er '.id' job.json > job-id.txt
  cat job.json
)

Ответ содержит id, status, polling_url, expires_at и request_id; content_url появляется при completed. Заголовки ответа: Location, Retry-After, Preference-Applied: respond-async и X-KodikRouter-Request-Id.

GET
/v1/media/jobs/{id}

Статус задания; используйте тот же API-ключ, что и при отправке

GET
/v1/media/jobs/{id}/content

Готовый результат: JSON изображения или аудиобайты речи

Выполните следующий блок после получения job-id.txt. При временном сбое повторяйте этот блок с сохранённым ID, а не создавайте новое задание. При 429 соблюдайте Retry-After.

bash
(
  set -e
  JOB_ID=$(cat job-id.txt)
  for attempt in $(seq 1 180); do
    curl --fail-with-body -sS --max-time 30 \
      "$BASE_URL/v1/media/jobs/$JOB_ID" \
      -H "Authorization: Bearer $KODIK_API_KEY" --output status.json
    STATUS=$(jq -er '.status' status.json)
    echo "$STATUS"
    case "$STATUS" in
      completed)
        curl --fail-with-body -sS --max-time 120 \
          "$BASE_URL/v1/media/jobs/$JOB_ID/content" \
          -H "Authorization: Bearer $KODIK_API_KEY" \
          --output image.json
        jq -er '.data[0].b64_json' image.json | base64 --decode > image.png
        exit 0 ;;
      failed|needs_reconciliation|expired)
        cat status.json
        exit 1 ;;
      queued|running|billing_pending|billing) sleep 10 ;;
      *) cat status.json; exit 1 ;;
    esac
  done
  echo "Ожидание завершено. Сохраните job-id.txt и продолжите polling позже."
  exit 1
)

queued — в очереди; running — генерация; billing_pending / billing — ожидание или обработка стоимости; completed — результат доступен. В billing_pending звук уже может быть сохранён: сервер повторяет получение стоимости, не синтез. failed означает отказ, а needs_reconciliation — неизвестный исход платного запроса: обратитесь в поддержку с ID задания и request_id, не запускайте генерацию автоматически повторно.

Повторы и срок хранения
Сохраняйте Prefer: respond-async, тот же API-ключ, Idempotency-Key и тело при каждом повторе, в том числе если ответ 202 потерян. Повтор возвращает прежнее задание; другое тело с тем же ключом даёт 409. Не переключайтесь на sync для повтора. Результаты хранятся по умолчанию 24 часа после отправки — ориентируйтесь на expires_at и скачивайте их вовремя. После истечения срока возвращается 410; до готовности /content возвращает 409.

Дневной или месячный лимит расходов блокирует новые генерации (402), но не polling и скачивание уже оплаченного результата. Ключ должен оставаться активным и неистёкшим; scope gateway:write, доступ к модели и rate limit продолжают действовать. Другой ключ не получает доступ к заданию. Повторные GET не запускают генерацию и не списывают стоимость задания снова. Async не отменяет сетевые таймауты скачивания больших файлов.

Типовые ошибки

Model must use the 'images' API surface

Image-only модель отправлена в /chat/completions. Отправьте image-модель в /images/generations, а для /chat/completions используйте модель с API surface chat.

Not Found

Проверьте Base URL: он должен быть ровно https://api.kodikrouter.ru/v1. Полный путь вручную в это поле добавлять не нужно.

Server Connection Error

Повторите запрос через cURL и проверьте сетевую доступность API. Убедитесь, что у ключа есть scope gateway:write, модель разрешена allowlist-ом, а на балансе достаточно средств.