Если вы встраиваете suno api в приложение, основная задача начинается не с самого HTTP-запроса. Генерация музыки — операция, результат которой пользователь получает не мгновенно, поэтому интерфейс, очередь, обработчик готовности и хранение результата нужно проектировать как одну цепочку. В этом материале разберём именно эту архитектуру: как организовать музыку по api в приложении так, чтобы пользователь мог продолжить работу, пока задача выполняется.
API сервиса работает по адресу https://genius-bot.ru/api/. Для запуска операции используется POST /generate, состояние можно получить через GET /tasks/{id}, а для асинхронного сценария запрос допускает параметр callback_url: после готовности задачи сервис отправляет на указанный адрес POST.
Цена операции «Создать музыку (music)» составляет 59 ₽ за один запуск. Оплата идёт за запуск, без абонентской платы и минимального платежа. При первом выпуске ключа на баланс начисляется 50 ₽, поэтому архитектуру удобно сначала проверить на одной-двух тестовых задачах, а уже затем подключать постоянное хранение и обработку очереди.
1) Долгая задача в быстром интерфейсе: где обычно ломается
Типичная ошибка при интеграции генерации музыки — воспринимать POST /generate как обычный короткий API-вызов. В веб-приложении запрос от браузера отправляется на сервер, сервер запускает генерацию и дальше приложение пытается держать исходный HTTP-запрос открытым до появления результата. Такая схема связывает время жизни пользовательского соединения со временем выполнения фоновой задачи.
Для интерфейса это неудобно сразу по нескольким причинам. Пользователь может закрыть страницу, перейти в другой раздел или потерять соединение, хотя сама генерация продолжает быть нужной. Кроме того, повторное нажатие кнопки может создать вторую задачу, если клиент не получил ответ вовремя и решил, что первый запрос не сработал.
Поэтому полезно разделить два события: «задача принята приложением» и «трек готов». После создания записи в собственной базе приложение должно быстро вернуть клиенту внутренний идентификатор задачи. Пользователь получает экран со статусом «Готовим трек», а сервер самостоятельно ждёт завершения через вебхук или проверяет состояние задачи.
Минимальная последовательность выглядит так:
- Клиент отправляет в ваше приложение параметры будущего трека.
- Приложение создаёт локальную запись со статусом
queued. - Фоновый обработчик отправляет запрос в API и получает идентификатор внешней задачи.
- Локальная запись получает внешний
task_idи статусprocessing. - После завершения обработчик вебхука получает POST от API.
- Приложение сохраняет результат и переводит задачу в
completed. - Интерфейс получает готовый результат при следующем запросе статуса или через собственный механизм обновления.
Такой подход позволяет не держать пользовательский запрос открытым. Сам запрос к API остаётся частью фонового процесса, а браузер работает только с вашей базой состояний.

