Felo API PlatformFelo API Platform

Руководства

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

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

Image API Felo генерирует и редактирует изображения с помощью моделей из каталога Felo. API асинхронный: запрос на отправку сразу возвращает ID задачи, после чего вы опрашиваете задачу до терминального состояния.

На этой странице описан контракт, специфичный для изображений. Общий жизненный цикл задач см. в разделе Асинхронные задачи.

Перед началом

  • API key. См. Аутентификация.
  • Модель с model_kind, равным image_generation. См. раздел «Найти модель изображений» ниже.
  • Base URL: https://openapi.felo.ai.

Отличия от OpenAI Images API

Путь похож на OpenAI Images API, но ответ — нет:

OpenAI Images APIImage API Felo
Ответ на отправку200 с данными изображения202 с task_id, status: "queued"
Когда изображение готовоВ ответе на отправкуПосле того как polling дойдёт до succeeded
Данные изображенияb64_json или urlПодписанный url с ограниченным сроком действия
Ошибки запросаHTTP-ошибка при отправкеОбычно status: "failed" у задачи

Не отправляйте openai.images.generate() на этот endpoint. SDK ожидает синхронный массив data и не умеет опрашивать задачу. Вызывайте endpoint напрямую по HTTP и опрашивайте задачу либо оберните оба шага в свой клиент.

Отправить задачу

curl -X POST "https://openapi.felo.ai/api/v1/images/generations" \
  -H "Authorization: Bearer ${FELO_API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "gemini-3-pro-image",
    "prompt": "A studio product photo of a ceramic coffee cup on warm linen, soft morning light",
    "resolution": "2K",
    "aspect_ratio": "4:3"
  }'

Успешная отправка возвращает HTTP 202:

{
  "task_id": "6d1f0c9a-...",
  "status": "queued",
  "kind": "image",
  "outputs": []
}

202 означает только то, что платформа приняла задачу. Это не значит, что prompt, параметры или референсные изображения корректны. Запрос к upstream выполняется после ответа, поэтому большинство проблем с запросом проявляется позже как неудачная задача. Исключение — маршрутизация модели: chat- или video-модель отклоняется при отправке.

Опрашивать до терминального состояния

curl "https://openapi.felo.ai/api/v1/images/generations/${TASK_ID}" \
  -H "Authorization: Bearer ${FELO_API_KEY}"
СтатусЗначениеЧто делать
queuedПринята, ожидает запускаПродолжать polling
runningМодель генерируетПродолжать polling
succeededТерминальный; outputs заполненСкачать результаты
failedТерминальный; прочитайте error.messageИсправить запрос; не повторять без изменений
interruptedТерминальный; результат не удалось восстановитьОтправить задачу заново

При ошибке задачи HTTP-статус остаётся 200. Проверяйте status в теле ответа, а не только HTTP-статус.

Начните с интервала 2–5 секунд и постепенно увеличивайте его. Большинство изображений готово в течение минуты; вывод 4K и редактирование с большим числом референсов занимают дольше. Всегда задавайте собственный дедлайн.

import os
import time

import requests

BASE = "https://openapi.felo.ai"
HEADERS = {"Authorization": f"Bearer {os.environ['FELO_API_KEY']}"}


def generate_image(prompt: str, **params) -> list[dict]:
    response = requests.post(
        f"{BASE}/api/v1/images/generations",
        headers=HEADERS,
        json={"model": "gemini-3-pro-image", "prompt": prompt, **params},
        timeout=30,
    )
    response.raise_for_status()
    task = response.json()

    deadline = time.monotonic() + 300
    delay = 2.0
    while time.monotonic() < deadline:
        time.sleep(delay)
        delay = min(delay * 1.5, 10.0)

        poll = requests.get(
            f"{BASE}/api/v1/images/generations/{task['task_id']}",
            headers=HEADERS,
            timeout=30,
        )
        poll.raise_for_status()
        task = poll.json()

        if task["status"] == "succeeded":
            return task["outputs"]
        if task["status"] in {"failed", "interrupted"}:
            message = task.get("error", {}).get("message", task["status"])
            raise RuntimeError(message)

    raise TimeoutError(f"image task {task['task_id']} did not finish")

Получить и сохранить результаты

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

{
  "task_id": "6d1f0c9a-...",
  "status": "succeeded",
  "kind": "image",
  "outputs": [
    {
      "media_type": "image/png",
      "url": "https://openapi.felo.ai/api/v1/media/...?exp=...&sig=..."
    }
  ]
}
  • outputs пуст, пока задача не завершится успешно.
  • url — подписанный URL. Он скачивается без API key, поэтому обращайтесь с ним как с bearer-ссылкой.
  • URL имеет ограниченный срок действия. Параметр exp — время истечения. Скачайте файл и сохраните его в свой bucket или файловую систему; задача не является постоянным хранилищем.
  • Повторный polling после истечения срока возвращает тот же просроченный URL. Новый URL не выдаётся.
  • Ответ не содержит изображение в base64.
