# Seedance 2.0 — /videos/generations

API de vídeo Seedance 2.0 (ByteDance), con cobro por segundo. Trabajos asíncronos: al enviar recibes al momento un id de trabajo y luego consultas el resultado o dejas que un webhook te envíe el MP4. 5–15 segundos, 480P / 720P / 1080P, cinco proporciones; las imágenes, los vídeos y el audio de referencia pueden ir en la misma petición, y todos los clips llevan sonido. Los fallos no se cobran.

**Endpoint:** `POST https://kovapi.com/v1/videos/generations`

## Inicio rápido

Tres pasos: envía un trabajo, consúltalo y toma el enlace del vídeo. Sustituye YOUR_API_KEY por tu clave y los comandos de abajo funcionan tal cual.

**Paso 1 · Envía el trabajo (devuelve al momento un id de trabajo, HTTP 202)**

```
curl https://kovapi.com/v1/videos/generations \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model": "seedance-2.0", "prompt": "Un barco de papel avanza por una calle de neones en una noche de lluvia, la cámara lo sigue a ras de suelo, salpicaduras y reflejos nítidos", "duration": 5, "resolution": "720P", "size": "16:9"}'
```

Respuesta (HTTP 202)

```
{
  "id": "img_5c2e8f1a9b3d7e6f0a4c8b2d",
  "object": "video.generation.job",
  "status": "queued",
  "created": 1791281400,
  "model": "seedance-2.0"
}
```

**Paso 2 · Consulta cada 5–10 segundos hasta que `status` sea `succeeded` o `failed`**

```
curl https://kovapi.com/v1/videos/jobs/img_5c2e8f1a9b3d7e6f0a4c8b2d \
  -H "Authorization: Bearer YOUR_API_KEY"
```

Respuesta cuando sale bien

```
{
  "id": "img_5c2e8f1a9b3d7e6f0a4c8b2d",
  "object": "video.generation.job",
  "status": "succeeded",
  "created": 1791281400,
  "model": "seedance-2.0",
  "data": [
    {"url": "https://img.kovapi.com/i/8c1f0e5d2b7a49e3a6d0c4f1b9e2a7d5.mp4"}
  ],
  "usage": {"clips": 1, "model": "seedance-2.0"}
}
```

**Paso 3 · `data[0].url` es el clip (MP4). Se guarda 15 días; descárgalo en tu propio almacenamiento para conservarlo más tiempo.**

**Ejemplo completo (Python: enviar, esperar y guardar en disco)**

```
import time
import requests

BASE = "https://kovapi.com/v1"
HEADERS = {"Authorization": "Bearer YOUR_API_KEY"}

# 1. enviar el trabajo
job = requests.post(f"{BASE}/videos/generations", headers=HEADERS, json={
    "model": "seedance-2.0",
    "prompt": "Un barco de papel avanza por una calle de neones en una noche de lluvia, la cámara lo sigue a ras de suelo, salpicaduras y reflejos nítidos",
    "duration": 5,
    "resolution": "720P",
    "size": "16:9",
}).json()
if "error" in job:
    raise SystemExit(job["error"]["message"])

# 2. consultar cada 10 segundos, durante una hora como máximo
deadline = time.time() + 3600
while job["status"] not in ("succeeded", "failed") and time.time() < deadline:
    time.sleep(10)
    job = requests.get(f"{BASE}/videos/jobs/{job['id']}", headers=HEADERS).json()

# 3. descargar el vídeo
if job["status"] == "succeeded":
    url = job["data"][0]["url"]
    with open("seedance-2.0.mp4", "wb") as f:
        f.write(requests.get(url).content)
    print("saved", url)
elif job["status"] == "failed":
    print("failed:", job["error"]["code"], job["error"]["message"])
else:
    print("still", job["status"], job["id"])
```

Mientras está en cola, la consulta también devuelve `queue_position` (trabajos por delante del tuyo) y `eta_seconds` (espera estimada). Si se agota el tiempo máximo que fijaste, no vuelvas a enviarlo: el trabajo sigue ahí, así que vuelve a consultar el mismo id más tarde.

## Autenticación
```
Authorization: Bearer YOUR_API_KEY
Content-Type: application/json
```

## De un vistazo

