La API de Seedance 2.0 no devuelve el video final en la primera llamada. Funciona como una tarea asíncrona: creas el trabajo, guardas el ID que devuelve el proveedor, consultas el estado o recibes un callback, y cuando el estado llega a succeeded copias content.video_url a tu propio almacenamiento.
En la ruta internacional de BytePlus ModelArk, el endpoint de creación es https://ark.ap-southeast.bytepluses.com/api/v3/contents/generations/tasks. Los modelos públicos de Seedance 2.0 son dreamina-seedance-2-0-260128 y dreamina-seedance-2-0-fast-260128. Si usas Volcengine Ark en China, usa su endpoint regional y los modelos doubao-*; no mezcles esos IDs con los de BytePlus.

Envía una sola tarea
La llamada de creación debe ser el único punto donde se inicia la generación. Antes de llamarla, valida ruta, modelo, prompt, referencias, relación de aspecto, resolución, duración, audio y callback. Después de recibir respuesta, guarda el task ID junto a tu propio job ID, hash de solicitud, usuario y parámetros de salida.
bashcurl -X POST "https://ark.ap-southeast.bytepluses.com/api/v3/contents/generations/tasks" \ -H "Authorization: Bearer $ARK_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "dreamina-seedance-2-0-260128", "content": [ { "type": "text", "text": "A quiet product demo shot, slow camera move, clean studio light" } ], "ratio": "16:9", "resolution": "720p", "duration": 5, "generate_audio": false, "return_last_frame": true, "callback_url": "https://example.com/webhooks/seedance" }'
Si la llamada HTTP se corta después del envío, no crees otra tarea de inmediato. Primero consulta tu registro local: si ya existe un provider task ID, continúa con polling; si no existe y la creación no terminó, reintenta sobre el mismo job local.
Consulta estado o procesa callbacks
Los estados de Seedance se tratan como una máquina de estados, no como una barra de progreso.
| Estado | Significado | Acción |
|---|---|---|
queued | Trabajo aceptado | Esperar con backoff |
running | Generación activa | Seguir consultando |
succeeded | Salida lista | Copiar content.video_url |
failed | Fallo del proveedor | Guardar código y mensaje |
expired | Ventana agotada | Decidir si el usuario debe reenviar |
cancelled | Tarea cancelada | Detener polling |
bashcurl -X GET \ "https://ark.ap-southeast.bytepluses.com/api/v3/contents/generations/tasks/$TASK_ID" \ -H "Authorization: Bearer $ARK_API_KEY"
Los callbacks reducen latencia, pero no sustituyen al polling de reparación. Mantén un worker que revise trabajos vivos con backoff para cubrir despliegues, callbacks perdidos y errores temporales de verificación.
Descarga y guarda el resultado
Cuando la tarea termina, content.video_url apunta a un MP4 temporal. No lo uses como URL permanente. La documentación indica que el video generado se limpia después de 24 horas, así que tu handler de éxito debe copiarlo a tu bucket.
tsasync function saveSeedanceOutput(job, task) { const url = task.content?.video_url; if (!url) throw new Error("Seedance succeeded without video_url"); const res = await fetch(url); if (!res.ok) throw new Error(`download failed: ${res.status}`); await storage.putObject({ key: `seedance/${job.id}/output.mp4`, body: res.body, contentType: "video/mp4", }); }
Guarda también el payload del proveedor: modelo, estado, seed, ratio, resolution, duration, generate_audio y usage.total_tokens. Esos campos ayudan con soporte, costes y reproducción.

Usa referencias dentro de content
Las referencias no son adjuntos sueltos. El array content define el modo de generación. Para primer fotograma usa first_frame; para primer y último fotograma usa first_frame y last_frame; para referencias multimodales usa reference_image, reference_video y reference_audio.
json{ "model": "dreamina-seedance-2-0-260128", "content": [ { "type": "text", "text": "Use [image 1] as the product reference and keep the motion minimal." }, { "type": "image_url", "image_url": { "url": "https://cdn.example.com/reference-product.webp" }, "role": "reference_image" } ], "ratio": "16:9", "duration": 5, "generate_audio": false }
Valida formatos, tamaños, duración y permisos antes de llamar a la API. El audio no debe enviarse solo; necesita al menos una imagen o un video de referencia. Si el flujo usa personas reales, añade revisión de consentimiento antes del envío.
Reintenta sin duplicar generaciones
El error caro es crear dos videos pagados para el mismo job. Construye un request hash con modelo, ruta, prompt normalizado, referencias, parámetros y usuario. Si ya existe un job con ese hash, devuelve ese job en vez de crear otro.

| Operación | Regla |
|---|---|
| Crear sin task ID guardado | Reintentar tras revisar job y hash |
| Crear con task ID guardado | No reenviar; continuar polling |
| Consultar estado | Reintentar con backoff |
| Procesar callback | Debe ser idempotente |
| Descargar video | Reintentar hasta que expire la URL |
| Tarea fallida | Clasificar error antes de reenviar |
FAQ
¿La API es síncrona?
No. La primera llamada devuelve un task ID; el MP4 aparece después en content.video_url.
¿Polling o callback?
Usa callback para rapidez y polling como reparación. Un sistema solo con callbacks pierde trabajos durante despliegues o fallos de verificación.
¿Puedo conservar el video_url?
No como enlace permanente. Cópialo a tu almacenamiento porque la URL temporal se limpia después de 24 horas.
¿Qué hago si falla la creación?
Consulta tu job local por request hash. Si ya hay task ID, no crees otra tarea; continúa con polling.
