spicyapiDocumentación
Desarrollar con la API
Contenido principal

Catálogo de modelos y esquemas

Usa el catálogo autenticado en vivo para elegir modelos disponibles, consultar sus precios y crear solicitudes con su JSON Schema actual.

createTask.model recibe el valor exacto de model devuelto por la respuesta autenticada de GET /api/v1/models. Su input se define en el inputSchema actual de esa misma entrada. Esa respuesta en tiempo de ejecución es la única fuente válida para obtener un ID que pueda ejecutarse.

No copies un ID de una página estática

Las capturas de lanzamiento, las páginas de familia y los catálogos de los creadores sirven para descubrir modelos. Pueden incluir entradas previstas, deshabilitadas o renombradas, y nunca deben utilizarse como alternativa si el catálogo en vivo está vacío. Antes de ejecutar trabajos de pago, consulta el catálogo autenticado y exige tanto enabled: true como available: true.

En los ejemplos de esta página, MODEL_ID_FROM_CATALOG es un marcador de posición, no un modelo real: sustitúyelo por un valor de model de tu propio catálogo en vivo. Consultar el catálogo es gratis.

Seleccionar un modelo y sus entradas validadas

Los esquemas de solicitud varían entre modelos, así que el método seguro consiste en seleccionar juntos un modelo en vivo y uno de sus ejemplos validados por el servidor:

const response = await fetch(
  'https://api.spicyapi.ai/api/v1/models?includeSchema=1&includeExamples=1',
  { headers: { Authorization: `Bearer ${process.env.SPICY_API_KEY}` } },
);
const envelope = await response.json();
if (!response.ok || envelope.code !== 200) throw new Error(envelope.msg);

const selected = envelope.data.items.find(
  (item) => item.enabled && item.available && item.examples?.length,
);
if (!selected) throw new Error('No callable model with a validated example');

const createTaskBody = {
  model: selected.model,
  input: selected.examples[0].input,
};

Cada ejemplo es un input completo que el servidor ya validó con el esquema actual, por lo que puedes enviarlo sin cambios.

Consultar el catálogo desde tu código

GET /api/v1/models
GET /api/v1/models/{model}
curl "https://api.spicyapi.ai/api/v1/models?modality=video" \
  -H "Authorization: Bearer $SPICY_API_KEY"

La lista no está paginada: una respuesta contiene todos los modelos que tu clave puede ver y que coinciden con los filtros. Todos los parámetros son opcionales.

ParámetroSignificado
modalityimage, video, audio o text
providerCreador del modelo, con coincidencia exacta y distinción entre mayúsculas y minúsculas, por ejemplo ByteDance. Cópialo del provider de una entrada
taskUna tarea, por ejemplo text-to-image, edit (edición de imágenes), image-to-video, text-to-video, reference-to-video, speech-to-text, video-analyze, video-edit, video-extend, character-animation o chat. Consulta tasks en el catálogo para conocer el conjunto actual. La edición de imágenes acepta dos formas: los modelos se publican como .../edit, pero tasks los clasifica como image-to-image, y el filtro acepta cualquiera de las dos
searchTexto que aparece en cualquier parte del ID o nombre visible del modelo, sin distinguir mayúsculas y minúsculas y con un máximo de 128 caracteres
includeSchematrue o 1 añade el inputSchema de cada entrada
includeExamplestrue o 1 añade los examples de cada entrada, validados con el esquema actual

El endpoint de un solo modelo, GET /api/v1/models/{model}, siempre incluye inputSchema y examples; includeSchema e includeExamples solo afectan a la lista. Las barras del ID pueden mantenerse tal cual; codifícalas porcentualmente solo si tu cliente HTTP trata un parámetro de ruta como un único segmento. Un modelo que no exista o no se ofrezca actualmente devuelve 404.

Una respuesta de lista abreviada:

{
  "code": 200,
  "msg": "success",
  "data": {
    "total": 1,
    "items": [
      {
        "model": "MODEL_ID_FROM_CATALOG",
        "family": "…",
        "displayName": "…",
        "provider": "…",
        "modality": "video",
        "tasks": ["image-to-video"],
        "async": true,
        "enabled": true,
        "available": true,
        "availability": "available",
        "taskTimeoutSeconds": 1200,
        "maxOutputDurationSeconds": 10,
        "quantityField": "duration_seconds",
        "pricing": [
          { "variant": "generate_audio=false;resolution=720p", "unit": "per_second", "price": "0.05", "currency": "USD" },
          { "variant": "generate_audio=true;resolution=720p", "unit": "per_second", "price": "0.08", "currency": "USD" }
        ],
        "startingPrice": { "variant": "generate_audio=false;resolution=720p", "unit": "per_second", "price": "0.05", "currency": "USD" },
        "badges": ["audio_output"],
        "relatedModels": [],
        "version": "v1",
        "updatedAt": "2026-09-16T16:47:57Z"
      }
    ]
  },
  "request_id": "req_01k3m8x9q2z4v7n5p6r8s0t1w2"
}