2) Очередь задач: минимальная схема на одну таблицу
Для первого варианта не обязательно создавать отдельную инфраструктуру из нескольких таблиц. Одной таблицы music_tasks достаточно, если в ней хранить жизненный цикл каждой генерации. Важнее не количество таблиц, а то, чтобы состояние нельзя было двусмысленно трактовать.
Например, запись может содержать следующие поля: id, user_id, status, external_task_id, callback_url, result_url, error, created_at, updated_at. Отдельно стоит хранить ключ идемпотентности, если клиент может повторять один и тот же запрос.
Набор статусов можно сделать небольшим:
queued— задача записана, но ещё не отправлена во внешний API;processing— запуск выполнен, внешнийtask_idизвестен;completed— результат получен и сохранён;failed— обработка завершилась ошибкой;cancelled— локальная задача больше не должна обрабатываться.
Главное преимущество такой очереди — возможность отделить приём пользовательского запроса от фактического запуска. Если внешний API временно не отвечает, запись остаётся в очереди, а обработчик может повторить попытку по своим правилам. При этом не нужно заставлять пользователя повторно нажимать кнопку.
Запрос к API должен содержать Bearer-ключ в заголовке Authorization. Адрес базового API — https://genius-bot.ru/wp-json/genius/v1, поэтому вызов запуска выглядит как обращение к /generate.
curl -X POST "https://genius-bot.ru/wp-json/genius/v1/generate" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"service": "music",
"callback_url": "https://example.com/api/music/webhook"
}'
Конкретные параметры самой операции нужно передавать согласно контракту используемого запуска. Здесь важен архитектурный момент: callback_url относится к задаче и сообщает API, куда отправить POST после её готовности.
На стороне приложения не стоит считать сам факт отправки запроса признаком готового трека. Сначала сохраняется идентификатор внешней задачи, а окончательное состояние устанавливается только после получения результата через вебхук или проверки GET /tasks/{id}.
3) Вебхук против опроса: считаем запросы и задержку
У сервиса есть два подхода к отслеживанию состояния. Первый — периодически запрашивать GET /tasks/{id}. Второй — передать callback_url и получить POST после готовности задачи. Оба варианта совместимы с архитектурой очереди, но дают разную нагрузку на API и ваше приложение.
Рассмотрим условный пример. Пусть одновременно запущено 100 музыкальных задач, а приложение проверяет каждую каждые 10 секунд. Это до 10 проверок в минуту на одну задачу, то есть до 1000 запросов статуса в минуту. Для ключа действует ограничение не более 60 запросов в минуту, поэтому наивный общий polling может быстро упереться в лимит.
У вебхука другая модель. Вместо постоянных запросов ваше приложение сообщает адрес обработчика один раз при запуске задачи, после чего ждёт входящий POST. Само приложение должно обеспечить доступность этого endpoint и обработать повторную доставку корректно, но постоянного цикла запросов статуса уже не требуется.
| Схема | Что происходит | Запросы статуса | Что учитывать |
|---|---|---|---|
| Polling 10 секунд | Приложение регулярно вызывает GET /tasks/{id} | До 6 в минуту на одну задачу | Нагрузка зависит от количества задач |
| Polling 30 секунд | Проверка выполняется реже | До 2 в минуту на одну задачу | Результат может отображаться с задержкой до интервала проверки |
| Webhook | API отправляет POST при готовности | Отдельные проверки не нужны для обычного пути | Нужен публичный обработчик и защита от повторной обработки |
Числа в таблице показывают именно верхнюю частоту проверки при заданном интервале, а не фактическую задержку генерации. Продолжительность самой операции в предоставленной справке не указана, поэтому закладывать в интерфейс конкретное количество секунд до готовности не стоит.
Практический вариант — использовать вебхук как основной канал, а GET /tasks/{id} оставить для восстановления состояния. Например, если пользователь открыл страницу уже после отправки callback, сервер может самостоятельно проверить сохранённый внешний идентификатор и синхронизировать запись.
При этом вебхук готовности трека не должен напрямую доверять только пользовательскому интерфейсу. Сначала сервер принимает событие, сопоставляет его с существующей записью и проверяет, не обработана ли задача ранее. После этого результат сохраняется, и только затем интерфейс узнаёт о новом статусе.