for index, output in enumerate(outputs):
    image = requests.get(output["url"], timeout=60)
    image.raise_for_status()
    with open(f"image-{index}.png", "wb") as file:
        file.write(image.content)

Редактировать изображение

Передайте референсные изображения в input_references. Модель использует их как контекст; в prompt по-прежнему нужно описать изменение.

curl -X POST "https://openapi.felo.ai/api/v1/images/generations" \
  -H "Authorization: Bearer ${FELO_API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "gemini-3-pro-image",
    "prompt": "Keep the product and framing. Replace the background with a sunlit marble countertop.",
    "resolution": "2K",
    "aspect_ratio": "4:3",
    "input_references": [
      {
        "type": "image_url",
        "image_url": {"url": "https://example.com/product.png"}
      }
    ]
  }'

Каждый референс — HTTP(S) URL или base64 data URL. Порядок референсов сохраняется; при необходимости ссылайтесь на изображения по позиции в prompt. Текущие модели принимают до 14 референсов.

Поля запроса

ПолеОбязательноОписание
modelДаID модели из каталога, например gemini-3-pro-image. Не используйте upstream-ID вида google/...
promptДаТекстовая инструкция. При редактировании опишите изменение и то, что должно остаться прежним
resolutionНетУровень вывода. Текущие модели принимают 1K, 2K или 4K
aspect_ratioНетСоотношение сторон. Допустимые значения зависят от модели
nНетКоличество изображений. Текущие модели принимают 1
input_referencesНетРеференсные изображения для редактирования или композиции

Текущие модели изображений:

ID моделиРазрешенияСоотношения сторонИзображений на задачуРеференсы
gemini-3-pro-image1K, 2K, 4K1:1, 2:3, 3:2, 3:4, 4:3, 4:5, 5:4, 9:16, 16:9, 21:91До 14
gemini-nano-banana-2.11K, 2K, 4K1:1, 1:4, 1:8, 2:3, 3:2, 3:4, 4:1, 4:3, 4:5, 5:4, 8:1, 9:16, 16:9, 21:91До 14

Чтобы закрепить поставщика, отправьте заголовок X-Felo-Channel со значением channel code из списка channels модели. См. Каналы и маршрутизация провайдеров.

Найти модель изображений

Отдельного endpoint со списком моделей изображений нет. Запросите общий каталог и отфильтруйте по model_kind:

curl "https://openapi.felo.ai/api/v1/models" \
  -H "Authorization: Bearer ${FELO_API_KEY}" \
  | jq '.data[] | select(.model_kind == "image_generation") | {id, name}'

GET /api/v1/models/{model_id} возвращает каналы модели и опубликованные тарифы.

supported_parameters в общем каталоге — список параметров уровня протокола. Для моделей изображений он не перечисляет надёжно поля изображений выше, поэтому не стройте по нему запрос. Используйте таблицу полей на этой странице, а ID модели проверяйте по каталогу.

Тарификация

Генерация изображений тарифицируется по опубликованным тарифам модели. В каталоге, помимо текстовых prompt и completion, есть input_image и output_image. Тарифы изображений — это тарифы за токены: resolution меняет число выходных image-токенов на одно изображение, поэтому 4K стоит дороже 1K. Референсные изображения добавляют входные image-токены.

В ответе задачи нет объекта usage. Фактическое списание смотрите в записях об использовании Console Billing.

Задача, завершившаяся со статусом failed или interrupted, не даёт результата и не тарифицируется. Ответ 402 при отправке означает, что задача не была создана.

Ошибки

Ошибки уровня endpoint на маршруте изображений используют конверт ошибок OpenAI:

{
  "error": {
    "message": "Model kind does not match this generation endpoint",
    "type": "invalid_request_error",
    "param": null,
    "code": "model_kind_mismatch"
  }
}

Request ID находится в заголовке ответа X-Request-Id. Записывайте его вместе с task ID.

HTTPerror.codeЗначение
400invalid_request_errorНет model или тело не является JSON-объектом
400model_not_configuredID модели отсутствует в активном каталоге
400chat_model_not_allowedВ endpoint изображений отправлена chat-модель
400model_kind_mismatchВ endpoint изображений отправлена video- или другая не-image модель
402insufficient_balanceНедостаточно кредитов; задача не создана
404generation_task_not_foundID задачи неизвестен или принадлежит другому аккаунту
429daily_spend_limit_exceededДостигнут дневной лимит расходов аккаунта
503generation_unavailableГенерация временно недоступна; повторите позже

Ошибки аутентификации описаны в разделе Ошибки, лимиты и повторные попытки.

Связанные разделы