SDK de Go
El SDK oficial de Go para SpicyAPI (github.com/spicyapi-ai/spicy-go) — instálalo, cotiza y lanza tu primera generación, sube un archivo local, recorre tu historial de tareas y trata todos los códigos de error.
spicy-go es el SDK oficial de Go para SpicyAPI. Pone la generación de imagen, video y audio detrás de un único *spicy.Client: adjunta tu clave de API, decodifica la envoltura de la respuesta en structs tipados, espera a una tarea con esperas crecientes acotadas, sube archivos locales en una sola llamada y le da a cada fallo un único tipo de error que puedes reconocer con errors.As.
Necesita Go 1.23 o posterior y usa solo la biblioteca estándar. Es una decisión deliberada: este paquete entra en tu grafo de dependencias, y cada módulo que arrastrase sería una cosa más que auditar.
¿Es la herramienta adecuada?
| Quieres… | Usa |
|---|---|
| Llamar a SpicyAPI desde tu propio código de Go | Este SDK |
| Llamarla desde Node.js o TypeScript | El SDK de TypeScript |
| Llamarla desde Python, PHP o Java | El SDK de Python, de PHP o de Java |
| Generar cosas desde una terminal, sin escribir código | La CLI |
| Que un asistente de IA llame a SpicyAPI por ti | El servidor MCP y el Agent Skill |
| Usar un modelo de chat o de texto | Un cliente de Go consolidado para los formatos de OpenAI, Anthropic o Gemini; consulta Texto y streaming |
Instálalo
Go resuelve los módulos directamente desde su repositorio, así que no hay ningún registro de por medio ni nada que esperar. Este comando funciona hoy mismo:
go get github.com/spicyapi-ai/spicy-goDespué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.
client, err := spicy.New(spicy.Options{}) // lee SPICY_API_KEY del entorno
if err != nil {
return err
}
request := spicy.CreateTaskInput{
Model: "MODEL_ID_FROM_CATALOG",
Input: map[string]any{"prompt": "a lantern in fog"},
}
quote, err := client.Quote(ctx, request)
if err != nil {
return err
}
log.Printf("this will cost at most %s", quote.MaxCharge)
// Guárdala junto a tu propio registro del trabajo antes de enviarlo: su razón de
// ser es seguir ahí después del fallo que se llevó por delante la respuesta.
key, err := spicy.NewIdempotencyKey()
if err != nil {
return err
}
request.Model = quote.Model
request.QuoteID = quote.QuoteID
request.ExpectedCost = quote.EstimatedCost
task, err := client.CreateTask(ctx, request, key, spicy.CreateTaskOptions{})
if err != nil {
return err
}
final, err := client.WaitForTerminal(ctx, task.TaskID, spicy.WaitOptions{})
if err != nil {
return err
}
for _, asset := range final.ReadyAssets() {
log.Println(asset.URL)
}MODEL_ID_FROM_CATALOG es un marcador de posición. Saca un ID real del catálogo autenticado —client.ListModels, 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 a partir del inputSchema del propio modelo, que devuelve ListModels; cada modelo acepta campos distintos.
Los importes son cadenas decimales en dólares estadounidenses, nunca números en coma flotante, porque un flotante binario no puede guardar con exactitud la mayoría de los importes decimales. En esta API no hay créditos ni puntos por ninguna parte.
Un estado terminal no es un estado correcto: failed, canceled y expired también terminan la espera. Comprueba final.State.
Sáltate la primera consulta
WaitSeconds le pide al servicio que mantenga la conexión abierta hasta que la tarea termine, lo que en un modelo rápido significa que el resultado llega con el propio envío.
seconds := 30
task, err := client.CreateTask(ctx, request, key, spicy.CreateTaskOptions{WaitSeconds: &seconds})
if err != nil {
return err
}
// Task es nil siempre que se agota la espera. Ese es el desenlace normal, no un
// error: la generación sigue en marcha, así que a partir de aquí toca sondear.
final := task.Task
if final == nil {
final, err = client.WaitForTerminal(ctx, task.TaskID, spicy.WaitOptions{})
if err != nil {
return err
}
}
log.Println(final.State, final.Cost)Es una optimización, nunca una garantía. El código que lee task.Task sin comprobar si es nil funciona hasta la primera generación más lenta que ese margen, y entonces provoca un panic en producción.
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. UploadFile 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 Input.
uploaded, err := client.UploadFile(ctx, "/path/to/reference.png", "")
// uploaded.URI -> "spicy://f/fil_..."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; consulta Carga y descarga de medios.
Saldo, consumo e historial
Los tres se limitan a la clave de API que hace la llamada: ni las claves hermanas de la misma cuenta ni las generaciones iniciadas en la consola web se incluyen, y esa ausencia no es un fallo.
balance, err := client.GetBalance(ctx)
if err != nil {
return err
}
log.Printf("%s available, %s held by accepted tasks", balance.Available, balance.Held)
usage, err := client.GetUsage(ctx, spicy.UsageOptions{From: "2026-09-01", To: "2026-09-08"})
if err != nil {
return err
}
log.Printf("%d calls, %s settled over [%s,%s)", usage.TotalCalls, usage.TotalSpend, usage.From, usage.To)
// EachTask repite todos los filtros en cada página y solo mueve el cursor.
if err := client.EachTask(ctx, spicy.ListTasksOptions{From: "2026-09-01", To: "2026-09-08"},
func(task spicy.TaskListItem) bool {
log.Println(task.TaskID, task.State, task.Cost)
return true // devuelve false para parar antes de tiempo
}); err != nil {
return err
}Las fechas son días naturales en UTC, dentro de un intervalo semiabierto [from, to) de 92 días como máximo. Fija From y To en cualquier recorrido que pueda cruzar la medianoche UTC. Si los dejas vacíos, el servidor recalcula su propia ventana —to es mañana en UTC— en cada página, de modo que un recorrido que empieza a las 23:59 y continúa a las 00:01 hace dos preguntas distintas y empalma las respuestas. Nada te avisa de ello.
GetUsage sirve para cuadrar cuentas, no para seguir el progreso. Está detrás de un presupuesto de solicitudes común a toda la cuenta que comparten todas las claves, y ese limitador falla en cerrado: sondearlo puede dejar a todas las demás claves sin acceso a sus propios informes. Para seguir una tarea usa WaitForTerminal. La liquidación también puede llegar tarde, así que una cifra que leas hoy todavía puede cambiar mañana.
Errores
Los fallos llegan como *spicy.APIError, al que llegas con errors.As:
var apiErr *spicy.APIError
if errors.As(err, &apiErr) {
log.Println(apiErr.Status, apiErr.Code, apiErr.RequestID, apiErr.RetryAfter)
}Ramifica según Code, nunca según el mensaje: los mensajes se traducen, los códigos no. El estado HTTP 503 lo comparten tres códigos de negocio distintos, así que el estado por sí solo no basta para decidir qué hacer:
| Code | Qué significa | Qué hacer |
|---|---|---|
40003 | los bytes subidos no coinciden con su ticket | empieza de cero con un ticket nuevo |
40004 | ningún despliegue atiende esa combinación de parámetros | cambia el parámetro que nombra el mensaje |
40201 | saldo insuficiente | añade fondos |
40901 | el precio cambió antes de que se creara la tarea | vuelve a cotizar y conserva la misma clave de idempotencia |
503 | una dependencia no está disponible durante un momento | espera lo que indique RetryAfter |
50301 | ahora mismo no hay ningún despliegue ni precio utilizable | actualiza el catálogo y no insistas a lo loco |
50302 | una generación síncrona falló aguas arriba y se reembolsó | reintenta con una clave de idempotencia nueva |
Todos los códigos están en Errores. Cita el RequestID cuando escribas a soporte.
Dos cosas que te ahorrarán dinero
Usa una sola clave de idempotencia por envío. Reutilízala en cada reenvío, también después de un tiempo de espera agotado. 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. CreateTask exige la clave justamente por eso. Consulta Idempotencia.
Una tarea que sale bien se cobra, aunque el resultado te decepcione. Quote no retiene nada, así que cotiza primero cuando el precio importe.
Lo que este paquete no hace
- Modelos de texto. Hablan los formatos de OpenAI, Anthropic y Gemini: los clientes de Go consolidados para esos formatos ya funcionan contra
https://api.spicyapi.ai/v1con la misma clave. Consulta Texto y streaming. - Aplicaciones de cliente. No metas nunca esta clave dentro de un binario que distribuyas. 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. Cancelar el
contextsolo detiene tu espera.
Código fuente
El módulo tiene licencia MIT y se desarrolla en abierto en github.com/spicyapi-ai/spicy-go. Su README es el recorrido completo más corto por el cliente; esta página añade los conceptos que lo rodean.
Siguientes pasos
Catálogo de modelos y esquemas
Cómo leer el inputSchema de un modelo y elegir entradas válidas
Tareas asíncronas
Estados de las tareas, sondeo y qué significa cada campo
Cotizaciones y compatibilidad
Consulta el precio exacto de una solicitud antes de pagarlo
Webhooks
Formato de las entregas, calendario de reintentos y verificación de la firma
Errores
Todos los códigos de error y cuáles vale la pena reintentar
SDK de Python
El mismo flujo desde Python, sin ninguna dependencia en tiempo de ejecución
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.
SDK de PHP
El SDK oficial de PHP para SpicyAPI (spicyapi/spicyapi) — instálalo con Composer, cotiza y lanza tu primera generación, sube un archivo local, verifica webhooks sin riesgos y trata todos los códigos de error.