Los precios anteriores son ilustrativos. inputSchema y examples solo aparecen en las entradas de la lista cuando los solicitas.

Campos que debe utilizar el cliente

CampoFinalidad
modelValor exacto que debes enviar como createTask.model, con el formato publisher/model/task
familyIdentificador estable de familia de producto para agrupar endpoints relacionados
displayNameNombre legible de este endpoint
providerCreador del modelo, como el laboratorio que lo desarrolló
modality / tasksMedio bajo el que se agrupa el endpoint en el catálogo y tarea que realiza. modality controla la navegación y el filtro modality; no garantiza el tipo de archivo o resultado: un endpoint speech-to-text se agrupa bajo audio y responde con texto. Consulta Forma de la salida
toolkitFamilia de herramientas a la que pertenece el endpoint; actualmente solo subject-swap. Está ausente cuando el modelo no pertenece a ninguna, así que comprueba el valor y no solo la existencia de la clave. Como badges, es una clasificación que nunca afecta a la aceptación, el precio o la validación. Consulta Cambio de sujeto
asynctrue: crea una tarea y recoge el resultado mediante consultas de estado o webhook. false: la respuesta se devuelve en la misma solicitud
inputSchemaJSON Schema 2020-12 utilizado para generar formularios y validar solicitudes
examplesEntradas validadas con el esquema actual; en la lista, solo se devuelven si las solicitas
enabledSi la entrada puede ejecutarse ahora. El catálogo también incluye entradas publicadas pero aún no habilitadas y otras pausadas por la plataforma; estas devuelven false. Compruébalo junto con available antes de enviar una tarea
availableSi el modelo puede ejecutarse ahora. false indica que una llamada fallaría, por ejemplo porque no tiene precio actual
availabilityFase anunciada: planned, available, preview (los campos y resultados aún pueden cambiar) o maintenance (fallo temporal del que se espera recuperación)
pricing / startingPriceTus precios; consulta Cómo leer los precios
quantityFieldCampo de entrada cuyo valor es la cantidad facturable, normalmente duration_seconds para video. Si está vacío, cada llamada cuenta como una unidad. Léelo siempre del catálogo
taskTimeoutSecondsPlazo máximo de ejecución de una tarea en la plataforma. No es la duración del medio
maxOutputDurationSecondsDuración máxima de salida que puede producir el modelo, tomada del maximum de su propiedad duration_seconds; se omite cuando no se declara un máximo
badgesEtiquetas de capacidad y licencia, como commercial_use o no_watermark. Ignora los valores que no reconozcas
relatedModelsAlternativas o siguientes pasos seleccionados, expresados como ID de modelos que puedes ejecutar
version / updatedAtSeguimiento de cambios: compáralos para saber si una consulta posterior contiene modificaciones

mature y policyTier son etiquetas informativas. No intervienen en la aceptación de una solicitud.

Cómo leer los precios

Cada entrada de pricing es un nivel de precio:

CampoSignificado
unitper_image, per_second (de medios de salida), per_request o per_1k_tokens
priceTu precio por unidad en dólares estadounidenses, como cadena decimal. Ya refleja las tarifas de tu cuenta, que pueden variar entre cuentas
variantAjustes que seleccionan el nivel. Está vacío cuando el modelo tiene un solo precio; es el valor solo (por ejemplo, 720p) cuando un campo selecciona el nivel; son pares field=value unidos con ; y ordenados por nombre de campo (por ejemplo, generate_audio=false;resolution=480p) cuando intervienen varios
currencySiempre USD
regularPrice, offerLabel, offerPercent, offerEndsAtSolo aparecen mientras se aplica una oferta para el cliente: precio anterior a la oferta, nombre, porcentaje y fecha de finalización