| Duración | 5–15 s |
| --- | --- |
| Resolución | 480P / 720P / 1080P |
| Proporción | 16:9 / 9:16 / 1:1 / 4:3 / 3:4 |
| Imágenes de referencia | hasta 9 · como referencia (sujeto, escena, estilo), nunca como fotograma |
| Vídeos de referencia | hasta 3, 15 s en total |
| Audio de referencia | hasta 3, de 15 s como máximo cada uno |
| Sonido | siempre activado (diálogos, efectos y música generados por el modelo); no se puede desactivar |
| Longitud del prompt | hasta 5 000 bytes (UTF-8; unos 1 666 caracteres chinos) |
| Forma de cobro | por segundo: tarifa × (segundos de salida + segundos de vídeo de referencia) |

## Parámetros de la petición

El cuerpo JSON de `POST /v1/videos/generations`:

| Parámetro | Tipo | Obligatorio | Descripción |
| --- | --- | --- | --- |
| model | string | Sí | El ID del modelo: `seedance-2.0`. |
| prompt | string | Sí | Lo que ocurre en el clip: escena, cámara y movimiento. Hasta 5 000 bytes (UTF-8; un carácter chino cuenta como 3 bytes, así que caben unos 1 666). |
| duration | integer | No | Duración en segundos: cualquier entero de 5 a 15; si se omite, 5. Alias: `seconds`. |
| resolution | string | No | Resolución: 480P / 720P / 1080P; si se omite, 720P. Incluye la P (`720` sin la P se rechaza). |
| size | string | No | Proporción: 16:9 / 9:16 / 1:1 / 4:3 / 3:4; si se omite, 16:9. Alias: `aspect_ratio`. |
| images | array | No | Imágenes de referencia, hasta 9. Fijan el sujeto, la escena y el estilo, y nunca se usan como fotograma. Menciónalas en el prompt como `@Image1`, `@Image2`, en el orden en que las envías. Una sola imagen puede ir en `image`. |
| video_references | array | No | Vídeos de referencia, hasta 3 y 15 s en total. Pueden ir junto con imágenes de referencia (las fotos fijan el aspecto; el vídeo, el movimiento y la cámara); menciónalos en el prompt como `@Video1`. Cada uno puede ser una URL pública o `data:video/mp4;base64,...` (mp4 / mov, hasta 50 MB cada uno y 100 MB por petición). El enlace tiene que descargar el archivo en sí; los enlaces a páginas web como YouTube, Bilibili, TikTok o carpetas compartidas en la nube se rechazan con un 400 al enviar. |
| audio_reference | array | No | Audio de referencia, hasta 3, de 15 s como máximo cada uno (se mide la duración real: un audio de 15,1 s se rechaza), como URL http(s) públicas; tiene que ir junto con una imagen o un vídeo de referencia. Menciónalo en el prompt como `@Audio1`. El enlace tiene que descargar el archivo en sí; los enlaces a páginas web como YouTube, Bilibili, TikTok o carpetas compartidas en la nube se rechazan con un 400 al enviar. |
| audio | boolean | No | Cada clip sale con sonido (diálogos, efectos y música generados a partir del prompt); no se puede desactivar y `false` no tiene efecto. |
| callback_url | string | No | Adónde enviar el resultado cuando termine el trabajo; consulta la sección de webhooks más abajo. |
| n | integer | No | Cada trabajo genera un solo clip; no hace falta enviarlo. Un valor mayor que 1 también genera un solo clip, pero al enviar se retiene el precio de n clips y lo retenido de más se devuelve cuando termina el trabajo. |

Los demás campos (`seed`, `negative_prompt`, `fps`, `watermark` y similares) no se reenvían y no cambian el precio; los resultados siempre son MP4 y se devuelven como `url`.

## Precio

Se cobra por segundo: coste = tarifa por segundo de la resolución elegida × (segundos de salida + los segundos de cada vídeo de referencia). Las imágenes y el audio de referencia son gratis. Se retiene al enviar y se reembolsa íntegro si falla. 1 crédito = $0.005.

| Resolución | Créditos / s | USD / s | Clip de 5 segundos |
| --- | --- | --- | --- |
| 480P Aún no disponible | 14 | $0.07 | $0.35 |
| 720P | 30 | $0.15 | $0.75 |
| 1080P | 60 | $0.30 | $1.50 |

