Tutorial de imagen a video
Empieza con una imagen de tu equipo y termina con un archivo de video descargado. Cada paso muestra cómo hacerlo con la CLI, con el SDK de TypeScript y con HTTP directo.
Este tutorial toma una imagen de tu equipo y la convierte en un clip de video corto que descargas y conservas. Recorre todo el camino en orden: elegir un modelo, subir la imagen, comprobar el precio, iniciar la generación, esperar a que termine, descargar el resultado y resolver lo que salga mal. No tienes que ir juntando las piezas a partir de las páginas de referencia.
Cada paso muestra tres formas de hacer lo mismo. Elige una y úsala de principio a fin:
| Forma | Elígela si | Necesitas |
|---|---|---|
| CLI | Quieres escribir comandos sin programar nada | Node.js 22.13 o posterior |
| TypeScript SDK | Estás creando un programa de Node.js y quieres ver las llamadas que hace | Node.js 22.13 o posterior |
| HTTP (curl) | Quieres ver la API tal cual, por ejemplo para usar después otro lenguaje de programación | Una terminal bash con curl y jq (macOS, Linux o WSL en Windows) |
¿Prefieres pedírselo sin más a un asistente de IA? Consulta Hazlo desde un asistente de IA.
Qué vas a crear
- El resultado: un archivo de video corto, normalmente MP4, que parte de tu imagen y se mueve tal como describe tu frase.
- Tu tiempo: calcula unos 20 minutos la primera vez, casi todo de lectura. La generación en sí suele tardar unos minutos; algunos modelos y ajustes tardan más.
- Dónde se ejecuta: en SpicyAPI. Tu equipo solo envía la imagen y las instrucciones, y después descarga el archivo terminado.
Cuánto cuesta y cómo saberlo antes de pagar
Los precios están en dólares estadounidenses. Dependen del modelo que elijas y de tus ajustes, como la duración y la resolución del video, así que esta página no da ninguna cifra. Puedes ver el precio en tres sitios, y ninguno cuesta nada:
- El catálogo en vivo (paso 1) enumera todos los modelos que tu cuenta puede usar, con los precios de tu cuenta. Los videos suelen cobrarse
per_second, es decir, por segundo de resultado. Cuando un modelo muestra varios precios, cada uno corresponde a un ajuste, como una resolución. - Una cotización (paso 3) te da el precio exacto de tu solicitud exacta. No inicia nada ni aparta dinero.
- La confirmación: la CLI vuelve a mostrar el precio y espera a que escribas
yantes de iniciar nada.
Qué pasa con tu dinero:
- Solo el paso 4 cuesta dinero, igual que volver a ejecutar una tarea fallida. Listar modelos, subir, cotizar, esperar y descargar son gratis.
- Cuando empieza una generación, el importe cotizado se aparta de tu saldo (una retención). Ese importe es también lo máximo que puede costar el video.
- Si el video sale bien, se te cobra lo que se produjo, nunca más de lo retenido, y lo que sobre vuelve a tu saldo.
- Las tareas fallidas o vencidas normalmente liberan toda la retención. Algunos rechazos de contenido se cobran al precio aceptado, solo si la página del modelo indica expresamente esta excepción. Consulta
settledycostpara conocer el importe final. - Las cuentas nuevas empiezan sin ninguna cantidad gratuita, así que primero necesitas saldo. Consulta Facturación.
Cómo funciona
tu imagen ──subida──▶ dirección spicy:// (válida durante un día)
│
tu frase + ajustes ───────┴──▶ cotización (gratis) ──▶ iniciar la tarea (dinero apartado)
│
queued ──▶ running ──▶ succeeded / failed / expired
│
enlace de descarga (unos 20 minutos) ──▶ tu archivoUna tarea es un trabajo de generación. SpicyAPI trabaja en ella en segundo plano, así que iniciarla y recoger el resultado son dos acciones separadas. Cada tarea tiene un ID que empieza por job_; guárdalo, porque es la forma de volver a encontrar la tarea. Las demás palabras que aparecen en esta página se explican en el Glosario.
Antes de empezar
Ten una cuenta, saldo y una clave de API
Sigue primero Configura tu cuenta. Cuando termines, deberías tener:
- una cuenta de SpicyAPI con dinero en el saldo;
- una clave de API, que empieza por
sk-spicy-; - esa clave guardada en una variable de entorno llamada
SPICY_API_KEY. Una variable de entorno es un valor con nombre que tu terminal entrega a los programas que ejecuta, así que la clave nunca tiene que aparecer en un comando ni en tu código.
Comprueba que la clave está configurada en la ventana de terminal que estás usando, sin mostrar la clave completa:
echo "${SPICY_API_KEY:0:9}"Deberías ver sk-spicy-. Si no ves nada o aparece un error, la clave no está configurada en esta ventana; vuelve a Configura tu cuenta.
Pon tu imagen en una carpeta de trabajo
Crea una carpeta para este tutorial y abre la terminal en ella. Todos los comandos de esta página se ejecutan desde esa carpeta, y todos los archivos que crean acaban ahí.
mkdir -p ~/spicy-video
cd ~/spicy-videoCopia tu imagen en esa carpeta y llámala start-frame.jpg (o start-frame.png si es un PNG; en ese caso, usa ese nombre en todos los sitios donde esta página dice start-frame.jpg).
Qué hace que una imagen de partida funcione bien:
- Formato y tamaño: JPEG, PNG, WebP o GIF, hasta 10 MiB (unos 10 megabytes). Un modelo puede fijar sus propios límites, más estrictos, que verás en el paso 1.
- El archivo tiene que estar de verdad en ese formato. SpicyAPI comprueba el contenido del archivo, no solo su nombre. Las fotos hechas con el teléfono suelen estar en HEIC, y cambiarles la extensión a
.jpgno las convierte. Abre la foto y expórtala o guárdala antes como JPEG. - Forma: la imagen se convierte en el primer fotograma. Usa una imagen horizontal para un video horizontal y una vertical para un video vertical; algunos modelos ofrecen además un ajuste
aspect_ratio. - Derechos: usa una imagen que tengas permiso para usar.
Prepara la herramienta que elegiste
La CLI es un programa llamado spicyapi que se ejecuta en la terminal. Instálala una sola vez (instrucciones completas, Node.js incluido: CLI: antes de empezar):
npm install -g @spicyapi/cli
spicyapi --versionSi la instalación falla con EACCES, sáltatela y escribe npx @spicyapi/cli en todos los sitios donde esta página escribe spicyapi.
Después comprueba que tu clave funciona y que tienes saldo:
spicyapi balanceDeberías ver available, held y total en dólares estadounidenses. available tiene que ser mayor que cero.
Los siete pasos
Sustituye MODEL_ID_FROM_CATALOG por un ID de modelo real
MODEL_ID_FROM_CATALOG es un marcador, no un modelo. En el paso 1 eliges un ID de modelo real del catálogo en vivo (spicyapi models list, listModels() en el SDK o GET /api/v1/models). Pégalo en todos los sitios donde esta página muestra el marcador. Si envías el marcador tal cual, solo recibirás un error de «no encontrado».
¿Usas Windows? Elige la pestaña CLI o la del SDK
La pestaña HTTP está escrita para bash. Windows PowerShell trata de otra manera las comillas, los saltos de línea (\) y $(...), así que esos comandos no funcionarán allí tal como están escritos. En Windows, usa la pestaña CLI o la del SDK, o ejecuta los comandos HTTP dentro de WSL (Subsistema de Windows para Linux).
1. Elige un modelo y lee sus ajustes
Qué hacer: lista los modelos de imagen a video que tu cuenta puede usar, elige uno y lee qué ajustes acepta.
Lista los modelos:
spicyapi models list --modality video --task image-to-video --jsonDeberías ver una lista en JSON. Esta está abreviada, y los importes aparecen como …:
{
"total": 1,
"items": [
{
"model": "MODEL_ID_FROM_CATALOG",
"displayName": "Example video model",
"modality": "video",
"tasks": ["image-to-video"],
"enabled": true,
"available": true,
"quantityField": "duration_seconds",
"maxOutputDurationSeconds": 10,
"taskTimeoutSeconds": 1800,
"pricing": [
{ "variant": "720p", "unit": "per_second", "price": "…", "currency": "USD" },
{ "variant": "1080p", "unit": "per_second", "price": "…", "currency": "USD" }
]
}
]
}Elige un modelo que tenga enabled y available en true y copia su valor model. Después lee sus ajustes:
spicyapi models get MODEL_ID_FROM_CATALOG --jsonLa respuesta trae los mismos campos y, además, inputSchema (los ajustes) y examples (entradas que el servidor ya ha comprobado).
Qué mirar en la lista:
| Campo | Qué te dice |
|---|---|
model | El ID de modelo que usarás en todos los pasos siguientes |
enabled, available | Usa un modelo solo cuando los dos estén en true |
pricing | Una entrada por cada nivel de precio: variant (el ajuste al que se aplica, a menudo una resolución), unit (en video, normalmente per_second) y price en dólares estadounidenses. regularPrice y offerLabel aparecen cuando hay un descuento |
quantityField | El ajuste que decide cuántas unidades pagas, normalmente duration_seconds. Si está vacío, se cobra una unidad por video |
maxOutputDurationSeconds | La duración máxima de video que permite el modelo |
taskTimeoutSeconds | Cuánto tiempo puede durar una generación antes de detenerse como expired (sin que se cobre) |
Cómo leer los ajustes. inputSchema es la lista de ajustes del modelo, escrita en JSON Schema:
requiredenumera los ajustes que tienes que enviar. En este tipo de modelo,image_urlsiempre está entre ellos, y la mayoría exige tambiénprompt.propertiesenumera todos los ajustes que acepta el modelo, cada uno con untype, unadescriptionen inglés sencillo y, a menudo,enum(los únicos valores permitidos),minimumymaximum, ydefault(el valor que se usa cuando omites el ajuste).- Dentro de cada ajuste,
x-uicontiene indicaciones de presentación.x-ui.affects_price: truesignifica que cambiar ese ajuste cambia el precio. - Algunos modelos añaden condiciones en el nivel superior, como «si envías A, también tienes que enviar B». Spicy Schema explica cómo leerlas.
examplescontiene entradas completas que el servidor ya ha comprobado, y son un punto de partida seguro.
Los ajustes con los que más te vas a encontrar. Los nombres son iguales en todos los modelos, pero cada modelo acepta solo algunos, con sus propios valores permitidos:
| Ajuste | Qué hace | ¿Cambia el precio? |
|---|---|---|
image_url | La imagen de partida, que se usa como primer fotograma. Acepta la dirección spicy:// del paso 2 o un enlace público https:// | No |
prompt | Lo que debe ocurrir: el movimiento, la cámara, el ambiente. Describe el movimiento en lugar de repetir lo que ya muestra la imagen | Normalmente no |
duration_seconds | Duración del video en segundos. Los valores permitidos están en enum o entre minimum y maximum; algunos modelos aceptan un valor especial que se explica en la description del ajuste | Sí en la mayoría de los modelos: el video suele cobrarse por segundo, así que un clip más largo cuesta más |
resolution | Tamaño del resultado, como 720p o 1080p | Normalmente: las resoluciones más altas suelen tener su propio precio, más alto |
aspect_ratio | La forma del video, como 16:9, 9:16 o 1:1. Algunos modelos ofrecen adaptive, que sigue la forma de la imagen | Mira x-ui.affects_price |
generate_audio | Genera además una banda sonora que encaja con la imagen | En algunos modelos |
last_image_url | Una imagen para el último fotograma; consulta Primer y último fotograma | Cotiza para comprobarlo |
negative_prompt | Lo que el video debe evitar | Normalmente no |
seed | Un número que facilita comparar los resultados de varias ejecuciones; no garantiza videos idénticos | No |
Envía solo los ajustes que enumera este modelo
Cada modelo tiene su propia lista. Un ajuste que no está en las properties de ese modelo, un nombre mal escrito o un valor fuera de enum hacen que se rechace la solicitud entera con 400. No se cobra nada, pero no copies la entrada de otro modelo sin comprobarla.
Qué acaba de pasar: has leído el catálogo en vivo de tu cuenta, con tus precios. No se ha cobrado nada. Leer el catálogo cuenta para el límite de solicitudes de tu cuenta, así que no hace falta repetirlo antes de cada video; vuelve a hacerlo cuando cambies de modelo o recibas un 400.
2. Sube tu imagen
Qué hacer: sube start-frame.jpg y copia la dirección spicy:// que recibes. Subir es gratis.
spicyapi files upload start-frame.jpgDeberías ver:
fileId: fil_01k3m8x9q2z4v7n5p6r8s0t1w3
status: ready
bytes: 402118
contentType: image/jpeg
sha256: a91f4c2e5b7d16809e3f2a4c8d0b61e7c4a890e5d2f71b3c609ae8427d15f0c6
uri: spicy://f/fil_01k3m8x9q2z4v7n5p6r8s0t1w3
expiresAt: 2026-09-18T09:12:11ZCopia el valor uri exactamente como aparece.
Qué acaba de pasar: tu imagen se ha guardado de forma privada para tu cuenta, se ha comprobado (su formato real tiene que coincidir con su tipo) y ha recibido una dirección spicy://. Esa dirección no es un enlace web: solo tu cuenta puede usarla, y solo dentro de la entrada de una tarea. Se puede usar durante un día (mira expiresAt); pasado ese plazo, vuelve a subir la imagen.
¿La imagen ya está en internet?
Si la imagen ya está en una dirección pública https:// que se abre sin iniciar sesión, puedes saltarte este paso y poner esa dirección en image_url. Tiene que seguir accesible mientras se genera el video.
Si la subida falla:
| Ves | Qué significa | Qué hacer |
|---|---|---|
Unsupported file type, o la CLI o el SDK dicen que no saben de qué tipo es | El archivo no es JPEG, PNG, WebP ni GIF | Convierte la imagen o ponle la extensión correcta |
File exceeds the 10 MB limit | La imagen ocupa más de 10 MiB | Exporta una versión más pequeña |
The uploaded file does not match the upload ticket | El contenido del archivo no es del tipo que indican su nombre o su declaración; por ejemplo, una foto HEIC a la que se le cambió la extensión a .jpg | Expórtala como JPEG o PNG de verdad y vuelve a subirla |
The upload ticket has expired | Pasaron más de unos 20 minutos entre el ticket y la confirmación | Vuelve a empezar la subida |
3. Describe el video y comprueba el precio
Qué hacer: escribe tus ajustes en un archivo llamado input.json y después pide una cotización.
Escribe input.json. Empieza con los dos ajustes que acepta cualquier modelo de imagen a video. Pon tu propia frase en prompt y sustituye el valor de image_url por el uri que copiaste en el paso 2:
{
"prompt": "The camera slowly pushes in while rain runs down the window and neon light flickers",
"image_url": "spicy://f/fil_01k3m8x9q2z4v7n5p6r8s0t1w3"
}Los ajustes que omitas usan el default del modelo. Si quieres elegirlos tú, añade solo ajustes que aparezcan en las properties de tu modelo, con valores que ese modelo permita. Por ejemplo, si tu modelo enumera duration_seconds y resolution y permite estos valores:
{
"prompt": "The camera slowly pushes in while rain runs down the window and neon light flickers",
"image_url": "spicy://f/fil_01k3m8x9q2z4v7n5p6r8s0t1w3",
"duration_seconds": 5,
"resolution": "720p"
}Guarda el archivo en tu carpeta de trabajo:
Pega todas las líneas de una vez, después de poner en ellas tu propio texto y tu uri:
cat > input.json <<'EOF'
{
"prompt": "The camera slowly pushes in while rain runs down the window and neon light flickers",
"image_url": "spicy://f/fil_01k3m8x9q2z4v7n5p6r8s0t1w3"
}
EOFUsa comillas rectas normales ("), no comillas tipográficas, y no pongas coma después del último ajuste.
Pide la cotización:
spicyapi tasks quote --model MODEL_ID_FROM_CATALOG --input-file input.jsonDeberías ver algo así (los importes aparecen como …):
quoteId: eyJ2IjoxLCJtIjoi…
model: MODEL_ID_FROM_CATALOG
estimatedCost: …
maxCharge: …
currency: USD
quantity: 5
unit: per_second
expiresAt: 2026-09-17T08:05:00ZQué acaba de pasar: SpicyAPI ha comprobado tu entrada contra los ajustes del modelo y ha puesto precio a esta solicitud exacta. No se ha iniciado nada ni se ha cobrado nada. estimatedCost es el importe que se apartará si inicias la tarea ahora, y maxCharge es lo máximo que puede costar. quantity y unit muestran qué pagas, por ejemplo 5 segundos.
Una cotización es válida durante cinco minutos y solo para esta entrada exacta. Cambia la duración o la resolución y vuelve a cotizar para comparar precios. La cotización hace las mismas comprobaciones que iniciar la tarea, así que los problemas aparecen aquí sin costarte nada: un 400 significa que la entrada no coincide con los ajustes del modelo (corrige input.json con lo que viste en el paso 1), y un 40201, que tu saldo no alcanzaría. El paso 7 enumera los demás códigos.
¿Quieres hacer una prueba sin tu propia imagen?
Los examples del modelo que viste en el paso 1 son entradas completas que el servidor ya ha comprobado. Puedes poner uno tal cual en input.json, cotizarlo y generarlo para ver funcionar todo el flujo antes de usar tu propia imagen.
4. Inicia la generación
Este paso cuesta dinero
Al iniciar la generación, el importe cotizado se aparta de tu saldo. Cuando SpicyAPI la acepta, la tarea no se puede cancelar: se ejecuta hasta el final y se cobra si sale bien. Las tareas fallidas o vencidas normalmente liberan toda la retención. Algunos rechazos de contenido se cobran al precio aceptado, solo si la página del modelo indica expresamente esta excepción. Consulta settled y cost para conocer el importe final.
La clave de idempotencia, en pocas palabras. Es una etiqueta para un video concreto, como un número de pedido. Si se te corta la conexión y no sabes si el video llegó a iniciarse, vuelve a enviar la misma solicitud con la misma etiqueta: SpicyAPI te devuelve la tarea que ya había iniciado en lugar de iniciar, y cobrarte, una segunda. Una etiqueta sirve durante 24 horas con la misma clave de API. Usa una etiqueta nueva para cada video distinto: la misma etiqueta con una entrada diferente se rechaza con 409. Más detalles en Idempotencia.
Qué hacer:
spicyapi tasks create --model MODEL_ID_FROM_CATALOG --input-file input.jsonLa CLI vuelve a cotizar, muestra el precio y espera tu respuesta:
Estimated charge: USD …; maximum charge: USD …. Quote expires: 2026-09-17T08:05:00Z.
Idempotency-Key: 4f6755a5-0d72-4420-9f6b-9faf0e2b0241
Create a billable task with model MODEL_ID_FROM_CATALOG? … Continue? [y/N]Escribe y y pulsa Enter. Cualquier otra respuesta cancela, y no se cobra nada. Copia el valor de Idempotency-Key en algún sitio antes de responder: la CLI crea una etiqueta nueva cada vez que se ejecuta, y necesitarás esta si se corta la conexión.
Deberías ver:
idempotencyKey: 4f6755a5-0d72-4420-9f6b-9faf0e2b0241
taskId: job_01k3m8x9q2z4v7n5p6r8s0t1w3
state: queued
estimatedCost: …
deadlineAt: 2026-09-17T08:35:00ZCopia el taskId.
Qué acaba de pasar: SpicyAPI ha aceptado la generación, que ahora es una tarea, y ha apartado estimatedCost de tu saldo. Eso es también lo máximo que puede costar este video. state: queued significa que está esperando su turno. deadlineAt es la hora límite para que termine; si para entonces no ha terminado, acaba como expired y no se cobra nada. deadlineAt no es la fecha en que caducan tus archivos.
Pasar la cotización significa «acéptala solo al precio que me mostraron». Si mientras tanto han cambiado el precio o la entrada, la solicitud se rechaza con 40901 en lugar de apartar un importe distinto. La CLI lo hace por ti.
Si se cortó la conexión y nunca llegaste a ver un ID de tarea, no vuelvas a empezar sin más con una etiqueta nueva: eso crearía un segundo video y te lo cobraría. Repite la solicitud con la misma etiqueta:
- CLI:
spicyapi tasks create --model MODEL_ID_FROM_CATALOG --input-file input.json --idempotency-key <the value you copied>. Consulta Retomar una tarea después de que se interrumpiera la espera. - SDK: vuelve a ejecutar
node generate.mjs --yes; reutilizaidempotency-key.txt. - HTTP: vuelve a ejecutar el bloque «Iniciar el video»; reutiliza
idempotency-key.txt.
Si el paso 4 se rechazó, no se ha creado nada ni se ha cobrado nada; el motivo y la solución están en el paso 7.
5. Espera a que el video esté listo
Qué hacer: consulta la tarea hasta que termine. Los videos suelen tardar unos minutos.
spicyapi tasks wait job_01k3m8x9q2z4v7n5p6r8s0t1w3 --wait-timeout-ms 1800000 --jsonUsa tu propio ID de tarea. --wait-timeout-ms 1800000 espera hasta 30 minutos en lugar de los 10 predeterminados.
Deberías ver, cuando termine (abreviado):
{
"taskId": "job_01k3m8x9q2z4v7n5p6r8s0t1w3",
"model": "MODEL_ID_FROM_CATALOG",
"state": "succeeded",
"output": {
"assets": [
{
"key": "tasks/2026/09/17/job_01k3m8x9q2z4v7n5p6r8s0t1w3/result.mp4",
"url": "https://…",
"expiresAt": "2026-09-17T08:40:00Z",
"mime": "video/mp4",
"durationSeconds": 5,
"width": 1280,
"height": 720,
"bytes": 4839211
}
]
},
"cost": "…",
"settled": true
}La próxima vez puedes juntar los pasos 4 y 5 añadiendo --wait a tasks create.
Qué acaba de pasar: tu herramienta ha preguntado a SpicyAPI por la tarea cada pocos segundos. Los estados son queued (en espera), running (generándose) y, al final, un estado definitivo: succeeded, failed o expired. Cuando sale bien, el archivo se copia al almacenamiento propio de SpicyAPI, lo que tarda unos segundos más; mientras tanto, el resultado muestra pending: true. cost es definitivo cuando settled está en true.
- Cerrar la terminal o pulsar Ctrl + C no cancela la tarea. Solo detiene la espera. Guarda el ID de la tarea y vuelve a hacer este paso más tarde.
- No consultes más a menudo que cada pocos segundos. Consultar una tarea utiliza la capacidad de lectura de la cuenta, de forma independiente del inicio de tareas; consulta Límites de solicitudes.
- En una aplicación o un servidor, deja que SpicyAPI te avise cuando la tarea termine en lugar de preguntar: consulta Recibe un aviso en lugar de esperar.
6. Descarga y guarda el video
Qué hacer: consigue un enlace de descarga nuevo y guarda el archivo en tu equipo.
Consigue un enlace nuevo:
spicyapi files download-url job_01k3m8x9q2z4v7n5p6r8s0t1w3key: tasks/2026/09/17/job_01k3m8x9q2z4v7n5p6r8s0t1w3/result.mp4
url: https://…
expiresAt: 2026-09-17T08:52:00ZPega la url en un navegador para descargar el video, o guárdalo desde la terminal (deja las comillas, porque el enlace contiene caracteres &):
curl -L -o my-video.mp4 "PASTE_THE_URL_HERE"En Windows PowerShell, escribe curl.exe en lugar de curl. Si una tarea ha producido varios archivos, spicyapi tasks get <task-id> --json los enumera, y --key elige uno.
Qué acaba de pasar: cada resultado trae un enlace de descarga directo. El enlace funciona durante unos 20 minutos y no necesita clave de API; no le añadas nunca tu clave. Cuando caduque, vuelve a pedir la tarea (o usa files download-url) para obtener uno nuevo.
Guarda tu propia copia
SpicyAPI conserva los archivos generados unos 14 días como máximo, menos si el ajuste de conservación de tu cuenta es más corto, y después los borra para siempre. Tu imagen subida se puede usar durante un día, y el texto de tu prompt se borra a los 30 días de forma predeterminada. El registro de la tarea y de su costo se conserva. Para borrar antes los archivos y el prompt de una tarea terminada, usa spicyapi tasks purge, purgeTask() o POST /api/v1/jobs/purge; eso solo borra contenido y no supone ningún reembolso. Consulta Conservación y destrucción.
7. Cuando algo sale mal
Los problemas son de dos tipos, y cada uno se resuelve de una manera distinta.
Se rechaza una solicitud. Tu comando o tu llamada recibe un error al instante. No se ha creado nada ni se ha cobrado nada. El mensaje explica por qué, y el error lleva un code y un ID de solicitud (req_…) que puedes indicar si contactas con soporte.
| Código | Qué significa | Qué hacer |
|---|---|---|
400, o un código más específico que empieza por 400 | La entrada no coincide con los ajustes del modelo: falta un ajuste obligatorio, hay uno desconocido o mal escrito, un valor que no está permitido, una dirección spicy:// caducada o ajena, una combinación de ajustes que el modelo no puede atender en este momento o una dirección de callback no válida | Compara input.json con los ajustes del paso 1 y corrígelo. Si la dirección ha caducado, vuelve a subir la imagen |
401 | Falta la clave de API, está mal escrita o se ha revocado | Comprueba SPICY_API_KEY como se explica en Antes de empezar |
40201 | Tu saldo no cubre el importe que hay que apartar | Añade saldo en Facturación, dentro de la consola |
40202 | Se ha alcanzado un límite de gasto de tu clave (diario, mensual o acumulado) | Súbelo en la página Claves de API o espera a que se reinicie |
403, 40301 | Esta clave no puede usar este modelo, o la solicitud llegó desde un lugar desde el que no está permitida | Revisa los modelos permitidos y las IP permitidas de la clave en la página Claves de API |
404 | No hay ningún modelo con ese ID para tu cuenta (o enviaste el marcador), o no hay ninguna tarea con ese ID para esta clave | Vuelve a copiar el ID exacto; usa la misma clave que creó la tarea |
409 | Esta clave de idempotencia ya se usó con una solicitud distinta | Usa una etiqueta nueva para cada video nuevo |
40901 | La cotización caducó (dura cinco minutos) o el precio cambió | Vuelve a cotizar y a iniciar la tarea |
429 | Demasiadas solicitudes en poco tiempo | Espera los segundos que indica el error y continúa más despacio |
50301 | El modelo no puede aceptar trabajo nuevo en este momento | Vuelve a intentarlo más tarde o elige otro modelo |
La tarea falla. La tarea se aceptó, pero termina como failed o expired en lugar de succeeded. La tarea muestra un errorCode que indica el motivo y un errorMessage que lo explica con más detalle. Las tareas fallidas o vencidas normalmente liberan toda la retención. Algunos rechazos de contenido se cobran al precio aceptado, solo si la página del modelo indica expresamente esta excepción. Consulta settled y cost para conocer el importe final.
Consúltalo:
spicyapi tasks get job_01k3m8x9q2z4v7n5p6r8s0t1w3Mira state, errorCode y errorMessage.
Después decide qué hacer según el errorCode, no según cómo esté redactado el mensaje:
errorCode | Qué significa | Qué hacer |
|---|---|---|
rate_limited, upstream_unavailable, upstream_failed | La generación estaba saturada o no estaba disponible temporalmente | Vuelve a ejecutarla tras una breve pausa |
timeout (o el estado es expired) | El video no se terminó a tiempo | Inténtalo una vez más; si vuelve a pasar, elige una duración más corta o una resolución más baja e inicia una tarea nueva |
generation_failed | La generación se ejecutó, pero no produjo nada | La misma entrada suele fallar de la misma manera. Cambia el prompt o la imagen e inicia una tarea nueva |
invalid_asset | No se pudo usar la imagen, por ejemplo porque la subida caducó o porque no se pudo leer el archivo | Vuelve a subir la imagen e inicia una tarea nueva con la nueva dirección |
invalid_request, unsupported_combination | No se aceptaron los ajustes | Cambia los ajustes e inicia una tarea nueva |
content_rejected | El modelo elegido rechazó esta solicitud según su propia política | Cambia el prompt o la imagen, o elige otro modelo |
Cualquier otro valor debe tratarse como upstream_failed. La lista completa está en Errores.
Vuelve a ejecutar una tarea fallida. Un reintento crea una tarea nueva con el mismo modelo y la misma entrada, al precio actual del modelo. Cuesta dinero, igual que el paso 4, y conserva la entrada original: si esa entrada usaba una subida que ya ha caducado, vuelve a subir la imagen e inicia una tarea nueva en su lugar.
spicyapi tasks retry job_01k3m8x9q2z4v7n5p6r8s0t1w3 --wait --wait-timeout-ms 1800000Te pide confirmación, pero, a diferencia de tasks create, no te muestra antes el precio. Para ver el precio, ejecuta el comando tasks quote del paso 3.
Solo se pueden reintentar las tareas failed y expired. Si quieres otra versión de un video que salió bien, vuelve al paso 3 con una etiqueta nueva. Una vez aceptada, una tarea no se puede cancelar de ninguna manera. Más detalles en Ejecución y reintentos.
Hazlo desde un asistente de IA
Si usas un asistente de IA compatible con MCP, como Claude Code, Cursor o Codex, el servidor MCP de SpicyAPI le permite hacer los siete pasos por ti. Configúralo una sola vez siguiendo Servidor MCP: configura tu cliente y después pídeselo con tus propias palabras, por ejemplo:
Sube a SpicyAPI la imagen que está en /Users/me/spicy-video/start-frame.jpg. Busca los modelos de imagen a video que puede usar mi clave y cotiza un clip de 5 segundos con los dos más baratos. Después, cuando yo lo confirme, genera el video con el más barato de los dos, espera a que termine y dame el enlace de descarga.
En Windows, indica la ruta como C:\Users\me\spicy-video\start-frame.jpg.
Qué ocurre:
spicyapi_upload_filesube la imagen y obtiene su direcciónspicy://. Es gratis, pero sí envía el archivo, así que nombra exactamente el archivo que quieres subir. De forma predeterminada solo se pueden leer los archivos que están dentro de tu carpeta personal; consulta Archivos locales.spicyapi_models_listyspicyapi_model_getbuscan un modelo y sus ajustes, yspicyapi_task_quotepone precio a las opciones. Todo gratis.spicyapi_task_createse detiene y te pregunta, mostrando el modelo, el importe estimado y el máximo, y cuándo caduca la cotización. No se cobra nada hasta que confirmas, y el asistente no puede confirmar por ti.spicyapi_task_waitespera hasta cinco minutos cada vez, así que, con un video largo, puede que el asistente espere más de una vez. Después te da el enlace de descarga, que funciona durante unos 20 minutos.
Más ejemplos y todas las herramientas: Servidor MCP.
Para ir más lejos
Primer y último fotograma
Algunos modelos de imagen a video aceptan también last_image_url: una segunda imagen con la que termina el video. Para saber si el tuyo lo admite, busca last_image_url en las properties del paso 1. Si no aparece, el modelo no lo admite, y enviarlo hace que la solicitud se rechace con 400.
Sube las dos imágenes como en el paso 2 y pon las dos direcciones en input.json:
{
"prompt": "The flower slowly opens as morning light spreads across the table",
"image_url": "spicy://f/fil_01k3m8x9q2z4v7n5p6r8s0t1w3",
"last_image_url": "spicy://f/fil_01k3m8x9q2z4v7n5p6r8s0t1w4"
}La description del ajuste explica cómo usa ese modelo la imagen final. Vuelve a cotizar antes de iniciar la tarea, para ver si la segunda imagen cambia el precio.
Varios videos
- Reutiliza la subida. Una misma dirección
spicy://se puede usar en varias tareas hasta suexpiresAt, salvo que la conservación de subidas de tu cuenta esté configurada para borrarlas en cuanto termina una tarea. - Una etiqueta por video. Da a cada video su propia clave de idempotencia, como
campaign-clip-1ocampaign-clip-2. Así, si vuelves a ejecutar tu script, recuperas los videos que ya se habían iniciado en lugar de pagarlos dos veces. - No lo inicies todo a la vez. Cada tarea en marcha tiene dinero apartado, un límite de gasto rechaza con
40202las tareas que lo superen, y cada consulta de estado cuenta para tu límite de solicitudes. Empieza con dos a la vez.
Scripts ya preparados: Ejecutar un lote con concurrencia limitada (SDK) y Ejecutar la CLI desde un script.
Recibe un aviso en lugar de esperar
En un sitio web o un servicio en segundo plano, mantener un programa en marcha solo para preguntar «¿ya ha terminado?» es un desperdicio. En su lugar, dale a la tarea una dirección de callback: una dirección web pública en tu propio servidor a la que SpicyAPI llama una sola vez, cuando la tarea termina, con los mismos detalles de la tarea que muestra el paso 5.
Qué necesitas:
- un servidor accesible desde internet en una dirección
https://, en el puerto estándar (localhostse rechaza); - tu clave de firma de webhooks, que encuentras en la consola, para que tu servidor pueda comprobar que cada llamada viene de verdad de SpicyAPI;
- código que compruebe la firma, responda rápido y sepa manejar la misma notificación cuando llegue dos veces.
Webhooks explica el formato, la firma y el calendario de reintentos, y el SDK incluye un receptor mínimo. Ten un plan de respaldo: de vez en cuando, consulta las tareas que ya han superado con creces su deadlineAt sin que haya llegado ninguna notificación.
Añade la dirección de callback cuando pidas la cotización e inicies la tarea:
spicyapi tasks create --model MODEL_ID_FROM_CATALOG --input-file input.json --callback-url https://your-app.example.com/hooks/spicyLa CLI incluye la dirección en su propia cotización, así que la comprobación del precio sigue coincidiendo.
Siguientes pasos
Catálogo de modelos y esquemas
Cómo funcionan el catálogo en vivo y los ajustes de cada modelo
Carga y descarga de medios
Las subidas en detalle, incluidas las que se hacen desde un navegador
Webhooks
Recibe un aviso cuando una tarea termina y verifica cada llamada
Errores
Todos los códigos de error y cuándo sirve de algo reintentar
Facturación
Retenciones, cargos finales, límites de gasto y tu saldo
Conservación y destrucción
Cuánto tiempo se guardan los archivos y los prompts, y cómo borrarlos antes
Integración en producción
Qué añadir antes de que usuarios reales dependan de tu integración
Preguntas frecuentes
Respuestas breves a las preguntas más habituales
Migrar desde OpenAI u OpenRouter
Conecta tu código de OpenAI a SpicyAPI. Cambios de URL base, clave e ID de modelo, campos con otro comportamiento y ajustes que debes quitar de OpenRouter.
Guía: generar y editar imágenes
Cómo se comportan los endpoints de imagen como familia — las dos formas de petición, las tres maneras de escribir el tamaño de salida, lo que una imagen de referencia añade a la factura, los pesos de adaptación y cómo leer un rechazo.