startingPrice es la entrada más económica, no el precio de tus ajustes. Los campos que seleccionan un nivel aparecen en inputSchema["x-pricing"].variantFields cuando existe; de lo contrario, son los marcados con x-ui.affects_price: true (salvo quantityField). Un campo omitido utiliza su default del esquema.

  • Modelos de medios: el costo es price × quantity, donde la cantidad es el valor de quantityField en tu entrada, o una unidad si quantityField está vacío. Algunos endpoints añaden un cargo separado por cada entrada adicional, normalmente por cada imagen de referencia. Es una parte real de la factura, no un detalle de redondeo: en un endpoint de edición que acepta un array de imágenes de referencia, llenar ese array puede multiplicar el importe, mientras que unit, quantity, variant y el price del nivel permanecen exactamente iguales que para una sola imagen. Por eso, el cálculo anterior subestima el costo sin una señal visible. La señal legible por máquinas es un campo marcado con x-ui.affects_price: true que no es quantityField ni aparece en x-pricing.variantFields: modifica el importe sin seleccionar un nivel. En esos endpoints, interpreta pricing como un mínimo y obtén el importe de una cotización.
  • Modelos de texto (per_1k_tokens): un solo número no describe el cargo. price es únicamente la tarifa de tokens de salida por cada 1,000 tokens. Los tokens de entrada y de entrada en caché tienen sus propias tarifas, normalmente menores, y se facturan según el uso medido de cada llamada. No multipliques price por el total de tokens. Los precios por millón de tokens de entrada, entrada en caché y salida aparecen en la página de cada modelo y en price.perMillionTokens del catálogo público.

Para conocer el importe de una solicitud exacta en la API nativa antes de pagar, utiliza POST /api/v1/jobs/quote. Los cargos definitivos aparecen en el registro de la tarea (cost con settled: true) y en Facturación.

Caché del catálogo

  • La respuesta del catálogo puede guardarse en caché durante un máximo de 60 segundos (Cache-Control: private, max-age=60). Utiliza únicamente una caché privada por cuenta: los precios son específicos de tu cuenta, así que nunca la pongas detrás de una caché CDN compartida o pública.
  • Aparte de eso, no vuelvas a consultarlo antes de cada tarea. Las consultas del catálogo utilizan la capacidad de lectura de la cuenta, de forma independiente del envío de tareas. Consultar el catálogo antes de cada llamada sigue desperdiciando capacidad de lectura; consulta Límites de solicitudes.
  • Actualízalo periódicamente (por ejemplo, cada pocos minutos), después de un error de validación 400 y cuando se rechace el envío de una tarea para ese modelo.
  • Conserva la version y el updatedAt del registro utilizado para saber si una consulta posterior contiene cambios.
  • Nunca distribuyas una copia del esquema fijada en el código ni recurras a una página estática cuando el catálogo en vivo no tenga una entrada que pueda ejecutarse.

Reglas compartidas del esquema y Playground

inputSchema utiliza JSON Schema 2020-12. Las palabras clave estándar controlan la validación del servidor; el objeto x-ui de cada propiedad solo describe el orden, la presentación y las indicaciones para renovar cotizaciones en Playground. Los clientes deben ignorar las extensiones de presentación desconocidas, que nunca sustituyen required, las enumeraciones, los límites ni las ramas condicionales. Consulta el contrato completo de Spicy Schema.

Crear tu propia tabla de parámetros por modelo

Este sitio no incluye una tabla de parámetros por modelo. No es por falta de trabajo: los modelos y campos cambian cada semana, cualquier copia estática se desactualiza y leer un parámetro antiguo puede causar un 400 o un precio incorrecto. Los parámetros tienen una sola fuente: el inputSchema devuelto por el catálogo. Playground muestra ese mismo documento y el SDK valida con él.

Cómo representar cada parte del esquema en una tabla

En el esquemaEn tu tabla de parámetros
Una clave de propertiesNombre del parámetro. Los campos se unifican entre modelos: un significado y un nombre en todas partes
Inclusión en requiredObligatorio u opcional
anyOf / allOfObligatorio bajo condiciones. anyOf significa proporcionar al menos uno de estos campos; allOf con if → then significa que, si proporcionas A, debes proporcionar B
typeTipo
descriptionTexto de ayuda. Está escrito en inglés y puede mostrarse tal cual; no necesitas escribir uno propio
enumValores aceptados. Si existe x-ui.enum_labels, úsalo para convertir los valores en etiquetas legibles
minimum / maximumRango aceptado; x-ui.step es el paso y x-ui.unit el sufijo (s, px)
minItems / maxItemsCantidad de entradas que admite un array. Omitir esto impide que el lector conozca el límite que encontrará
itemsForma de una entrada del array. Si items.type es object, ese subesquema es un esquema completo de objeto: lee su required, su additionalProperties y el type, description, rango y default de cada subcampo, y represéntalos como una tabla anidada en lugar de un cuadro de texto libre
defaultValor predeterminado aplicado si omites el campo
x-ui.orderOrden de mayor a menor. No ordenes según las claves de properties: el orden de las claves de un objeto JSON no forma parte del contrato
x-ui.primaryFijar arriba como entrada principal
x-ui.advancedPuede plegarse en una sección avanzada
x-ui.affects_priceMarcar que afecta al precio; cambiar el valor justifica una cotización nueva
x-ui.widgetControl que debe mostrarse si construyes un formulario editable (trece valores; consulta Spicy Schema)

