spicyapiDocumentación
SDK
Contenido principal

SDK de Python

El SDK oficial de Python para SpicyAPI (spicyapi) — instálalo, cotiza y lanza tu primera generación, sube un archivo local, verifica webhooks y consulta todos los códigos de error.

spicyapi es el SDK oficial de Python para SpicyAPI. Pone la generación de imagen, video y audio detrás de un único objeto cliente: adjunta tu clave de API, convierte la envoltura de la respuesta en diccionarios normales, espera a que una tarea termine con esperas crecientes sensatas, sube archivos locales en una sola llamada y verifica las firmas de los webhooks en tiempo constante.

Necesita Python 3.11 o posterior y no tiene ninguna dependencia en tiempo de ejecución. Es una decisión deliberada: este paquete entra en un árbol de dependencias que ya es tuyo, y cada restricción que añade es una oportunidad más de conflicto.

¿Es la herramienta adecuada?

Quieres…Usa
Llamar a SpicyAPI desde tu propio código de PythonEste SDK
Llamarla desde Node.js o TypeScriptEl SDK de TypeScript
Llamarla desde Go, PHP o JavaEl SDK de Go, de PHP o de Java
Generar cosas desde una terminal, sin escribir códigoLa CLI
Que un asistente de IA llame a SpicyAPI por tiEl servidor MCP y el Agent Skill
Usar un modelo de chat o de textoLa biblioteca oficial openai, anthropic o google-genai apuntando a SpicyAPI; consulta Texto y streaming

Instálalo

pip install spicyapi

Después guarda tu clave en el entorno, para que no aparezca nunca ni en tu código ni en el archivo de historial de tu terminal:

export SPICY_API_KEY="sk-spicy-..."

Tu primer resultado

Pide el precio, decide, crea la tarea, espera y lee los archivos. Cotizar es gratis y no retiene nada.

import os, uuid
from spicyapi import SpicyClient, output_assets

client = SpicyClient()  # lee SPICY_API_KEY del entorno

model = client.get_model("MODEL_ID_FROM_CATALOG")
quote = client.quote_task(model=model["model"], input_data={"prompt": "a lantern in fog"})
print(quote["estimatedCost"], quote["maxCharge"])   # decide antes de gastar

task = client.create_task(
    model=model["model"],
    input_data={"prompt": "a lantern in fog"},
    idempotency_key=str(uuid.uuid4()),
    quote_id=quote["quoteId"],
    expected_cost=quote["estimatedCost"],
)
final = client.wait_for_terminal(task["taskId"])
for asset in output_assets(final):                  # función del módulo, no un método
    print(asset["url"])

MODEL_ID_FROM_CATALOG es un marcador de posición. Saca un ID real del catálogo autenticado —client.list_models(), que es GET /api/v1/models— y nunca de memoria ni de una página antigua: los modelos se añaden, se pausan y se retiran. Construye input_data a partir del inputSchema del propio modelo, que devuelve list_models(include_schema=True); cada modelo acepta campos distintos.

Los importes llegan como cadenas decimales en dólares estadounidenses, nunca como números en coma flotante, porque un flotante binario no puede guardar con exactitud la mayoría de los importes decimales. Compáralos y súmalos con decimal.Decimal. En esta API no hay créditos ni puntos por ninguna parte.

wait_for_terminal devuelve el control en cualquier estado terminal. Un estado terminal no es un estado correcto: comprueba final["state"] antes de leer la salida.

Algunos endpoints responden con texto en lugar de con archivos. Lee primero output_text(final) cuando el modelo produzca una transcripción o una descripción; un cliente que solo recorre la lista de archivos da por vacía una tarea que ha salido perfectamente.

Empieza desde un archivo local

La generación de imagen a video, la sustitución de personas y la edición de imágenes necesitan que tu material esté antes de nuestro lado. upload_file hace en una sola llamada la subida en tres pasos —ticket, PUT al almacenamiento y confirmación— y devuelve un URI spicy://. Ese URI es lo que va en la entrada.

