SDK Go
L’SDK Go ufficiale di SpicyAPI (github.com/spicyapi-ai/spicy-go) — installalo, chiedi un preventivo, esegui la tua prima generazione, carica un file locale, scorri lo storico delle attività e gestisci ogni codice di errore.
spicy-go è l’SDK Go ufficiale di SpicyAPI. Mette la generazione di immagini, video e audio dietro un unico *spicy.Client: allega la tua chiave API, decodifica la struttura della risposta in struct tipizzate, attende un’attività con un backoff limitato, carica i file locali con una sola chiamata e dà a ogni fallimento un unico tipo di errore che puoi intercettare con errors.As.
Richiede Go 1.23 o successivo e usa soltanto la libreria standard. È una scelta deliberata: questo pacchetto entra nel tuo grafo delle dipendenze, e ogni modulo che si porta dietro è una cosa in più da controllare.
È lo strumento giusto?
| Vuoi… | Usa |
|---|---|
| Chiamare SpicyAPI dal tuo codice Go | Questo SDK |
| Chiamarlo da Node.js o TypeScript | L’SDK TypeScript |
| Chiamarlo da Python, PHP o Java | L’SDK Python, PHP o Java |
| Generare contenuti da un terminale, senza scrivere codice | La CLI |
| Far chiamare SpicyAPI al posto tuo da un assistente IA | Il server MCP e l’Agent Skill |
| Usare un modello di chat o di testo | Un client Go affermato per i formati OpenAI, Anthropic o Gemini: vedi Testo e streaming |
Installalo
Go risolve i moduli direttamente dal loro repository, quindi non c’è alcun passaggio da un registro e non c’è nulla da aspettare. Questo comando funziona già oggi:
go get github.com/spicyapi-ai/spicy-goPoi metti la chiave nell’ambiente, così non comparirà mai nel tuo codice né nel file della cronologia della shell:
export SPICY_API_KEY="sk-spicy-..."Il tuo primo risultato
Chiedi il prezzo, decidi, crea l’attività, aspetta, leggi i file. Il preventivo è gratuito e non accantona nulla.
client, err := spicy.New(spicy.Options{}) // legge SPICY_API_KEY dall'ambiente
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)
// Salvala accanto al tuo record del lavoro prima di inviare la richiesta: il suo unico
// scopo e' essere ancora li' dopo il crash che ha fatto perdere la risposta.
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 è un segnaposto. Prendi un ID reale dal catalogo autenticato — client.ListModels, cioè GET /api/v1/models — e mai a memoria o da una pagina vecchia: i modelli vengono aggiunti, sospesi e ritirati. Costruisci Input a partire dall’inputSchema di quel modello, che ListModels restituisce; ogni modello accetta campi diversi.
Gli importi sono stringhe decimali in dollari USA, mai numeri a virgola mobile, perché i float binari non riescono a contenere esattamente la maggior parte degli importi decimali. In questa API non esistono crediti né punti.
Uno stato terminale non è uno stato riuscito: failed, canceled ed expired terminano tutti l’attesa. Controlla final.State.
Salta il primo polling
WaitSeconds chiede al servizio di tenere aperta la connessione finché l’attività non è finita: su un modello veloce, questo significa che il risultato arriva insieme all’invio.
seconds := 30
task, err := client.CreateTask(ctx, request, key, spicy.CreateTaskOptions{WaitSeconds: &seconds})
if err != nil {
return err
}
// Task e' nil ogni volta che l'attesa si esaurisce. E' l'esito normale, non un
// errore: la generazione e' ancora in corso, quindi da qui passa al polling.
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)È un’ottimizzazione, mai una garanzia. Il codice che legge task.Task senza il controllo su nil funziona fino alla prima generazione più lenta del budget, e poi va in panic in produzione.
Parti da un file locale
Da immagine a video, sostituzione del soggetto e modifica delle immagini hanno tutte bisogno che il tuo materiale arrivi prima dalla nostra parte. UploadFile esegue il caricamento in tre passaggi — ticket, PUT allo storage, commit — con una sola chiamata e restituisce un URI spicy://. È quell’URI che va in Input.
uploaded, err := client.UploadFile(ctx, "/path/to/reference.png", "")
// uploaded.URI -> "spicy://f/fil_..."Un URL HTTPS raggiungibile pubblicamente può entrare direttamente nell’input e non richiede alcun caricamento. Le immagini sono accettate fino a 10 MiB, audio e video fino a 90 MiB; vedi Media.
Saldo, consumi e storico
Tutti e tre riguardano la chiave API che chiama: le altre chiavi dello stesso account, e le generazioni avviate nella console web, non sono incluse, e la loro assenza non è un difetto.
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 ripete ogni filtro su ogni pagina e sposta soltanto il cursore.
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 // restituisci false per fermarti prima
}); err != nil {
return err
}Le date sono giorni di calendario UTC in un intervallo semiaperto [from, to) di al massimo 92 giorni. Fissa From e To in qualsiasi scorrimento che possa attraversare la mezzanotte UTC. Se li lasci vuoti, il server ricalcola la propria finestra a ogni pagina — to è il domani in UTC — quindi uno scorrimento che parte alle 23:59 e prosegue alle 00:01 pone due domande diverse e ne incolla insieme le risposte. Nessuno te lo segnala.
GetUsage serve alla riconciliazione, non a seguire l’avanzamento. Sta dietro a un budget valido per tutto l’account e condiviso da ogni chiave, e quel limitatore si chiude in caso di dubbio: interrogarlo di continuo può escludere ogni altra chiave dai propri dati. Segui invece un’attività con WaitForTerminal. Anche la liquidazione può arrivare in ritardo, quindi un dato letto oggi può ancora cambiare domani.
Errori
I fallimenti tornano come *spicy.APIError, raggiungibile con errors.As:
var apiErr *spicy.APIError
if errors.As(err, &apiErr) {
log.Println(apiErr.Status, apiErr.Code, apiErr.RequestID, apiErr.RetryAfter)
}Ramifica su Code, mai sul messaggio: i messaggi vengono tradotti, i codici no. L’HTTP 503 è condiviso da tre codici applicativi diversi, quindi il solo stato non basta per decidere che cosa fare:
| Codice | Che cosa significa | Che cosa fare |
|---|---|---|
40003 | i byte caricati non corrispondono al loro ticket | ricomincia da un ticket nuovo |
40004 | nessuna istanza disponibile serve quella combinazione di parametri | cambia il parametro indicato nel messaggio |
40201 | saldo insufficiente | aggiungi fondi |
40901 | il prezzo è cambiato prima che l’attività venisse creata | chiedi di nuovo il preventivo, mantenendo la stessa chiave di idempotenza |
503 | una dipendenza è momentaneamente non disponibile | aspetta il tempo indicato da RetryAfter |
50301 | al momento non c’è né un’istanza utilizzabile né un prezzo | aggiorna il catalogo, senza martellare |
50302 | una generazione sincrona è fallita a monte ed è stata rimborsata | riprova con una nuova chiave di idempotenza |
Tutti i codici sono elencati in Errori. Cita il RequestID quando contatti l’assistenza.
Due cose che ti faranno risparmiare
Tieni una sola chiave di idempotenza per ogni invio. Riusala a ogni reinvio, anche dopo un timeout. Una risposta persa non dimostra che l’attività non sia stata creata: una chiave nuova trasforma un esito sconosciuto in una seconda attività a pagamento. CreateTask richiede la chiave proprio per questo motivo. Vedi Idempotenza.
Un’attività che riesce viene addebitata, anche se il risultato delude. Quote non accantona nulla, quindi chiedi prima un preventivo quando il prezzo conta.
Che cosa questo pacchetto non fa
- I modelli di testo. Parlano i formati OpenAI, Anthropic e Gemini: i client Go affermati per quei formati funzionano già su
https://api.spicyapi.ai/v1con la stessa chiave. Vedi Testo e streaming. - Le applicazioni lato client. Non spedire mai questa chiave dentro un binario che distribuisci. Chiama dal tuo server.
- Scegliere un modello o compilarne i campi. Ti consegna il catalogo e lo schema di ogni modello; che cosa inviare lo decidi tu.
- Annullare un’attività. Una volta accettata, un’attività arriva fino in fondo. Annullare il
contextinterrompe soltanto la tua attesa.
Codice sorgente
Il modulo ha licenza MIT ed è sviluppato in modo aperto su github.com/spicyapi-ai/spicy-go. Il suo README è il giro più breve e completo del client; questa pagina aggiunge i concetti che ci stanno intorno.
Prossimi passi
Catalogo dei modelli e schemi
Come leggere l’inputSchema di un modello e scegliere input validi
Attività asincrone
Gli stati delle attività, il polling e il significato di ogni campo
Preventivi e compatibilità
Scopri il prezzo esatto di una richiesta prima di pagarlo
Webhook
Formato delle consegne, calendario dei nuovi tentativi e verifica della firma
Errori
Tutti i codici di errore e quali conviene riprovare
SDK Python
Lo stesso flusso di lavoro da Python, senza dipendenze a runtime
SDK Python
L’SDK Python ufficiale di SpicyAPI (spicyapi) — installalo, chiedi un preventivo, esegui la tua prima generazione, carica un file locale, verifica i webhook e consulta ogni codice di errore.
SDK PHP
L’SDK PHP ufficiale di SpicyAPI (spicyapi/spicyapi) — installalo con Composer, chiedi un preventivo, esegui la tua prima generazione, carica un file locale, verifica i webhook in sicurezza e gestisci ogni codice di errore.