4) Повторная отправка и защита от двойной оплаты
Цена музыкальной операции составляет 59 ₽ за один запуск. Поэтому повторный HTTP-запрос нельзя автоматически трактовать как безопасный retry: если первый запуск уже был принят, повторная отправка может означать второй запуск и вторую оплату.
Особенно опасен сценарий с таймаутом. Клиент отправляет запрос, ваше приложение создаёт задачу и запускает API, но ответ теряется. Клиент повторяет запрос, сервер не знает, что предыдущая операция уже стартовала, и создаёт вторую запись.
Для защиты нужен собственный ключ идемпотентности. Например, клиент передаёт request_id, а база данных требует уникальности этого значения в рамках пользователя. Если такой запрос уже существует, сервер возвращает существующую локальную задачу вместо повторного запуска.
import requests
BASE_URL = "https://genius-bot.ru/wp-json/genius/v1"
API_KEY = "YOUR_API_KEY"
payload = {
"service": "music",
"callback_url": "https://example.com/api/music/webhook"
}
response = requests.post(
f"{BASE_URL}/generate",
headers={
"Authorization": f"Bearer {API_KEY}",
"Content-Type": "application/json",
},
json=payload,
timeout=30,
)
response.raise_for_status()
task = response.json()
print(task)
В реальном приложении этот код должен выполняться не непосредственно в HTTP-обработчике страницы, а в фоновой очереди. Саму запись о задаче лучше создать до внешнего вызова, а после успешного запуска атомарно сохранить полученный внешний идентификатор.
Есть ещё один важный случай — повторный вебхук. Сетевые события нельзя проектировать так, будто каждый callback гарантированно приходит ровно один раз. Поэтому обработчик должен уметь увидеть уже завершённую задачу и безопасно ответить без повторного сохранения результата или повторного списания внутри вашей бизнес-логики.
Для этого достаточно проверять состояние записи перед изменением. Если status = completed и результат уже сохранён, повторное событие не должно запускать процедуру обработки заново.
Также полезно разделить стоимость запуска и стоимость хранения. Сам запуск music стоит 59 ₽, а дальнейшее хранение результата в вашей системе — отдельная инфраструктурная задача. В справке сервиса не указана плата за хранение готовых треков, поэтому её нельзя приписывать API.
5) Где хранить готовые треки и сколько это стоит
После завершения задачи результат нужно отделить от записи о состоянии. В таблице задач удобно сохранить метаданные: внешний task_id, статус, время завершения и ссылку на результат. Сам файл лучше рассматривать как отдельный объект, чтобы изменение статуса не было связано с размером аудиоданных.
Для небольшого приложения можно начать с простой модели: база хранит информацию о треке, а приложение хранит полученный результат в файловом хранилище. В базе при этом остаётся путь или URL, идентификатор пользователя и технические метаданные.
Важно заранее определить момент, когда задача считается действительно завершённой. Получение callback ещё не обязательно означает, что ваш сервер успел сохранить результат. Надёжнее сначала принять событие, извлечь данные, сохранить файл и только после успешной операции поставить completed.
Если сохранение не удалось, запись должна остаться в промежуточном состоянии, которое можно обработать повторно. Иначе пользователь увидит готовый трек в интерфейсе, а сервер фактически не будет иметь сохранённого файла.
В предоставленной информации о сервисе нет тарифа на хранение файлов, срока жизни результатов или гарантированного времени доступности ссылок. Поэтому эти параметры следует задавать на стороне собственного приложения и не смешивать с ценой генерации.
Для самого API финансовая модель проще: музыкальный запуск — 59 ₽. Другие операции тарифицируются отдельно, например картинка по описанию стоит 9 ₽, видео по описанию — 119 ₽, озвучка текста — 18 ₽ за 1000 знаков, а расшифровка записи — 10 ₽ за запуск.
| Операция | Цена |
|---|---|
| Создать музыку (music) | 59 ₽ за запуск |
| Видео по описанию (video) | 119 ₽ за запуск |
| Картинка по описанию (image) | 9 ₽ за запуск |
| Расшифровка записи (stt) | 10 ₽ за запуск |
| Звук по описанию (sfx) | 9 ₽ за запуск |
| Оживить фото (photo-video) | 25 ₽ за запуск |
Для оценки нагрузки можно считать именно количество запусков, а не количество открытий страницы или проверок статуса. Например, 100 запусков music соответствуют 5900 ₽ стоимости операций по указанному тарифу. Повторные запросы к GET /tasks/{id} нужны для управления состоянием, но не следует путать их с новыми запусками операции.