uploaded = client.upload_file("/path/to/reference.png")
task = client.create_task(
    model="MODEL_ID_FROM_CATALOG",
    input_data={"image": uploaded["uri"], "prompt": "slow dolly in"},
    idempotency_key=str(uuid.uuid4()),
)

Una URL HTTPS accesible públicamente puede ir directamente en la entrada y no necesita ninguna subida. Se aceptan imágenes de hasta 10 MiB, y audio y video de hasta 90 MiB; lo demás está en Carga y descarga de medios.

Webhooks

verify_webhook es una función del módulo, así que el manejador de una solicitud puede usarla sin construir un cliente. Compara en tiempo constante y solo revisa la marca de tiempo después de que la firma sea válida.

from spicyapi import verify_webhook

delivery = verify_webhook(
    raw_body=request.body,                      # los bytes exactos, antes de analizarlos
    signature=request.headers["X-Webhook-Signature"],
    timestamp=request.headers["X-Webhook-Timestamp"],
    payload_version=request.headers["X-Webhook-Payload-Version"],
    secret=os.environ["SPICY_WEBHOOK_SECRET"],
)

Verifica los bytes en bruto. Un json.loads seguido de un json.dumps los cambia, y la firma ya no volverá a coincidir jamás. Las entregas se reintentan, así que deduplica por el ID de entrega. El formato completo de las entregas y el calendario de reintentos están en Webhooks.

Errores

Cada fallo lanza SpicyApiError, que lleva status, code, request_id y retry_after_seconds. Ramifica según code, nunca según el texto del mensaje: los mensajes se traducen, los códigos no.

El estado HTTP 503 lo comparten tres códigos de negocio distintos, así que leer solo el estado HTTP no basta para decidir qué hacer:

codeQué significaQué hacer
40003los bytes subidos no coinciden con su ticketempieza de cero con un ticket nuevo
40004ningún despliegue atiende esa combinación de parámetroscambia el parámetro que nombra el mensaje
40201saldo insuficienteañade fondos
40901el precio cambió antes de que se creara la tareavuelve a cotizar y conserva la misma clave de idempotencia
503una dependencia no está disponible durante un momentoespera lo que indique Retry-After
50301ahora mismo el modelo no tiene ningún despliegue ni precio utilizableactualiza el catálogo y no insistas a lo loco
50302una generación síncrona falló aguas arriba y se reembolsóreintenta con una clave de idempotencia nueva

err.recovery devuelve esa misma orientación en tiempo de ejecución. Cita el request_id cuando escribas a soporte. Todos los códigos están en Errores.

Dos cosas que te ahorrarán dinero

Usa una sola clave de idempotencia por envío. Reutilízala en cada reenvío de ese envío, también después de un tiempo de espera agotado o de una conexión cortada. Que se pierda una respuesta no demuestra que la tarea no se haya creado: una clave nueva convierte un resultado desconocido en una segunda tarea de pago. Consulta Idempotencia.

Una tarea que sale bien se cobra, aunque el resultado te decepcione. Cotiza primero cuando el precio importe; quote_task no retiene nada y no crea nada.

Lo que este paquete no hace

  • Modelos de texto. Hablan los formatos de OpenAI, Anthropic y Gemini, así que las bibliotecas oficiales openai, anthropic y google-genai ya funcionan: apúntalas a https://api.spicyapi.ai/v1 con la misma clave. Consulta Texto y streaming.
  • Aplicaciones de navegador, móviles y de escritorio. No metas nunca esta clave dentro de una aplicación: una clave compilada en un cliente es una clave pública. Llama desde tu servidor.
  • Elegir un modelo o rellenar sus campos. Te entrega el catálogo y el esquema de cada modelo; tú decides qué enviar.
  • Cancelar una tarea. Cuando una tarea se acepta, se ejecuta hasta el final. Detener tu programa solo detiene tu espera.

Código fuente

El paquete tiene licencia MIT y se desarrolla en abierto en github.com/spicyapi-ai/spicy-python. Su README es el recorrido completo más corto por el cliente; esta página añade los conceptos que lo rodean.

Siguientes pasos

En esta página