Curso
En este tutorial, te mostraré cómo usar la API de BytePlus Ark para interactuar con Seedance 2 usando Python.
Seedance 2 es un potente modelo de IA generativa de ByteDance que actúa como un "director multimodal", capaz de procesar texto, imágenes, vídeo y audio a la vez para crear secuencias cinematográficas con audio sincronizado. Tanto si quieres animar imágenes estáticas como dar vida a prompts de texto complejos, esta guía te acompaña en la configuración e implementación.
Generar una clave de API de Seedance 2
Para empezar, necesitamos crear una cuenta en BytePlus y una clave de API. Para ello:
- Ve al sitio web oficial de BytePlus y crea una cuenta.
- Accede a la página de creación de claves de API y haz clic en “Create API Key”.
Esta clave se usa para hacer solicitudes a la API ModelArk, su API oficial.
Lo más recomendable es guardar la clave en un archivo llamado .env en la misma carpeta donde escribimos nuestros scripts de Python. Asegúrate de mantenerla en secreto: cualquiera podría usarla para interactuar con la API con tu cuenta.
Pega la clave de API en el archivo .env con el siguiente formato:
ARK_API_KEY=<paste_api_key_here>
Activar el modelo Seedance 2 y estructura de precios
Para poder usar Seedance 2, primero debemos activar el modelo:

Ten en cuenta que el uso de la API no es gratuito y activar Seedance 2 requiere comprar créditos específicos del modelo. Para este artículo, compré un paquete de 7 millones de tokens para Seedance 2 por 30,10 $.

Por desgracia, los tokens son específicos de cada modelo. Es decir, si compramos tokens para las versiones fast o mini de Seedance 2, no podremos usarlos con el modelo base.
A continuación, tienes una tabla con los planes de precios de la API:

