spicyapiDocumentación
Guías
Contenido principal

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:

FormaElígela siNecesitas
CLIQuieres escribir comandos sin programar nadaNode.js 22.13 o posterior
TypeScript SDKEstás creando un programa de Node.js y quieres ver las llamadas que haceNode.js 22.13 o posterior
HTTP (curl)Quieres ver la API tal cual, por ejemplo para usar después otro lenguaje de programaciónUna 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:

  1. 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.
  2. Una cotización (paso 3) te da el precio exacto de tu solicitud exacta. No inicia nada ni aparta dinero.
  3. La confirmación: la CLI vuelve a mostrar el precio y espera a que escribas y antes 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 settled y cost para 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 archivo

Una 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-video

Copia 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 .jpg no 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 --version

Si 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 balance

Deberí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 --json

Deberí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 --json

La 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:

CampoQué te dice
modelEl ID de modelo que usarás en todos los pasos siguientes
enabled, availableUsa un modelo solo cuando los dos estén en true
pricingUna 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
quantityFieldEl ajuste que decide cuántas unidades pagas, normalmente duration_seconds. Si está vacío, se cobra una unidad por video
maxOutputDurationSecondsLa duración máxima de video que permite el modelo
taskTimeoutSecondsCuá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:

  • required enumera los ajustes que tienes que enviar. En este tipo de modelo, image_url siempre está entre ellos, y la mayoría exige también prompt.
  • properties enumera todos los ajustes que acepta el modelo, cada uno con un type, una description en inglés sencillo y, a menudo, enum (los únicos valores permitidos), minimum y maximum, y default (el valor que se usa cuando omites el ajuste).
  • Dentro de cada ajuste, x-ui contiene indicaciones de presentación. x-ui.affects_price: true significa 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.
  • examples contiene 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:

AjusteQué hace¿Cambia el precio?
image_urlLa imagen de partida, que se usa como primer fotograma. Acepta la dirección spicy:// del paso 2 o un enlace público https://No
promptLo que debe ocurrir: el movimiento, la cámara, el ambiente. Describe el movimiento en lugar de repetir lo que ya muestra la imagenNormalmente no
duration_secondsDuració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 ajusteSí en la mayoría de los modelos: el video suele cobrarse por segundo, así que un clip más largo cuesta más
resolutionTamaño del resultado, como 720p o 1080pNormalmente: las resoluciones más altas suelen tener su propio precio, más alto
aspect_ratioLa forma del video, como 16:9, 9:16 o 1:1. Algunos modelos ofrecen adaptive, que sigue la forma de la imagenMira x-ui.affects_price
generate_audioGenera además una banda sonora que encaja con la imagenEn algunos modelos
last_image_urlUna imagen para el último fotograma; consulta Primer y último fotogramaCotiza para comprobarlo
negative_promptLo que el video debe evitarNormalmente no
seedUn número que facilita comparar los resultados de varias ejecuciones; no garantiza videos idénticosNo

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.jpg

Deberías ver:

fileId: fil_01k3m8x9q2z4v7n5p6r8s0t1w3
status: ready
bytes: 402118
contentType: image/jpeg
sha256: a91f4c2e5b7d16809e3f2a4c8d0b61e7c4a890e5d2f71b3c609ae8427d15f0c6
uri: spicy://f/fil_01k3m8x9q2z4v7n5p6r8s0t1w3
expiresAt: 2026-09-18T09:12:11Z

Copia 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:

VesQué significaQué hacer
Unsupported file type, o la CLI o el SDK dicen que no saben de qué tipo esEl archivo no es JPEG, PNG, WebP ni GIFConvierte la imagen o ponle la extensión correcta
File exceeds the 10 MB limitLa imagen ocupa más de 10 MiBExporta una versión más pequeña
The uploaded file does not match the upload ticketEl 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 .jpgExpórtala como JPEG o PNG de verdad y vuelve a subirla
The upload ticket has expiredPasaron más de unos 20 minutos entre el ticket y la confirmaciónVuelve 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:

input.json
{
  "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"
}
EOF

Usa 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.json

Deberí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:00Z

Qué 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.json

La 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:00Z

Copia 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; reutiliza idempotency-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 --json

Usa 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_01k3m8x9q2z4v7n5p6r8s0t1w3
key: tasks/2026/09/17/job_01k3m8x9q2z4v7n5p6r8s0t1w3/result.mp4
url: https://…
expiresAt: 2026-09-17T08:52:00Z

Pega 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ódigoQué significaQué hacer
400, o un código más específico que empieza por 400La 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álidaCompara input.json con los ajustes del paso 1 y corrígelo. Si la dirección ha caducado, vuelve a subir la imagen
401Falta la clave de API, está mal escrita o se ha revocadoComprueba SPICY_API_KEY como se explica en Antes de empezar
40201Tu saldo no cubre el importe que hay que apartarAñade saldo en Facturación, dentro de la consola
40202Se 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, 40301Esta clave no puede usar este modelo, o la solicitud llegó desde un lugar desde el que no está permitidaRevisa los modelos permitidos y las IP permitidas de la clave en la página Claves de API
404No hay ningún modelo con ese ID para tu cuenta (o enviaste el marcador), o no hay ninguna tarea con ese ID para esta claveVuelve a copiar el ID exacto; usa la misma clave que creó la tarea
409Esta clave de idempotencia ya se usó con una solicitud distintaUsa una etiqueta nueva para cada video nuevo
40901La cotización caducó (dura cinco minutos) o el precio cambióVuelve a cotizar y a iniciar la tarea
429Demasiadas solicitudes en poco tiempoEspera los segundos que indica el error y continúa más despacio
50301El modelo no puede aceptar trabajo nuevo en este momentoVuelve 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_01k3m8x9q2z4v7n5p6r8s0t1w3

Mira state, errorCode y errorMessage.

Después decide qué hacer según el errorCode, no según cómo esté redactado el mensaje:

errorCodeQué significaQué hacer
rate_limited, upstream_unavailable, upstream_failedLa generación estaba saturada o no estaba disponible temporalmenteVuelve a ejecutarla tras una breve pausa
timeout (o el estado es expired)El video no se terminó a tiempoInté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_failedLa generación se ejecutó, pero no produjo nadaLa misma entrada suele fallar de la misma manera. Cambia el prompt o la imagen e inicia una tarea nueva
invalid_assetNo se pudo usar la imagen, por ejemplo porque la subida caducó o porque no se pudo leer el archivoVuelve a subir la imagen e inicia una tarea nueva con la nueva dirección
invalid_request, unsupported_combinationNo se aceptaron los ajustesCambia los ajustes e inicia una tarea nueva
content_rejectedEl modelo elegido rechazó esta solicitud según su propia políticaCambia 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 1800000

Te 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:

  1. spicyapi_upload_file sube la imagen y obtiene su dirección spicy://. 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.
  2. spicyapi_models_list y spicyapi_model_get buscan un modelo y sus ajustes, y spicyapi_task_quote pone precio a las opciones. Todo gratis.
  3. spicyapi_task_create se 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.
  4. spicyapi_task_wait espera 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 su expiresAt, 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-1 o campaign-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 40202 las 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 (localhost se 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/spicy

La CLI incluye la dirección en su propia cotización, así que la comprobación del precio sigue coincidiendo.

Siguientes pasos

En esta página