> ## Documentation Index
> Fetch the complete documentation index at: https://docs.apiyi.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Практика интеграции Image API: создайте собственную асинхронную очередь

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

<Info>
  Эта страница — **технический материал / рекомендации** для команд разработки, интегрирующих генерацию изображений в свои продукты. Мы делимся только инженерными практиками — здесь не требуется никаких изменений со стороны APIYI. Вы можете реализовать все это поверх существующих синхронных API.
</Info>

## Синхронный или async? Сначала разберитесь с моделью API APIYI

APIYI image generation API — **все синхронные**: эндпоинты вроде `/v1/images/generations` выполняют запрос «до завершения» после отправки. Даже если клиент отключится посреди выполнения, сервер все равно завершит генерацию — то есть это **не** async API задач, где вы «сначала получаете task\_id, а затем опрашиваете результат».

<Info>
  На уровне шлюза APIYI уже оборачивает upstream async polling (некоторые провайдеры нативно используют цикл `polling_url`) в **синхронный OpenAI API для изображений**. Для вас это всегда «отправить один раз, получить результат один раз» — **не нужно писать цикл опроса самостоятельно**.
</Info>

Многие команды сразу спрашивают: «Тогда как мне управлять async-задачами?» На самом деле это две разные вещи:

* **Синхронный** — формат API APIYI (на уровне HTTP-запроса: один запрос, один результат).
* **Async-очередь** — **инженерная практика на вашей стороне** (на уровне бизнес-задачи: вернуть ответ сразу, а выполнение продолжить в фоне).

Эти два подхода не конфликтуют. Ниже показано, как самостоятельно обернуть async-очередь вокруг синхронного API.

## Почему командам разработки по-прежнему нужно управление «на уровне задач»

Синхронный вызов image API внутри потока запроса пользователя подходит для демо. Но как только вы строите реальный продукт для конечных пользователей, вам почти наверняка нужно отделить «бизнес-задачу» от «одного HTTP-вызова». По четырем причинам:

<CardGroup cols={2}>
  <Card title="Успех ≠ один вызов" icon="repeat">
    «Успешная задача» часто собирается из **нескольких синхронных вызовов**: первоначальный тайм-аут или иногда 429/503 требуют повторной попытки. Когда задача и вызов разделены, повторы, backoff и тайм-ауты полностью прозрачны для конечного пользователя — он видит только, что «это изображение в итоге успешно сгенерировалось».
  </Card>

  <Card title="Прозрачная пересылка, без хранения" icon="database">
    APIYI выполняет только **прозрачную пересылку и не хранит входные данные или результаты пользователя** (prompt, reference images и сгенерированные результаты не сохраняются). Чтобы дать пользователям историю, проверку статуса и сохранение результатов, вы **должны сохранять их самостоятельно** — это неизбежный шаг на стороне продукта.
  </Card>

  <Card title="Более удобный UX для конечных пользователей" icon="smile">
    Пользователь получает `task_id` при отправке, а frontend **периодически опрашивает статус задачи** вместо того, чтобы держать долгое соединение. Обновление страницы или краткий обрыв сети не приведут к потере задачи; batch generation может ставиться в очередь и заполняться по одной.
  </Card>

  <Card title="Поддержка нескольких провайдеров становится возможной" icon="layers">
    Как только у вас появляется собственная абстракция задачи, слой Worker может по требованию переключаться / выполнять failover / сравнивать цену между **несколькими провайдерами** — как минимум это делает возможным принцип «не класть все яйца в одну корзину».
  </Card>
</CardGroup>

## Справочная архитектура: оберните синхронный вызов в асинхронную очередь

Основная идея в одном предложении: **уровень API только «получает задачу, помещает ее в очередь и возвращает task\_id»; сам синхронный вызов выполняется во фоновом Worker.**

```text theme={null}
  Client/Frontend ──①submit──▶  API layer  ──②enqueue──▶  Queue (Redis / MQ / DB table)
        ▲                          │                                  │
        │ ⑤poll task status         │ return task_id now                │ ③pull task
        │                          ▼                                  ▼
        └────────────────────  Database  ◀──④persist(status/input/url)── Worker
                                                                        │ sync call APIYI
                                                                        │ (with retry/backoff)
                                                                        ▼
                                                            api.apiyi.com (sync image API)
```