6) Что показывать пользователю в ожидании
Пользователю не обязательно показывать технический статус processing. Интерфейс может объяснять состояние человеческим языком: «Трек готовится», «Сохраняем результат» или «Не удалось завершить обработку». При этом текст должен соответствовать реальному состоянию записи.
После нажатия кнопки генерации лучше сразу создать карточку будущего трека. В ней можно показать название или описание запроса, время создания и статус. Кнопка запуска при этом блокируется для того же request_id, чтобы пользователь не создавал дубликат простым двойным кликом.
Если используется вебхук, браузеру не нужно постоянно обращаться к API сервиса. Ваш backend получает callback, обновляет базу, а клиент обращается только к вашему endpoint статуса или получает событие через уже используемый механизм обновления интерфейса.
При перезагрузке страницы состояние должно восстанавливаться из базы. Это принципиальный момент архитектуры: генерация не принадлежит вкладке браузера. Она принадлежит серверной задаче, связанной с пользователем и сохранённой в базе.
Для ошибок лучше показывать действие, а не внутреннюю техническую информацию. Например, если задача завершилась ошибкой, интерфейс может предложить повторить генерацию, но сервер при этом должен создать новый запуск только после явного действия пользователя. Автоматический retry следует отличать от ручного повторения, потому что музыкальная операция тарифицируется за запуск.
Ещё один полезный статус — «Сохраняем трек». Он позволяет отделить момент, когда API сообщил о готовности, от момента, когда файл уже находится в вашем хранилище и доступен пользователю. После успешного сохранения карточка переходит в «Готово», а ссылка на результат берётся из вашей базы.
Ограничение частоты API — не более 60 запросов в минуту на ключ. Поэтому очередь должна учитывать не только количество одновременно выполняемых музыкальных задач, но и служебные обращения: повторные попытки, проверки состояния и другие операции. Вебхуки позволяют не расходовать запросы на постоянный polling для каждой активной задачи.
Отдельно стоит предусмотреть восстановление после перезапуска приложения. Если процесс очереди остановился, записи со статусом queued можно вернуть в обработку. Если внешний task_id уже сохранён, задача должна восстанавливаться как существующая операция, а не запускаться повторно.
В итоге архитектура для suno api получается достаточно компактной: пользователь создаёт локальную задачу, очередь запускает операцию, callback_url сообщает о готовности, сервер сохраняет результат, а интерфейс показывает состояние из собственной базы. Такой подход не требует удерживать браузерный запрос до конца генерации и отдельно защищает приложение от повторных отправок.
Частые вопросы
Нужно ли постоянно опрашивать GET /tasks/{id}?
Нет. В API предусмотрен необязательный callback_url, на который приходит POST после готовности задачи. GET /tasks/{id} можно оставить для проверки состояния и восстановления данных.
Сколько стоит создание одного музыкального трека?
Операция «Создать музыку (music)» стоит 59 ₽ за один запуск. Оплата производится за запуск, без абонентской платы и минимального платежа.
Как защититься от повторной генерации после таймаута?
Создавайте собственный идентификатор запроса и храните его с уникальным ограничением. При повторной отправке возвращайте уже созданную локальную задачу, если предыдущий запуск состоялся, вместо безусловного вызова /generate.
Можно ли использовать polling вместо вебхука?
Да. Состояние задачи доступно через GET /tasks/{id}. При частом polling нужно учитывать ограничение не более 60 запросов в минуту на один ключ.
Где хранить готовый трек?
Практически удобно хранить сам файл отдельно, а в базе — идентификатор задачи, статус, метаданные и ссылку или путь к сохранённому результату. В предоставленной справке нет отдельного тарифа сервиса за хранение готовых файлов.
Где получить API-ключ?
После входа ключ выдаётся в разделе API и доступ к ключу. Ключ показывается один раз, поэтому его следует сразу сохранить в защищённом хранилище приложения и передавать API через заголовок Authorization: Bearer <ключ>.