Cómo generar vídeos con la API de Seedance 2 en Python
He incluido los scripts completos correspondientes al código de este tutorial en este repositorio de GitHub.
En esta sección, aprenderás a hacer una solicitud de texto a vídeo para generar este vídeo:
1. Configura tu entorno de Python y el SDK
Para conectarnos a la API de BytePlus Ark con Python, usaremos el paquete oficial byteplus-python-sdk-v2.
La forma más sencilla de preparar las dependencias del proyecto es crear un entorno de Anaconda e instalar el archivo requirements.txt, así:
git clone git@github.com:fran-aubry/seedance2-tutorial.git
conda create --name seedance2 python=3.11
conda activate seedance2
pip install -r requirements.txt
2. Inicializar el cliente de BytePlus Ark en Python
Crea un nuevo script llamado text_to_video.py en la misma carpeta que el archivo .env que creamos antes.
Primero, importamos los paquetes necesarios:
import os
from byteplussdkarkruntime import Ark
from dotenv import load_dotenv
Después, cargamos la clave de API desde el archivo .env usando la función load_dotenv():
load_dotenv()
API_KEY = os.getenv("ARK_API_KEY")
Ahora inicializamos el cliente Ark, que nos permite hacer solicitudes a la API de BytePlus Ark.
Usamos la librería os para obtener la clave de API de las variables de entorno y establecemos la URL base adecuada para el acceso internacional a la API:
client = Ark(
api_key=os.getenv("ARK_API_KEY"),
base_url="https://ark.ap-southeast.bytepluses.com/api/v3"
)
Por último, definimos un prompt y usamos la función content_generation.tasks.create() del cliente para generar el vídeo:
prompt = """
A cinematic close-up of a weary but hopeful female astronaut inside a dimly lit, dusty spaceship cabin.
Soft blue light illuminates her face.
She takes a deep breath, showing subtle emotional relief, looks directly into the camera, and says:
'We finally made it. The atmosphere is stable.'
The ambient sound of a low, rhythmic spaceship engine hums in the background.
"""
response = client.content_generation.tasks.create(
model="dreamina-seedance-2-0-260128",
content=[
{
"type": "text",
"text": prompt,
}
],
generate_audio=True,
ratio="16:9",
duration=8,
watermark=True,
resolution="480p",
)
task_id = response.id
print(f"Task successfully submitted! Task ID: {task_id}")
Tras solicitar la generación, no obtenemos el vídeo al instante porque tarda un tiempo en crearse.
Esto significa que la variable response no es el vídeo en sí.
En su lugar, es un objeto que contiene información sobre la tarea de generación del vídeo.
En particular, incluye el identificador de la tarea, que nos permite seguir el progreso y descargar el vídeo cuando el modelo termine.
3. Seguir el estado de la generación (sondeo de tareas)
Podemos recuperar el estado de una tarea proporcionando su identificador a la función content_generation.tasks.get(), así:
task = client.content_generation.tasks.get(task_id=task_id)
status = task.status
print(f"Status: {status}")
Para facilitar el seguimiento, creamos un nuevo script utils.py en la misma carpeta y añadimos esta función que monitoriza el progreso dado el cliente y el identificador de la tarea:
import time
import requests
def poll_task(client, task_id, poll_interval=5):
while True:
try:
task_status = client.content_generation.tasks.get(task_id=task_id)
status = getattr(task_status, "status", None) or (task_status.get("status") if isinstance(task_status, dict) else None)
if status == "succeeded":
print("\nTask completed successfully.")
return task_status.content.video_url
elif status == "failed":
error_details = getattr(task_status, "error", "Unknown error occurred during processing.")
raise RuntimeError(f"Task failed: {error_details}")
print(".", end="", flush=True)
time.sleep(poll_interval)
except Exception as e:
if isinstance(e, RuntimeError):
raise
print(f"\nWarning: Error polling task (will retry): {e}")
time.sleep(poll_interval)
4. Descargar el vídeo generado con requests en Python
La función poll_task() que implementamos arriba devuelve la URL del vídeo
Ahora que podemos seguir el proceso de generación, solo nos falta una forma de descargarlo cuando termine.
Como la API de BytePlus Ark devuelve una URL directa al archivo de vídeo generado en la respuesta de la tarea, podemos descargarlo con la librería requests.
Aquí tienes una función que lo hace:
def download_video(url, output_path = "video.mp4"):
try:
print(f"Downloading video from {url} to {output_path}...")
response = requests.get(url, stream=True)
response.raise_for_status()
with open(output_path, 'wb') as f:
for chunk in response.iter_content(chunk_size=8192):
f.write(chunk)
print(f"Video successfully downloaded to '{output_path}'")
return True
except Exception as e:
print(f"Error downloading video: {e}")
return False
Script completo en Python: texto a vídeo con Seedance 2
Uniendo todo, este es el script completo para generar un vídeo a partir de un prompt de texto usando Seedance 2:
import os
from byteplussdkarkruntime import Ark
from dotenv import load_dotenv
import utils
load_dotenv()
API_KEY = os.getenv("ARK_API_KEY")
client = Ark(
api_key=os.getenv("ARK_API_KEY"),
base_url="https://ark.ap-southeast.bytepluses.com/api/v3"
)
prompt = """
A cinematic close-up of a weary but hopeful female astronaut inside a dimly lit, dusty spaceship cabin.
Soft blue light illuminates her face.
She takes a deep breath, showing subtle emotional relief, looks directly into the camera, and says:
'We finally made it. The atmosphere is stable.'
The ambient sound of a low, rhythmic spaceship engine hums in the background.
"""
response = client.content_generation.tasks.create(
model="dreamina-seedance-2-0-260128",
content=[
{
"type": "text",
"text": prompt,
}
],
generate_audio=True,
ratio="16:9",
duration=8,
watermark=True,
resolution="480p",
)
task_id = response.id
print(f"Task successfully submitted! Task ID: {task_id}")
# Wait for the video to finish generating
video_url = utils.poll_task(client, task_id)
utils.download_video(video_url, "./videos/space.mp4")
Referencia de parámetros clave de la API de Seedance 2
A continuación, tienes la lista de valores aceptados para los parámetros clave de una solicitud de generación de vídeo:

Guía avanzada: capacidades de imagen a vídeo en Seedance 2.0
En la sección anterior vimos cómo generar un vídeo a partir de un prompt de texto. Seedance 2 también admite entradas de imagen, vídeo y audio, así que empecemos con imágenes.
Usar imágenes de referencia para mantener la coherencia de los personajes
Para probar entradas de imagen, generé dos personajes con IA para usarlos en una escena tipo sitcom.

Podemos aportar referencias de imagen al modelo añadiéndolas al campo content de la solicitud, así:
response = client.content_generation.tasks.create(
model="dreamina-seedance-2-0-260128",
content=[
{
"type": "text",
"text": prompt,
},
{
"type": "image_url",
"image_url": {
"url": utils.load_image("./images/male-character.png")
},
"role": "reference_image"
},
{
"type": "image_url",
"image_url": {
"url": utils.load_image("./images/female-character.png")
},
"role": "reference_image"
}
],
generate_audio=True,
ratio="16:9",
duration=15,
watermark=True,
resolution="480p",
)
Para este vídeo, usé el siguiente prompt:
Context: A cozy, warmly lit independent coffee shop counter with wooden accents.
Reference:
- The male barista's face and clothing follow @Image1.
- The female customer's face and styling follow @Image2.
Action & Dialogue (10 Seconds Total):
0:00 - 0:04:
Medium close-up on the male barista (@Image1) standing behind the counter,
next to the coffee machine. He leans across the counter with a smooth,
flirtatious smirk, acting like he is making a highly exclusive offer:
"The WiFi password? It's actually my phone number: five-five-five, zero-one—"
0:04 - 0:07:
Camera cuts to the female customer (@Image2), instantly interrupting him.
She holds up her phone screen toward him, entirely unimpressed:
"Your network is called 'Free_Guest_WiFi' and there is literally no password."
0:07 - 0:08: Quick cut back to the two-shot.
Flustered, the barista becomes visibly embarrassed, his confident smirk
instantly vanishing as he nervously breaks eye contact.
0:08 - 0:10: Camera cuts back to a medium shot of the female customer (@Image2).
She lowers her phone, tapping her fingers lightly on the counter,
and delivers her order with perfect deadpan clarity:
"It will be an espresso please. No milk, no sugar, and no phone number."
Framing/Timing:
Clean cuts to perfectly pace the comedic timing over exactly 10 seconds.
Tone/Audio:
Lighthearted sitcom vibe. Synchronized voice generation for the dialogue, a subtle low cafe background hum, and the sound of the customer's fingers lightly tapping the wooden counter at the end.
Fíjate en que el prompt usa @Image1 y @Image2 para referirse a las imágenes en el orden en que se proporcionan en la lista.
Por desgracia, al intentar generar este vídeo, recibí un error que decía:
The request failed because the input image may contain a real person.
Lo observé de forma consistente al usar Seedance 2 con una imagen de referencia que contiene un humano realista, incluso si estaba generada por IA.
Para salvar este obstáculo, convertí los personajes en dibujos animados y lo intenté de nuevo.

Esta vez funcionó perfectamente y el resultado fue exactamente lo que quería crear.
El código completo de este ejemplo está en el archivo image_to_video.py.
Fijar el primer y último fotograma para una animación guiada
El ejemplo anterior mostró cómo usar imágenes como referencias estableciendo ”role”: “reference_image” en la solicitud.
Seedance 2 admite otros dos roles de imagen:
”first_frame”: Indica al modelo que use la imagen proporcionada como fotograma inicial exacto del vídeo generado. Es el ajuste estándar para generación directa de imagen a vídeo.”last_frame”: Indica al modelo que termine el vídeo generado en esa imagen concreta. Nota: Solo puedes usarlo si también proporcionas unfirst_frameen la misma solicitud, pidiendo al modelo que interpole el vídeo entre ambas.
Lo probé generando una sucesión de fotogramas iniciales de un laboratorio de robots con IA intentando recrear plantas en un mundo donde la vida natural ha desaparecido.