<Steps>
  <Step title="Возврат немедленно">
    Фронтенд отправляет запрос на генерацию в ваш собственный уровень API; уровень API создает запись задачи (статус `pending`), помещает ее в очередь и **сразу возвращает `task_id` фронтенду**. Пользователь никогда не ждет — ответ приходит за миллисекунды.
  </Step>

  <Step title="Помещение в очередь">
    Очередь может быть легковесной: Redis List / Stream, RabbitMQ / Kafka или даже таблица базы данных со столбцом `status`, которую сканируют по расписанию. Выбор зависит от вашего масштаба — на старте не нужно сразу тянуть тяжелую middleware.
  </Step>

  <Step title="Worker: синхронный вызов + повтор">
    Фоновый Worker забирает задачу, переводит статус в `running` и **синхронно вызывает** APIYI image API. При ошибках, которые можно повторить, он выполняет повторы с экспоненциальной задержкой (см. «Повтор и тарификация» ниже), и все это прозрачно для пользователя.
  </Step>

  <Step title="Сохранение">
    Независимо от того, успешна ли операция или нет, запишите результат обратно в базу данных: при успехе сохраните URL выходного изображения, задержку и метаданные тарификации, установите статус `succeeded`; при ошибке сохраните ошибку и установите статус `failed`. **Именно эту часть APIYI не делает за вас, и вы должны реализовать ее сами.**
  </Step>

  <Step title="Периодический опрос фронтенда">
    Фронтенд периодически проверяет статус задачи с помощью `task_id` (или вы отправляете данные через WebSocket / SSE). Когда задача завершается, покажите результат; при ошибке — дружелюбное сообщение. Браузеру пользователя никогда не нужно держать длительное соединение.
  </Step>
</Steps>

## Машина состояний задачи и модель данных

Используйте понятную машину состояний, чтобы описать жизненный цикл каждой задачи:

| State       | Meaning                                                    | Typical transition                    |
| ----------- | ---------------------------------------------------------- | ------------------------------------- |
| `pending`   | Поставлено в очередь, ожидает воркера                      | → `running`                           |
| `running`   | Воркер синхронно вызывает APIYI                            | → `succeeded` / `retrying` / `failed` |
| `retrying`  | Возникла ошибка, допускающая повтор, ожидание backoff      | → `running`                           |
| `succeeded` | Успешно сгенерировано, результат сохранен                  | terminal                              |
| `failed`    | Повторные попытки исчерпаны или ошибка не подлежит повтору | terminal                              |

Таблица задач должна содержать как минимум следующие поля (типы зависят от вашего стека):

| Field                       | Description                                                                            |
| --------------------------- | -------------------------------------------------------------------------------------- |
| `task_id`                   | Уникальный идентификатор задачи, возвращаемый фронтенду при отправке                   |
| `status`                    | Указанный выше enum статуса                                                            |
| `provider` / `model`        | Используемые провайдер и модель (зарезервировано для поддержки нескольких провайдеров) |
| `input`                     | Ввод пользователя (prompt, ссылки на reference-image, размер и другие параметры)       |
| `output_url`                | URL результата (желательно после переноса в ваше собственное хранилище)                |
| `retry_count`               | Количество повторных попыток на данный момент, для ограничения по rate limit и отладки |
| `error`                     | Причина сбоя (код ошибки + понятное сообщение)                                         |
| `created_at` / `updated_at` | Время создания и последнего обновления (включите часовой пояс, например UTC+8)         |
| `latency` / `cost`          | Метаданные о задержке и тарификации, для учета затрат и мониторинга                    |

<Tip>
  Перенесите сгенерированный результат в **свое объектное хранилище** (OSS / S3 и т. д.) и сохраните этот URL — не полагайтесь надолго на временную ссылку стороннего сервиса. Временные ссылки могут истечь; хранить собственную копию стабильнее для конечных пользователей.
</Tip>

## Повторы и тарификация: что повторять, а что нет

Самая большая ценность «управления на уровне задачи» — правильно настроить повторы. Тарификация и стратегия повторов зависят от типа ошибки:

| Сценарий                                                                       | Подлежит тарификации?        | Повторять?                                                                                        |
| ------------------------------------------------------------------------------ | ---------------------------- | ------------------------------------------------------------------------------------------------- |
| `429` / `503` (лимит запросов / занят upstream)                                | Не тарифицируется            | ✅ Повторять, экспоненциальная задержка, примерно 2 раза                                           |
| Тайм-аут клиента / проактивное отключение                                      | **Все равно тарифицируется** | ⚠️ Можно повторять, но сначала задайте разумный тайм-аут в зависимости от разрешения (\~60–600 с) |
| Отказ по проверке безопасности контента (status 200, все равно тарифицируется) | **Все равно тарифицируется** | ❌ Не повторяйте; верните пользователю дружелюбное сообщение                                       |

<Warning>
  Учитывайте отдельно "**число повторных попыток бизнес-задачи**" и "**была ли она тарифицирована**". `429/503` повторные попытки не тарифицируются, поэтому смело увеличивайте задержку; но отключения из-за тайм-аута и отказы проверки безопасности контента тарифицируются даже когда они «сбоят» — слепые повторы **увеличивают затраты**. Проверьте тип ошибки, прежде чем снова тратить средства.
</Warning>

Полные критерии определения ошибки и дружелюбных сообщений см.:

<CardGroup cols={2}>
  <Card title="Обработка ошибок изображений Gemini" icon="triangle-alert" href="/ru/api-capabilities/gemini-image-error-handling">
    Сигналы обнаружения сбоев, политика модерации контента и стратегия дружелюбных сообщений.
  </Card>

  <Card title="Гарантия при сбое генерации" icon="shield-check" href="/ru/api-capabilities/nano-banana-pro-guarantee">
    За сбои, вызванные не вами, кредиты возмещаются по количеству.
  </Card>
