Has llamado a createTask y has recibido una taskId en lugar de una imagen. Ahora necesitas obtener el resultado sin mantener una conexión abierta durante minutos, pagar dos veces tras un timeout ni perder la tarea cuando se reinicie tu proceso.
La versión breve:
- Modelos de imagen rápidos en un script: añade
?wait=30acreateTasky, a menudo, podrás prescindir del sondeo. - Scripts, desarrollo local, sin servidor público: consulta la tarea con un bucle limitado como el del ejemplo siguiente.
- Cualquier trabajo que haga esperar al usuario, vídeo o grandes volúmenes: utiliza
callBackUrl, verifica la firma, confirma la recepción rápidamente y procesa los eventos desde una cola. - Producción: combina ambos métodos. El callback es la vía rápida; una revisión periódica del estado recupera los callbacks que nunca llegan.
En la API directa, ?wait=<seconds> (hasta 60) mantiene abierto createTask hasta que termine la tarea. Es adecuado para modelos de imagen que acaban en segundos, pero no para vídeo. callBackUrl (con B mayúscula) es la dirección HTTPS pública que recibe el resultado, y recordInfo es la llamada de estado que encapsula get_task en el SDK. La referencia de tareas explica las tres opciones.
Pruébalo primero en Playground
Abre el Playground de seedream-5-0-pro. Escribe un Prompt breve, establece Resolution en 1k, pulsa Run model y consulta el resultado en Output; History muestra las ejecuciones anteriores. El sondeo y los Webhooks automatizan para tu aplicación la espera que ves; no crean una segunda imagen.
Elige cómo recibir el resultado
| Tu aplicación | Empieza con | Motivo |
|---|---|---|
| Script local o lotes ocasionales | Sondeo mediante el SDK | No necesitas desplegar un receptor público |
| Aplicación interactiva de imagen o vídeo | Webhook verificado | Reacciona cuando termina la generación sin mantener un proceso en espera por tarea |
| Servicio en producción | Webhook más revisión de conciliación | Un callback perdido no deja desatendida una tarea aceptada |
Elige sondeo cuando haya pocas tareas o tu equipo no pueda recibir llamadas HTTPS públicas. Elige Webhooks cuando los usuarios esperen en una aplicación interactiva o las tareas de vídeo largas vayan a ocupar los procesos de trabajo.
Para tareas con imágenes de entrada, el flujo de carga del SDK ejecuta /files/{fileId}/commit y devuelve una URI spicy://; consulta la documentación de medios.
Ejecuta una tarea con el SDK
Instala spicyapi, configura SPICY_API_KEY y ejecuta el ejemplo. Conserva map-image.json para retomar la misma tarea tras una interrupción. Si quieres una imagen deliberadamente distinta, usa un nombre de ejecución nuevo. El Python SDK proporciona los métodos de la API; la referencia de tareas cubre el ciclo de vida completo.
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"])Interpreta correctamente la respuesta
Una tarea siempre está en uno de estos estados: queued, running, succeeded, failed o expired. Solo los dos primeros indican que sigue en curso. Por eso, termina el bucle cuando el estado no sea queued ni running, en vez de esperar únicamente a succeeded; de lo contrario, consultarás indefinidamente una tarea fallida (los registros antiguos también pueden mostrar canceled, un estado de solo lectura). El enlace al resultado está en output.assets[0].url. Si llamas a la API directa, lee la tarea del objeto data del contenedor de respuesta, no de la raíz. Una tarea aceptada no se puede cancelar, y solo se cobra succeeded; failed y expired normalmente liberan el importe retenido.
Qué significa un timeout para tu aplicación
HTTP 202 significa que se ha aceptado una tarea, no que haya terminado la generación. Un timeout local detiene la espera; no cancela la tarea aceptada. Conserva su ID y retómala en lugar de comprar otra generación. El plazo devuelto por la tarea marca el límite de recuperación, no una predicción del tiempo de renderizado.
- Se perdió la respuesta al envío: conserva la solicitud y su clave para que la recuperación pueda encontrar la tarea original.
- La generación falló: muestra el error del modelo y deja que el usuario decida expresamente si quiere otro intento.
- La generación terminó, pero el archivo está pendiente: sigue esperando en esa tarea; la imagen o el vídeo aún se está preparando para su entrega.
Una solicitud nueva recibe una clave de idempotencia nueva. Al recuperar la misma solicitud, mantén la clave y la entrada sin cambios. La referencia de idempotencia describe el plazo de 24 horas para repetir una solicitud.
Usa Webhooks como vía rápida
Envía los eventos de finalización al backend de tu aplicación y después actualiza la interfaz por tu canal habitual de notificaciones. Un callback no es un mensaje directo al navegador del usuario.
El receptor debe hacer cuatro cosas:
- Verificar el evento con el SDK oficial y el cuerpo original de la solicitud.
- Confirmar que la tarea corresponde a un trabajo de tu aplicación.
- Guardar el evento de forma persistente y deduplicarlo antes de confirmar su recepción.
- Encolar por separado la copia del resultado y la actualización de la aplicación, sin incluirlas en la respuesta HTTP.
Elígelo cuando: ya tengas un backend y quieras recibir avisos de finalización rápidamente. Busca otra opción cuando: sea un script local breve sin receptor público; el sondeo es más sencillo.
Comprueba por separado la finalización de la tarea y la entrega del archivo. Un evento bien formado puede describir una tarea fallida, mientras que una tarea completada puede requerir gestionar los archivos antes de que tu aplicación la marque como lista. La documentación de Webhooks recoge las condiciones de verificación y entrega.
Mantén una revisión de conciliación
Guarda el ID de cada tarea aceptada. Revisa periódicamente las tareas que tu aplicación no haya resuelto, sobre todo después de un despliegue, una caída de red o un cambio del secreto de firma. Pasa el resultado recuperado por el mismo manejador de finalización idempotente que usa un Webhook, para que la galería no reciba el mismo archivo dos veces.
Mantén los cobros sin resolver en el registro hasta que termine la liquidación. Si pierdes los registros locales, el historial de tareas de la clave actual permite recuperar los ID; la integración en producción explica esta vía.
Copia los resultados antes de que caduquen
Los enlaces a resultados listos son temporales. Copia los archivos a un almacenamiento que controles, con una cabecera User-Agent en la solicitud y un nombre de archivo que coincida con el formato devuelto. Un fallo de descarga es motivo para recuperar el resultado existente, no para volver a generarlo.
Consulta los plazos en retención y entrega de medios y el flujo de copia en almacenamiento duradero.
La decisión
Empieza con sondeo mediante el SDK para un script pequeño. Añade Webhooks verificados cuando tengas un servicio interactivo y conserva la revisión periódica para recuperar tareas. El ID guardado conecta las tres vías: recogen una única generación en lugar de enviar tareas de sustitución.
Para ejecutar más trabajos a la vez, añade un despacho de lotes limitado. Para tu primer par de imagen y vídeo, utiliza la guía de inicio.
Deja que un Agent gestione la llamada
Utiliza un Agent si quieres que prepare las entradas y devuelva el resultado mientras tú apruebas el precio y las decisiones creativas.
- CLI: guarda
{"prompt": "a folded paper map on a wooden table, overhead", "resolution": "1k", "output_format": "png"}comoinput.jsony utilizanpx @spicyapi/cli tasks create --model bytedance/seedream-5.0-pro/text-to-image --input-file input.json --wait. La CLI pide confirmar el precio; conserva el ID de tarea devuelto para la recuperación. - MCP: pide a tu asistente que use
spicyapi_task_createparabytedance/seedream-5.0-pro/text-to-imagey, después,spicyapi_task_wait. El MCP server solicita confirmar el precio antes de crear la tarea. - Skill: instala con
npx skills add spicyapi-ai/spicy-skilly pide a$spicyapique ejecute el mismo flujo y se detenga para solicitar tu aprobación. El Skill usa una conexión CLI o MCP configurada.