Para cada fotograma, generé un vídeo usando ese fotograma como primero. Así se estructura cada solicitud:
response = client.content_generation.tasks.create(
model="dreamina-seedance-2-0-260128",
content=[
{
"type": "text",
"text": prompt,
},
{
"type": "image_url",
"image_url": {
"url": utils.load_image("./images/robot-lab1.jpeg")
},
"role": "first_frame"
}
],
generate_audio=False,
ratio="16:9",
duration=4,
watermark=True,
resolution="480p",
)
Generé los vídeos sin audio porque no sería consistente, ya que cada clip se generó por separado. Aquí está el resultado al unir los cinco clips:
El script completo para generar un vídeo a partir de un fotograma inicial está aquí.
Interpolación de imágenes con last_frame
En este ejemplo, proporcionamos tanto un first_frame como un last_frame para generar un vídeo entre ambos.
Generé dos imágenes de la misma habitación: una vacía, sin muebles, y otra completamente amueblada. El objetivo es crear un vídeo que anime el proceso de amueblado.

El prompt que usé describía cómo quería que aparecieran los muebles en el vídeo:
A dynamic, fast-paced sequence where furniture pops into existence piece by piece. First, a rug rapidly unfolds itself, then the sofa drops from just above the floor with a heavy, realistic bounce. Lamps, artwork, and plants quickly spring into reality one after another. Vibrant lighting, photorealistic materials, lively and snappy energetic rhythm.
Esta es la solicitud:
response = client.content_generation.tasks.create(
model="dreamina-seedance-2-0-260128",
content=[
{
"type": "text",
"text": prompt,
},
{
"type": "image_url",
"image_url": {
"url": utils.load_image("./images/room-start.png")
},
"role": "first_frame"
},
{
"type": "image_url",
"image_url": {
"url": utils.load_image("./images/room-end.png")
},
"role": "last_frame"
}
],
generate_audio=True,
ratio="16:9",
duration=8,
watermark=True,
resolution="480p",
)
Y este es el resultado:
Puedes ver el script completo aquí.
Entradas multimodales en Seedance 2.0: añadir referencias de audio y vídeo
Como modelo multimodal, Seedance 2 va más allá de texto e imágenes. También admite entradas de vídeo y audio. Pueden usarse como referencia de estilo, para guiar el movimiento o para edición de vídeo.
Este ejemplo de su web oficial lo ilustra muy bien. Tomaron una escena de dos actores peleando y la usaron para guiar la acción, sustituyendo a los actores por imágenes de referencia:
Enviar una entrada de vídeo
Como con las imágenes, los vídeos se proporcionan en el parámetro content de la solicitud.
Sin embargo, a día de hoy las entradas de vídeo solo admiten URLs. Esto significa que, si queremos usar un vídeo local, primero tendremos que alojarlo en algún sitio.
Para este ejemplo, usé este vídeo de skate de uso libre de Marc Espejo. La idea era usar Seedance 2 para añadir algo de VFX. Este es el resultado:
Para generarlo, usé la siguiente solicitud:
response = client.content_generation.tasks.create(
model="dreamina-seedance-2-0-260128",
content=[
{
"type": "text",
"text": prompt,
},
{
"type": "video_url",
"video_url": {
"url": "https://www.pexels.com/download/video/38213009/"
},
"role": "reference_video"
}
],
generate_audio=True,
ratio="16:9",
duration=6,
watermark=True,
resolution="480p",
)
El script completo está aquí. El prompt que usé fue:
The original skater, environment, and camera movement must remain
unchanged and untouched.
Only add gaming-style visual effects: intense glowing sparks and electric arcs
streaming from the skateboard-rail contact point during the grind, and a
brilliant energy shockwaves and dust burst at the jump/pop.
Enviar una entrada de audio
Para probar entradas de audio, generé una imagen para un personaje y usé Fish Audio para generar una pista con distintas emociones.