</CardGroup>

## Продвинутый уровень: одна очередь, несколько провайдеров

С помощью абстракции задачи вызов Worker может перейти от «жесткой привязки к одному endpoint» к «маршрутизации по `provider`». Сведите все к одной записи `submit(provider, payload)` и позвольте Worker определять фактический upstream на основе поля `provider` у задачи:

* **Failover**: когда провайдер A продолжает сбоить, автоматически переключайтесь на B, незаметно для пользователя.
* **Сравнение стоимости / маршрутизация**: направляйте разные задачи к разным провайдерам или моделям в зависимости от стоимости или сценария.
* **Canary**: отправляйте небольшую долю трафика на новую модель для проверки, а затем постепенно увеличивайте объем.

<Info>
  В большинстве случаев вам на самом деле **не** нужен собственный многопровайдерный слой: сам APIYI агрегирует gpt-image-2, Nano Banana, FLUX, Seedream и другие, так что один ключ APIYI покрывает большинство потребностей в рамках одного стиля API. Собственная абстракция провайдеров — вариант «на всякий случай»; добавляйте ее только тогда, когда вам действительно нужно кросс-провайдерное аварийное переключение или сравнение стоимости.
</Info>

## Вопросы и ответы

<AccordionGroup>
  <Accordion title="Почему бы просто не дать мне API асинхронных задач вместо синхронного?">
    Генерация изображений по своей природе — это «отправить один раз, получить одно изображение»: это сильная синхронная семантика, и обернуть ее в синхронный API проще всего для подавляющего большинства вызывающих сторон (не нужно поддерживать опрос, не нужно обрабатывать истечение срока жизни задачи). Нужны ли вам асинхронная очередь, конечный автомат и персистентность, зависит от **формата вашего продукта** (ориентирован ли он на конечного пользователя, нужна ли вам история), поэтому эту часть вы при необходимости реализуете сами для максимальной гибкости.
  </Accordion>

  <Accordion title="Если у клиента истекло время ожидания и он отключился, задача все еще выполняется? Она тарифицируется?">
    Она продолжает выполняться. Как только синхронный endpoint получает запрос, он выполняется до завершения; отключение клиента **не** прерывает генерацию на стороне сервера, и эта генерация **тарифицируется как обычно**. Поэтому задайте достаточный timeout в зависимости от разрешения (\~60–600s) — не ставьте его слишком коротким и не оказывайтесь в ситуации, когда вы «платите, но не получаете изображение».
  </Accordion>

  <Accordion title="Сохраняет ли APIYI историю моей генерации изображений?">
    Нет. APIYI выполняет только **прозрачную пересылку и не хранит входные или выходные данные пользователя**. Чтобы предоставить пользователям историю, просмотр статуса и сохранение результатов, вам нужно хранить это на своей стороне — именно поэтому в этом руководстве рекомендуется «управление на уровне задач».
  </Accordion>

  <Accordion title="Я уже использую один ключ APIYI — нужен ли мне все еще слой для нескольких провайдеров?">
    Обычно нет. APIYI уже агрегирует несколько семейств моделей в одном стиле API, и одного ключа, как правило, достаточно. Только если у вас есть явные потребности в аварийном переключении между провайдерами, сравнении цен или маршрутизации по требованиям соответствия, стоит добавить абстракцию провайдера в слой Worker — это опционально, а не обязательно.
  </Accordion>
</AccordionGroup>

## Связанные документы

<CardGroup cols={2}>
  <Card title="Основы и лучшие практики Image API" icon="book-check" href="/ru/api-capabilities/image-api-best-practices">
    Таблица тайм-аутов по моделям, обработка base64 и справка по выходу URL.
  </Card>

  <Card title="Почему нет асинхронного API" icon="circle-help" href="/ru/faq/image-async-api">
    FAQ: Есть ли асинхронный image API? Можно ли запрашивать результаты по ID задачи?
  </Card>

  <Card title="Обзор FLUX" icon="sparkles" href="/ru/api-capabilities/flux/overview">
    Пример того, как асинхронный polling upstream обернут в синхронный OpenAI Images API.
  </Card>

  <Card title="Руководство разработчика Nano Banana" icon="compass" href="/ru/api-capabilities/nano-banana-dev-guide">
    Синхронные многопоточные вызовы, настройки тайм-аута и основы тарификации в одном месте.
  </Card>

  <Card title="Обработка ошибок Gemini при генерации изображений" icon="triangle-alert" href="/ru/api-capabilities/gemini-image-error-handling">
    Сигналы обнаружения сбоев и стратегия дружелюбных сообщений.
  </Card>

  <Card title="Гарантия при сбое генерации" icon="shield-check" href="/ru/api-capabilities/nano-banana-pro-guarantee">
    Правила возмещения кредитов за сбои, не вызванные вами.
  </Card>
</CardGroup>