Las ramas condicionales solo aparecen en la raíz de inputSchema y tienen dos formas: anyOf, donde cada rama enumera únicamente campos required («proporciona al menos uno de estos»), y allOf, donde cada rama es if → then («si proporcionas A, debes proporcionar B»). No las deduzcas de la familia ni esperes oneOf, not o dependentSchemas. Los endpoints nuevos usan los campos canónicos de Spicy Schema; los antiguos siguen su esquema en vivo.

Cómo obtenerlo

const modelId = 'MODEL_ID_FROM_CATALOG'; // the exact `model` value from GET /api/v1/models
const res = await fetch(`https://api.spicyapi.ai/api/v1/models/${modelId}`, {
  headers: { Authorization: `Bearer ${process.env.SPICY_API_KEY}` },
});
const { data } = await res.json();
const schema = data.inputSchema;

const rows = Object.entries(schema.properties)
  .sort(([, a], [, b]) => (b['x-ui']?.order ?? 0) - (a['x-ui']?.order ?? 0))
  .map(([name, prop]) => ({
    name,
    required: (schema.required ?? []).includes(name),
    type: prop.type,
    description: prop.description,
    values: prop.enum
      ? prop.enum.map((v) => prop['x-ui']?.enum_labels?.[v] ?? v)
      : prop.minimum !== undefined || prop.maximum !== undefined
        ? [`${prop.minimum ?? ''}–${prop.maximum ?? ''}${prop['x-ui']?.unit ?? ''}`]
        : [],
    default: prop.default,
    affectsPrice: prop['x-ui']?.affects_price === true,
    advanced: prop['x-ui']?.advanced === true,
  }));

Este ejemplo se limita deliberadamente a valores escalares: deja values vacío en los arrays porque estos indican lo que aceptan en items, minItems y maxItems, no en enum ni en un rango. Amplíalo antes de publicarlo: una fila que muestra un parámetro como un simple array, sin forma de sus entradas ni límite de longitud, no da al lector información suficiente para utilizarlo.

El registro de un solo modelo también devuelve examples: cada uno es una entrada completa ya validada por el servidor con el esquema actual, así que sirve como ejemplo mínimo ejecutable en tus documentos y como cuerpo de solicitud que puedes enviar sin cambios. Es más seguro que componer uno a mano: un ejemplo manual se desactualiza cuando cambia el esquema, mientras que examples se mantiene validado.

El mismo código sirve para cualquier modelo cambiando modelId y no pierde validez cuando añadimos modelos nuevos. Para generar una tabla de todo el catálogo, recorre items de GET /api/v1/models?includeSchema=1 y guárdala en caché como se explica en Caché del catálogo.

Ayuda más detallada que description

description es una frase breve, pensada para un formulario. Cuando un parámetro necesita más explicación, el catálogo público incluye una segunda nota por parámetro:

curl "https://api.spicyapi.ai/console/v1/catalog/models/MODEL_ID_FROM_CATALOG?locale=en"

El registro de un solo modelo devuelve paramNotes, un objeto cuyas claves son los nombres de los parámetros, escrito en cualquiera de los diez idiomas de documentación mediante locale. Allí se explican cuestiones que no caben en una etiqueta: contra qué se mide realmente un valor, qué ajuste hace notablemente más lenta una llamada y qué entradas acepta el esquema pero pueden producir un resultado decepcionante. Si el parámetro apunta a un archivo alojado por ti, normalmente es el único lugar que indica si lo descargamos, cuándo lo hacemos y qué ocurre si no puede utilizarse.

Hay dos límites: paramNotes pertenece al catálogo público, que no requiere una clave, y no al GET /api/v1/models autenticado, por lo que debes consultarlo por separado y guardarlo en caché junto al esquema. Además, es texto explicativo, nunca una regla: inputSchema sigue siendo lo único que determina si una solicitud se acepta.

En esta página