Вы вызвали createTask и получили taskId вместо изображения. Теперь нужно забрать результат, не удерживая соединение открытым несколько минут, не заплатив дважды после тайм-аута и не потеряв задачу при перезапуске процесса.
Кратко:
- Быстрые модели изображений в скрипте: добавьте
?wait=30кcreateTask, и часто опрос вообще не понадобится. - Скрипты, локальная разработка, нет публичного сервера: опрашивайте задачу в ограниченном цикле, как в примере ниже.
- Любая задача, результата которой ждёт пользователь, видео или большой объём: используйте
callBackUrl, проверяйте подпись, быстро подтверждайте получение и обрабатывайте события через очередь. - Продакшен: совмещайте оба способа. Обратный вызов — быстрый путь, а периодическая проверка состояния подхватывает случаи, когда он так и не пришёл.
При прямом обращении к API ?wait=<seconds> (до 60) удерживает вызов createTask открытым до завершения задачи. Это подходит моделям изображений, которые справляются за секунды, но не видео. callBackUrl (с заглавной B) — публичный HTTPS-адрес для получения результата, а recordInfo — вызов проверки состояния, который оборачивает метод get_task в SDK. Все три описаны в справочнике задач.
Сначала попробуйте в Playground
Откройте Playground seedream-5-0-pro. Введите короткий Prompt, установите Resolution в 1k, нажмите Run model и посмотрите результат в Output; History показывает предыдущие запуски. Опрос и Webhook автоматизируют для приложения именно то ожидание, которое вы видите; они не создают второе изображение.
Выберите способ получения результата
| Ваше приложение | С чего начать | Почему |
|---|---|---|
| Локальный скрипт или редкая пакетная обработка | Опрос через SDK | Не нужно развёртывать публичный обработчик |
| Интерактивное приложение для изображений или видео | Проверенный Webhook | Реагируете на завершение генерации, не удерживая отдельный ожидающий процесс для каждой задачи |
| Производственный сервис | Webhook и периодическая сверка | Пропущенный обратный вызов не оставляет принятую задачу без дальнейшей обработки |
Выбирайте опрос, если задач немного или ваш компьютер не может принимать публичные HTTPS-вызовы. Выбирайте Webhook, если пользователи ждут результата в интерактивном приложении или длительные видеозадачи займут рабочие процессы.
Для задач с изображением на входе процесс загрузки SDK выполняет /files/{fileId}/commit и возвращает URI spicy://; см. документацию по медиа.
Запустите одну задачу через SDK
Установите spicyapi, задайте SPICY_API_KEY и запустите пример. Сохраните map-image.json, чтобы возобновить ту же задачу после прерывания. Если намеренно хотите другое изображение, используйте новое имя запуска. Python SDK предоставляет методы API; справочник задач охватывает полный жизненный цикл.
import json
import os
import time
import uuid
from pathlib import Path
from spicyapi import SpicyClient, output_assets
client = SpicyClient()
def run(model, inputs, name):
path = Path(name + ".json")
request = {"model": model, "input": inputs}
saved = json.loads(path.read_text()) if path.exists() else {"request": request, "key": str(uuid.uuid4())}
if saved["request"] != request:
raise ValueError("Changed input needs a new run name; preserve the old attempt.")
def save():
temporary = path.with_suffix(".tmp")
temporary.write_text(json.dumps(saved))
temporary.chmod(0o600)
temporary.replace(path)
if not saved.get("taskId"):
if time.time() - saved.get("sentAt", time.time()) >= 23 * 3600:
raise RuntimeError("Reconcile the saved attempt before submitting again.")
if "quote" not in saved:
saved["quote"] = client.quote_task(model=model, input_data=inputs)
if input(f"Maximum ${saved['quote']['maxCharge']}. Create? [y/N] ") != "y":
raise SystemExit("Stopped without creating this task.")
saved.setdefault("sentAt", time.time())
save()
quote = saved["quote"]
task = client.create_task(model=model, input_data=inputs,
idempotency_key=saved["key"], quote_id=quote["quoteId"], expected_cost=quote["estimatedCost"])
saved["taskId"] = task["taskId"]
save()
print("Task:", saved["taskId"])
for _ in range(360):
task = client.get_task(saved["taskId"])
if task["state"] not in {"queued", "running"} and task["state"] != "succeeded":
raise RuntimeError(str(task.get("errorMessage", task["state"])))
if task["state"] == "succeeded":
assets = output_assets(task)
if assets and assets[0].get("unavailable"):
raise RuntimeError("Output unavailable; keep the task ID for reconciliation.")
if assets and assets[0].get("url") and not assets[0].get("pending"):
return assets[0]
time.sleep(5)
raise TimeoutError("Rerun the same saved attempt to resume; no new task is needed.")
asset = run("bytedance/seedream-5.0-pro/text-to-image", {
"prompt": "a folded paper map on a wooden table, overhead", "resolution": "1k", "output_format": "png"
}, "map-image")
print(asset["url"])Правильно прочитайте ответ
Задача всегда находится в одном из состояний: queued, running, succeeded, failed или expired. Только первые два означают, что работа ещё продолжается. Поэтому завершайте цикл, когда состояние уже не queued и не running, вместо ожидания только succeeded, иначе неудачная задача будет опрашиваться бесконечно (в старых записях также может встречаться доступное только для чтения состояние canceled). Ссылка на результат находится в output.assets[0].url. При прямом вызове API читайте задачу из объекта data в оболочке ответа, а не из корня ответа. Принятую задачу отменить нельзя, а плата списывается только за succeeded; при failed и expired зарезервированная сумма обычно освобождается.
Что тайм-аут означает для приложения
HTTP 202 означает, что задача принята, а не что генерация завершена. Локальный тайм-аут прекращает ожидание, но не отменяет принятую задачу. Сохраните её ID и возобновите её вместо покупки ещё одной генерации. Возвращённый срок задачи задаёт границу восстановления, а не прогнозирует время рендеринга.
- Ответ на отправку потерян: сохраните запрос и его ключ, чтобы при восстановлении можно было найти исходную задачу.
- Генерация не удалась: покажите ошибку модели и дайте пользователю осознанно выбрать ещё одну попытку.
- Генерация успешна, файл ещё не готов: продолжайте ждать по той же задаче; изображение или видео ещё готовится к выдаче.
Новый запрос получает новый ключ идемпотентности. При восстановлении того же запроса сохраняйте ключ и входные данные без изменений. Справочник идемпотентности описывает 24-часовое окно повторного запроса.
Используйте Webhook для быстрого получения результата
Отправляйте события завершения на бэкенд приложения, затем обновляйте интерфейс пользователя через обычный канал уведомлений. Обратный вызов — не прямое сообщение браузеру пользователя.
Обработчик должен выполнить четыре действия:
- Проверить событие официальным SDK, используя исходное тело запроса.
- Убедиться, что задача относится к работе, которой управляет ваше приложение.
- Надёжно сохранить событие и устранить дубликаты до подтверждения получения.
- Поставить копирование результата и обновление приложения в очередь отдельно от HTTP-ответа.
Выбирайте этот способ, если: у вас уже есть бэкенд и нужны своевременные уведомления о завершении. Рассмотрите другой вариант, если: это короткий локальный скрипт без публичного обработчика; опрос проще.
Проверяйте завершение задачи и доставку медиа отдельно. Корректно сформированное событие может описывать неудачную задачу, а успешная задача может требовать обработки файлов, прежде чем приложение сообщит о готовности. Документация Webhook содержит правила проверки и доставки.
Сохраняйте периодическую сверку
Записывайте ID каждой принятой задачи. Периодически возвращайтесь к задачам, которые приложение ещё не разрешило, особенно после развёртывания, сбоя сети или смены секрета подписи. Пропускайте восстановленный результат через тот же идемпотентный обработчик завершения, что и Webhook, чтобы галерея не получила один файл дважды.
Храните незавершённые расчёты по оплате в журнале до окончания взаиморасчётов. Если локальные записи потеряны, история задач текущего ключа позволяет восстановить ID; этот путь описан в интеграции для продакшена.
Копируйте результаты до истечения срока
Ссылки на готовые результаты временные. Копируйте файлы в подконтрольное вам хранилище, с заголовком запроса User-Agent и именем файла, соответствующим возвращённому формату. Ошибка скачивания — повод восстановить существующий результат, а не генерировать его заново.
Сроки описаны в статье о хранении и доставке медиа, а процесс копирования — в статье о долговременном хранилище.
Решение
Для небольшого скрипта начните с опроса через SDK. Добавьте проверенные Webhook, когда появится интерактивный сервис, и сохраните периодическую сверку для восстановления. Сохранённый ID задачи объединяет все три пути: они получают результат одной генерации вместо отправки заменяющих задач.
Для большего числа одновременных работ добавьте ограниченную отправку пакетов. Для первой пары «изображение — видео» используйте руководство для начала работы.
Поручите вызов Agent
Используйте Agent, если хотите поручить ему подготовку входных данных и получение результата, а цену и творческие решения утверждать самостоятельно.
- CLI: сохраните
{"prompt": "a folded paper map on a wooden table, overhead", "resolution": "1k", "output_format": "png"}какinput.json, затем используйтеnpx @spicyapi/cli tasks create --model bytedance/seedream-5.0-pro/text-to-image --input-file input.json --wait. CLI запрашивает подтверждение цены; сохраните возвращённый ID задачи для восстановления. - MCP: попросите помощника использовать
spicyapi_task_createдляbytedance/seedream-5.0-pro/text-to-image, затемspicyapi_task_wait. MCP server запрашивает подтверждение цены до создания задачи. - Skill: установите через
npx skills add spicyapi-ai/spicy-skill, затем попросите$spicyapiвыполнить тот же процесс и остановиться для вашего одобрения. Skill использует настроенное подключение CLI или MCP.