El nivel 480P aún no está disponible y se rechaza con 400 al enviar; esta tabla se actualizará sola cuando se abra.

## Material de referencia

- Hasta 9 imágenes de referencia fijan el sujeto, la escena, el producto y el estilo. Seedance nunca usa una imagen como fotograma: para que las personas u objetos de una imagen aparezcan en el clip, menciónala en el prompt (`@Image1`, `@Image2` …, en el orden en que las envías).

- Hasta 3 vídeos de referencia (15 s en total) fijan el movimiento, la cámara y el ritmo; hasta 3 pistas de audio de referencia (de 15 s como máximo cada una; una de 15,1 s se rechaza) fijan una voz o la música y tienen que ir junto con una imagen o un vídeo. Los tres tipos pueden ir en la misma petición; menciónalos como `@Image1` / `@Video1` / `@Audio1`.

- Se admiten rostros humanos reales como imágenes de referencia, sin necesidad de ocultarlos. Asegúrate de tener derecho a usar la imagen de las personas que aparecen.

- Cada imagen puede ser una URL pública de imagen, un `data:` URI o base64, de hasta 16 MB; para enviar varias, ponlas en un array en `images`. Guardamos las imágenes antes de poner el trabajo en cola; si alguna no se puede leer, la petición se rechaza con 400, `message` indica cuál y no se cobra nada.

- Los archivos de referencia que alojamos se guardan 7 días; los resultados, 15 días.

**Imágenes de referencia y un vídeo de referencia juntos**

```
curl https://kovapi.com/v1/videos/generations \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model": "seedance-2.0", "prompt": "La chica de @Image1 baila la coreografía de @Video1 en la calle de @Image2", "images": ["https://example.com/character.png", "https://example.com/street.png"], "video_references": ["https://example.com/dance.mp4"], "duration": 5, "size": "16:9"}'
```

`@Image1` es la primera imagen de referencia y `@Video1`, el primer vídeo de referencia. Los vídeos de referencia no se cobran; solo se cobra la duración del clip.

## Requisitos de contenido

Todos los trabajos de Seedance pasan por una revisión de contenido que cubre el prompt, los archivos de referencia y el vídeo final, tanto la imagen como el sonido. Un trabajo que no la supera termina con `content_policy` y no se cobra.

### Cuándo se revisa

Al enviar se revisan el prompt y las referencias; cuando el vídeo está generado se revisan de nuevo la imagen y el sonido. Por eso un rechazo puede llegar segundos después de enviar o solo al terminar el renderizado (puede tardar más de media hora). Un trabajo que termina con `status` `failed` y `error.code` `content_policy` no superó la revisión; con un webhook recibes el evento `video.failed`.

### Sin cargo

Un trabajo rechazado no cuesta nada: el importe retenido al enviarlo se devuelve íntegro cuando termina, y el registro de llamadas lo muestra como no cobrado.

### Sonido

El sonido (diálogos, narración, canto y efectos) lo genera el modelo a partir de tu prompt y se revisa junto con la imagen: los diálogos, la narración y las letras del prompt se locutan o se cantan. Para un vídeo sin voces, no pongas diálogos ni letras en el prompt. Este modelo siempre añade sonido; no se puede desactivar y `audio: false` no tiene efecto. El audio de referencia que subas también se revisa, y la música con derechos de autor usada como referencia puede ser rechazada. Cuando el sonido no supera la revisión, `error.param` es `audio` y el mensaje indica si fue el sonido generado o tu audio de referencia.

### Contenido que más se rechaza

- Contenido sexual, desnudos o insinuación sexual, incluidas imágenes generadas o compuestas con IA

- Sangre, crueldad, autolesiones y heridas violentas como disparos o hemorragias

- Armas de fuego, explosivos y drogas

- Cualquier contenido sexualizado con menores; también se rechazan menores con alcohol, tabaco, drogas o en situaciones de peligro

- Políticos, famosos, deportistas, empresarios y otras figuras públicas, por su aspecto o su voz, incluidos los parecidos

- Personajes y propiedad intelectual protegidos, como personajes de Disney o Marvel

- Contenido de odio o discriminatorio, y noticias falsas

### Guiones largos