Para el audio, quería varias emociones para comprobar si Seedance 2 era capaz de identificarlas al generar el vídeo.
Fish Audio AI permite especificar emociones usando [emotion] en el prompt, así que era perfecto para la tarea. Este fue el prompt de audio que usé:
[sighing] Nothing much is happening today. [surprised] Wait a minute... what are those on my ears?[laughing] Oh my gosh, I can't believe I got piercings last night! [angry] But hold on, I specifically told you not to let me do it!
Para cargar el archivo de audio, escribí esta función que añadí a utils.py:
def load_audio(file_path):
if not os.path.exists(file_path):
raise FileNotFoundError(f"Audio file not found at: {file_path}")
# Guess the mime type (e.g., audio/mpeg for mp3, audio/wav for wav)
mime_type, _ = mimetypes.guess_type(file_path)
if mime_type is None:
mime_type = "audio/mpeg" # Fallback to mp3 just in case
with open(file_path, "rb") as audio_file:
encoded_string = base64.b64encode(audio_file.read()).decode('utf-8')
return f"data:{mime_type};base64,{encoded_string}"
Proporcionar el audio al modelo funciona de forma muy similar a las imágenes.
Los cargamos como una URI de datos base64 y los enviamos en el parámetro contents, así:
response = client.content_generation.tasks.create(
model="dreamina-seedance-2-0-260128",
content=[
{
"type": "text",
"text": prompt,
},
{
"type": "image_url",
"image_url": {
"url": utils.load_image("./images/cat.png")
},
"role": "reference_image"
},
{
"type": "audio_url",
"audio_url": {
"url": utils.load_audio("./audio/voice.mp3")
},
"role": "reference_audio"
}
],
ratio="16:9",
duration=11,
watermark=True,
resolution="480p",
)
Este es el vídeo que generó Seedance 2:
En general, creo que el rendimiento fue bastante bueno.
Hacia el final, el personaje empezó a llorar, algo que no encaja con una emoción de enfado.
Pero ten en cuenta que Seedance 2 no recibió el prompt de audio; solo le pasamos el archivo final.
El script completo está aquí.
Idea de proyecto con Seedance 2.0: crear una herramienta de transiciones de vídeo con IA
Si quieres un proyecto entretenido para afianzar lo aprendido, te propongo escribir un script en Python que tome dos vídeos como entrada y:
- Extraiga el último fotograma del primer vídeo.
- Extraiga el primer fotograma del segundo vídeo.
- Use la interpolación de vídeo que hemos visto para generar una transición entre ambos.
- Por último, el script una los tres vídeos en uno solo.
Conclusión
En este artículo hemos visto cómo usar la API de Seedance 2 para generar vídeos a partir de texto, imágenes y audio.
Repasamos la preparación del entorno, la conexión con la API y la gestión de los diferentes medios necesarios para dar vida a tus ideas. Es un proceso directo, y la posibilidad de combinar estas entradas abre opciones creativas interesantes.
En conjunto, el modelo responde de forma fiable y produce resultados sólidos.
Sin embargo, no se siente como un avance disruptivo frente a otros modelos de generación de vídeo actuales. Cumple con su cometido, pero no cambia las reglas del juego.
Por último, aunque entiendo los motivos de las directrices de seguridad que restringen el uso de rostros humanos reales en las entradas, encontré que estas normas limitan bastante el uso práctico del modelo.
Cuando intenté usar la herramienta para mejorar mi propio contenido, rechazó sistemáticamente cualquier vídeo en el que apareciera mi cara.
Esto supone un obstáculo para quienes quieren crear medios personalizados, y es algo a tener en cuenta si planeas usarla en proyectos profesionales o personales.
Seedance 2.0: preguntas frecuentes
¿Qué es Seedance 2?
Seedance 2 es un modelo de IA generativa multimodal de ByteDance que actúa como un "director multimodal", capaz de procesar texto, imágenes, vídeo y audio simultáneamente para crear secuencias de vídeo cinematográficas.
¿Hay un plan gratuito para Seedance?
No, no hay plan gratuito; activar y usar la API de Seedance 2 requiere comprar créditos específicos del modelo a través de la consola de BytePlus Ark.
¿Puedo usar imágenes de referencia con rostros humanos realistas en mis vídeos de Seedance?
No, la API rechaza de forma consistente entradas con rostros humanos realistas, aunque sean generados por IA, y suele devolver un error. Una solución es convertir los personajes a estilo cartoon.
¿Seedance 2 es multimodal?
Sí. Seedance 2 es un modelo multimodal, lo que significa que puede procesar e integrar texto, imágenes, vídeo y audio para crear contenido con vídeo y audio sincronizados.