Una sola toma que incumpla las normas hace fallar el vídeo entero, y donde más ocurre es en los guiones largos: un tiroteo o un primer plano de sangre en una escena de acción, o menores bebiendo o fumando, hunden todo el vídeo. Lee el guion completo antes de enviarlo y elimina o reescribe esas tomas.

### Personas reales

Para usar la foto de una persona real como referencia necesitas el derecho a usar su imagen.

### Tras un rechazo

Reenviar lo mismo vuelve a ser rechazado. Localiza la parte que probablemente falla, reescribe el prompt o cambia la referencia y vuelve a enviarlo.

Código de error: `content_policy` · `status: failed`

## Estado y campos del trabajo

`GET /v1/videos/jobs/{id}` devuelve estos estados; cada campo se describe en [Trabajos asíncronos](https://docs.kovapi.com/es/guide/async-jobs#fields). Consulta cada 5–10 segundos o deja que un webhook (más abajo) te envíe el resultado. Las consultas son gratis, y cualquier clave de la misma cuenta puede consultar el trabajo.

| Estado | Significado |
| --- | --- |
| queued | En cola. Incluye `queue_position` (trabajos por delante del tuyo) y `eta_seconds` (espera estimada). |
| processing | Generándose. |
| succeeded | Terminado. El resultado está en `data[0].url` y el detalle del cobro en `usage`. |
| failed | Falló. El motivo está en `error`; los créditos reservados se devuelven íntegros. |

- `processing` puede volver a `queued`, por ejemplo mientras el servicio está saturado, mientras se vuelve a obtener un clip ya terminado o tras un reinicio del servicio. Si un trabajo no se puede terminar, la pasarela puede volver a generarlo automáticamente; se reintente las veces que se reintente, el trabajo se cobra una sola vez.

- Cada cuenta ejecuta hasta 50 trabajos a la vez; los demás esperan en orden de envío, y un trabajo que no ha empezado a los 15 minutos falla y se reembolsa íntegro. Los registros de trabajos se guardan 3 días y los enlaces de los vídeos, 15 días.

## Cuánto tarda

Normalmente 3–8 minutos; algunos tardan 15 minutos o más.

Llamadas reales de los últimos 30 días: mediana 4.8 min; el 90% termina en 7.2 min o menos.

## Errores y reembolsos

Un error al enviar se devuelve al momento como HTTP 4xx / 5xx: no se crea ningún trabajo y no se cobra nada. Un fallo después de que el trabajo fue aceptado lo informa el endpoint del trabajo, y la retención se reembolsa íntegra. Todos los códigos están en [Códigos de error](https://docs.kovapi.com/es/guide/errors).

### Errores al enviar

| HTTP | code | Causa | Qué hacer |
| --- | --- | --- | --- |
| 400 | invalid_request | Un parámetro no es válido: la duración, la resolución, la proporción o la cantidad de referencias está fuera del rango de este modelo, el prompt es demasiado largo, no se puede leer una imagen o un vídeo de referencia, se envió `audio: false` a un modelo cuyos clips siempre llevan sonido, etc. `message` indica cuál. | Corrige lo que indica `message` y vuelve a enviar la petición. |
| 400 | invalid_callback_url | `callback_url` no es una dirección http(s) pública o supera los 2000 caracteres. | Usa una dirección accesible desde internet. |
| 400 | model_not_found | El ID del modelo está mal escrito, falta `model`, o el modelo no se puede usar en este endpoint (por ejemplo, un modelo de chat en el endpoint de imágenes). | Usa un ID de modelo de `GET /v1/models` o de la [referencia de la API](https://docs.kovapi.com/es/api). |
| 401 | invalid_api_key | La clave no existe, se eliminó o está desactivada, o la cuenta está desactivada. | Revisa la clave en la página API Keys de la consola. |
| 402 | insufficient_quota | No hay saldo suficiente para retener el importe de esta petición. La pasarela retiene primero el coste máximo posible de la petición, así que una ráfaga de llamadas simultáneas también puede provocarlo. | Si se debe a una ráfaga, espera unos segundos y reintenta; si no, recarga. |
| 429 | rate_limit_exceeded | Se superó el límite de peticiones por minuto configurado en esta clave (contado sobre los últimos 60 segundos). Sin cabecera `Retry-After`. | Espera unos segundos y reintenta, o sube el límite de la clave en la consola. |
| 503 | model_maintenance | El modelo está en mantenimiento y no acepta peticiones; mientras tanto, ningún otro modelo responde en su lugar. | Reintenta cuando termine el mantenimiento; consulta el [estado del servicio](https://kovapi.com/es/status). |
| 503 | service_unavailable | Un fallo temporal de nuestro lado, por ejemplo al guardar un archivo de referencia. No se creó ningún trabajo y no se cobró nada. | Reintenta en breve. |

### Trabajos fallidos (`status` `failed`)

| code | Cuándo aparece | Qué hacer |
| --- | --- | --- |
| content_policy | Vídeo: el prompt, una referencia, o la imagen o el sonido generados no superaron la revisión de contenido (`param` es `audio` cuando lo que se marcó fue el sonido). Wan Standard y Prime: la entrada no superó la revisión. `error.reason` es `FaceDetectionSuspect` (posible rostro humano real) o `DataInspectionFailed` (fallo de revisión del contenido de entrada). `error.message` explica el motivo y cómo ajustar la solicitud. Las tareas fallidas no se cobran. | Reescribe el prompt o cambia la referencia antes de volver a enviarlo; la misma entrada se rechaza otra vez. |
| render_failed | Vídeo: esta vez no salió ningún clip; suele ser algo puntual. | Volver a enviar la misma petición suele funcionar. |
| invalid_material | Vídeo: el proveedor encontró un problema en la entrada. Es un error genérico y a menudo puntual. | Primero reenvíalo tal cual; si se repite, revisa las referencias y la longitud del prompt. |
| duration_rejected | Vídeo: no se aceptó la duración. Lo más habitual es que un audio o un vídeo de referencia supere el límite, aunque sea por una fracción de segundo. | Recorta el audio o el vídeo de referencia hasta quedar dentro del límite y vuelve a enviarlo; la misma entrada volverá a fallar. |
| reference_unreadable | Vídeo: el modelo no pudo descargar una imagen, un vídeo o un audio de referencia; lo más habitual es que el enlace sea una página web, como YouTube o una carpeta compartida en la nube. | Usa un enlace que descargue el archivo en sí o sube el archivo en el Playground; la misma entrada volverá a fallar. |
| render_timeout | Vídeo: la cola o el render tardaron demasiado y el proveedor abandonó el trabajo. | Vuelve a enviarlo. |
| wait_timeout | Vídeo: no hubo resultado en 120 minutos, así que el trabajo se dio por agotado y se reembolsaron todos los créditos; si el vídeo termina después, se entrega gratis. | Vuelve a enviarlo. |
| upstream_unstable | Vídeo: la conexión se cortó durante el render. | Vuelve a enviarlo. |
| result_fetch_failed | El vídeo se generó, pero su descarga o almacenamiento falló una y otra vez de nuestro lado. | Vuelve a enviarlo. |
| upstream_error | Cualquier otro fallo: los reintentos automáticos no bastaron, el trabajo esperó 15 minutos sin empezar, el proveedor lo rechazó sin dar motivo, etc.; `message` indica cuál. | Volver a enviarlo suele funcionar. |

Decide según `code`, no según el texto de `message`. Trata cualquier `code` que no reconozcas (o un error sin `code`) como `upstream_error`: volver a enviarlo suele funcionar. Todos los trabajos fallidos se reembolsan íntegros.

## Webhooks en lugar de consultas

Añade `callback_url` al enviar y, en cuanto termine el trabajo, con éxito o con error, la pasarela enviará a esa URL exactamente lo mismo que devuelve el endpoint de consulta, firmado y con reintentos si la entrega falla. También puedes configurar una dirección para toda la cuenta en Configuración → Webhook, en la consola. [Documentación de webhooks →](https://docs.kovapi.com/es/webhooks)

```
curl https://kovapi.com/v1/videos/generations \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model": "seedance-2.0", "prompt": "Un barco de papel avanza por una calle de neones en una noche de lluvia, la cámara lo sigue a ras de suelo, salpicaduras y reflejos nítidos", "duration": 5, "resolution": "720P", "size": "16:9", "callback_url": "https://your-domain.com/webhook"}'
```
