Errori
Tutti i codici applicativi, stati HTTP, azioni da intraprendere e tentativi che vale la pena ripetere.
Quando una chiamata non va a buon fine, la risposta comunica due cose: un numero (code) che indica il tipo di problema e una frase (msg) da leggere. Questa pagina elenca tutti i numeri, il loro significato e quando riprovare può aiutare. Se cerchi soltanto una soluzione rapida a un sintomo, comincia da Risoluzione dei problemi.
Struttura della risposta
Le risposte JSON dell'API nativa delle attività usano questa struttura esterna: gli stessi quattro campi intorno a ogni risposta. Interpreta le risposte compatibili /v1 e SSE secondo i rispettivi protocolli:
{ "code": 200, "msg": "success", "data": { }, "request_id": "req_…" }codeè un codice applicativo, non uno stato HTTP.200significa successo.- In caso di errore,
dataè assente (nonnull): ricevicode,msgerequest_id. L'unica eccezione: alcuni errori della libreria di asset aggiungonodata.reason. msgè per le persone: la formulazione può cambiare, così come la lingua (vedi sotto). Non basare mai la logica su questo testo; basala sucode.request_idviene restituito anche nell'headerX-Request-Id. Citalo quando segnali un problema.
Anche lo stato HTTP è impostato correttamente. Basa la logica sull'uno o sull'altro, ma soltanto su uno: i due differiscono per i codici specifici (un saldo insufficiente è code: 40201 con HTTP 402) e mescolarli prima o poi fa perdere un caso.
Consigliamo di basare la logica su code. 404 può dire soltanto «non trovato»; 40201 dice che il saldo disponibile è insufficiente.
Non tutte le risposte sono JSON
Una risposta non interpretabile come JSON, per esempio una pagina HTML o di testo della rete perimetrale, non ha code. Gestiscila in base allo stato HTTP, registra l'inizio del corpo e non riprovare alla cieca un 403: potrebbe essere un blocco regionale che nessun tentativo può cambiare.
Lingua dei messaggi di errore
Il testo leggibile degli errori può arrivare nella lingua che scegli. La lingua predefinita è l'inglese. Soltanto questi tre elementi seguono la scelta:
msgnella struttura esterna della risposta;errorMessagedi un'attività fallita, sia inrecordInfosia nei callback webhook (con un'eccezione, sotto);error.messagenegli oggetti di errore degli endpoint compatibili, nel formato OpenAI, Anthropic o Google Gemini (/v1beta).
Codici di lingua supportati: en, ja, ko, de, fr, es, pt-BR, ru, it, pl.
L'eccezione: quando il modello stesso fornisce un motivo specifico del fallimento, per esempio un'immagine di riferimento illeggibile o una restrizione sul copyright dell'audio generato, errorMessage riporta quel motivo in inglese, rimuovendo dettagli interni come nomi di servizi, host, URL e ID di richiesta, così puoi vedere cosa sia realmente accaduto. Motivi relativi all'account o alla fatturazione non vengono mai riportati; ricevi invece un nostro messaggio. Content-Language continua a descrivere msg ed errorCode resta sempre il valore su cui basare la logica.
Scegliere la lingua
Vince il primo di questi criteri applicabile:
- L'header di richiesta
Accept-Language, soltanto per quella richiesta. Si applicano i pesi HTTP standard: scegliamo la lingua riconosciuta con il valoreqpiù alto. Le varianti regionali vengono ricondotte alla lingua:de-DEvale comedeept-PTcomept-BR. Il cinese non è offerto, quindi ogni tag cinese non trova corrispondenza e viene provato il candidato successivo.*significa nessuna preferenza. - L'impostazione del tuo account, sotto Impostazioni → Lingua degli errori API nella console. Copre ogni richiesta effettuata con una delle chiavi API dell'account priva di un
Accept-Languagericonoscibile. La maggior parte degli SDK lato server e curl non invia questo header di default, quindi è l'opzione su cui si basa la maggior parte delle integrazioni. I callback webhook non hanno un header di richiesta su cui basarsi, quindi seguono sempre l'impostazione dell'account. - Altrimenti, inglese.
Le risposte di errore includono un header Content-Language che indica la lingua in cui è stato effettivamente scritto il messaggio.
Cambia soltanto il testo leggibile dalle persone
code, errorCode, type e code compatibili con OpenAI, code e status compatibili con Gemini e ogni nome di campo restano gli stessi in ogni lingua. Basa la logica sui codici e non analizzare mai la formulazione; registra code e request_id nei log e negli avvisi.
Esempio
Lo stesso 40201 (saldo insufficiente) viene restituito in inglese per impostazione predefinita:
HTTP/1.1 402 Payment Required
Content-Language: en
{ "code": 40201, "msg": "Insufficient balance", "request_id": "req_…" }Aggiungi un header Accept-Language: ja; il resto è la stessa chiamata di creazione dell'attività dell'avvio rapido:
curl -X POST https://api.spicyapi.ai/api/v1/jobs/createTask \
-H "Authorization: Bearer $SPICY_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $SPICY_IDEMPOTENCY_KEY" \
-H "Accept-Language: ja" \
--data "$TASK_PAYLOAD"msg ora è in giapponese; code è ancora 40201:
HTTP/1.1 402 Payment Required
Content-Language: ja
{ "code": 40201, "msg": "残高が不足しています", "request_id": "req_…" }Codici generali
code | HTTP | Significato | Riprovare? |
|---|---|---|---|
200 | 200 | Successo | — |
400 | 400 | Richiesta malformata o parametri non validi | No. Correggi e reinvia |
40003 | 400 | Dimensione, tipo o contenuto del file caricato non corrispondono a quanto dichiarato nella richiesta dell'URL di caricamento | No. Richiedi un nuovo URL con contentType e dimensione corretti, carica nuovamente e conferma |
40004 | 400 | La richiesta è valida, ma questo modello non può gestire questa combinazione di impostazioni adesso | No. Cambia l'impostazione indicata in msg (per esempio risoluzione o rapporto) oppure scegli un altro modello. Consulta sotto |
401 | 401 | Autenticazione fallita; msg indica quale dei tre casi | No. Correggi l'header o la chiave |
403 | 403 | Non consentito. In una chiamata API con chiave valida, l'account è soggetto a restrizioni: vedi sotto. Un'email non verificata restituisce invece 40310 | No |
404 | 404 | Non esiste (oppure non è tuo) | No |
409 | 409 | Conflitto con lo stato attuale | A volte, vedi sotto |
40901 | 409 | Preventivo scaduto, richiesta cambiata o prezzo cambiato | Ottieni e conferma un nuovo preventivo; recupera le richieste accettate con la chiave di idempotenza originale |
413 | 413 | Il corpo della richiesta supera il limite di dimensione accettato dal server | No. Riduci il corpo oppure carica i media con il percorso dedicato |
429 | 429 | Troppe richieste | Di solito. Attendi secondo Retry-After. Un tipo di 429 non riguarda la velocità: consulta Limiti di frequenza |
500 | 500 | Qualcosa non funziona dalla nostra parte | Sì. Attendi progressivamente e riprova |
503 | 503 | Un servizio di supporto (preventivi, archiviazione di immagini inline, caricamenti multimediali) non è disponibile per breve tempo | Sì. Attendi Retry-After se presente e riprova; su createTask, con lo stesso Idempotency-Key. Se persiste senza Retry-After, segui msg (per esempio usa un URL HTTPS pubblico invece di un'immagine inline) |
503 e 50301 condividono uno stato HTTP ma sono codici applicativi diversi: 503 riguarda un servizio di supporto, 50301 il modello stesso.
Distinguere i tre 401
Tre errori molto diversi portano tutti a 401 in autenticazione e hanno soluzioni diverse. code resta 401 in ogni caso; è msg a distinguerli.
msg (inglese) | Cosa è successo davvero | Cosa cambiare |
|---|---|---|
Missing credentials: send your API key in the Authorization header as "Bearer sk-spicy-..." (negli endpoint compatibili /v1 e /v1beta: …as "Authorization: Bearer sk-spicy-...", x-api-key, or x-goog-api-key) | La richiesta è arrivata senza alcuna credenziale | Il client sta omettendo l'header. Controlla interceptor, proxy e caricamento della variabile d'ambiente nel processo in esecuzione |
API key format is not recognized: keys begin with sk-spicy- | È arrivata una credenziale, ma non è una delle nostre | Hai incollato una sessione della console o la chiave di un altro servizio. Le chiavi SpicyAPI iniziano con sk-spicy-; echo "${SPICY_API_KEY:0:9}" deve mostrare esattamente quel prefisso |
Credentials are invalid or expired | La chiave ha il nostro formato, ma non esiste, è revocata o ha superato la scadenza | Controllala nella pagina delle chiavi API; se revocata o scaduta, creane una sostitutiva. Riprovare non produrrà mai un 200 |
Il caso centrale viene deciso senza una ricerca nel database, ed è per questo che comunica qualcosa che il terzo non può: la stringa non aveva mai alcuna possibilità di funzionare. Il terzo messaggio è intenzionalmente identico per chiavi inesistenti, revocate e scadute, così la risposta non permette di scoprire quali chiavi esistano.
Qui queste stringhe sono mostrate in inglese; msg segue la selezione della lingua descritta sopra e la sua formulazione non è un contratto. Basa la logica su code e tratta il messaggio come qualcosa da leggere, mostrare o registrare, non da confrontare per prendere decisioni.
L'account non può chiamare l'API
In entrambi questi casi la chiave stessa è valida; è l'account a non poter ancora chiamare l'API.
40310 significa che l'email non è stata verificata. Il messaggio è Verify your account email to run models and call the API…:
- Apri la console. Un banner in alto indica che l'email non è verificata e permette di inviare nuovamente l'email di verifica.
- Segui il link nell'email di verifica (controlla la cartella spam).
- Riprova la stessa chiamata. Non serve una nuova chiave.
La console continua a funzionare mentre l'API è bloccata, quindi puoi ancora vedere la cronologia.
Un semplice 403 con This account cannot call the API right now significa che l'account è soggetto a restrizioni. Contatta l'assistenza con il request_id.
Saldo e limiti
code | HTTP | Significato | Cosa fare |
|---|---|---|---|
40201 | 402 | Il saldo non copre il costo stimato | Aggiungi fondi sotto Fatturazione nella console; consulta Configurazione dell'account. Controlla GET /api/v1/chat/credit |
40202 | 402 | È stato raggiunto un limite di spesa | Leggi msg per sapere quale. Limite della chiave (This key has reached its …): aumentalo nella pagina delle chiavi API oppure attendi l'azzeramento. Limite del team (Workspace … budget has been reached): chiedi a proprietario, amministratore o responsabile del budget di aumentarlo nella pagina Team. Limite della piattaforma (Platform quota for today is exhausted…): non puoi cambiarlo; riprova dopo le 00:00 UTC. I limiti giornalieri si azzerano alle 00:00 UTC, quelli mensili il primo del mese alle 00:00 UTC; un limite totale non si azzera mai |
Nessuno dei due merita nuovi tentativi: saldi e limiti non cambiano perché chiedi di nuovo. Consulta Limiti di spesa.
Permessi
code | HTTP | Significato | Cosa fare |
|---|---|---|---|
40301 | 403 | Questa chiave non può chiamare questo modello | Amplia l'elenco dei modelli consentiti della chiave nella console |
40302 | 403 | L'indirizzo sorgente non è nell'elenco degli IP consentiti della chiave | Aggiungilo oppure rimuovi la restrizione IP della chiave |
40303 | 403 | Il servizio non è offerto in questa regione | Non può essere aggirato. Consulta Dove operiamo |
40310 | 403 | L'email dell'account non è ancora verificata, quindi non può eseguire modelli | Apri il link di verifica nell'email di registrazione oppure inviane una nuova dalla console. La chiave funziona appena l'indirizzo è verificato, senza modifiche da parte tua |
Nessuno di questi errori può essere risolto riprovando.
Codici di motivo della libreria di asset
Gli errori sugli endpoint della libreria di asset (/api/v1/assets…) possono aggiungere alla solita busta un campo leggibile da macchina, data.reason:
{ "code": 400, "msg": "…", "data": { "reason": "asset_library_locked" }, "request_id": "req_…" }code, lo stato HTTP, msg e l'errorCode dell'attività sono uguali con o senza motivo, quindi la gestione esistente continua a funzionare. Il motivo compare solo sugli endpoint della libreria di asset, mai sugli altri endpoint nativi, e il livello compatibile con OpenAI e gli altri livelli di compatibilità mantengono le proprie forme di errore. L'elenco può crescere: tratta un valore sconosciuto come un motivo assente.
data.reason | HTTP | Significato | Cosa fare |
|---|---|---|---|
asset_library_locked | 400 | La libreria di asset si apre con la prima ricarica | Ricarica. Gli asset già salvati restano utilizzabili |
asset_library_full | 400 | La quota di archiviazione (elementi o byte) è piena | Elimina asset o passa a un piano superiore; GET /api/v1/assets/usage mostra il successivo |
asset_file_too_large | 400 | Il file supera il limite per file del piano | Usa un file più piccolo o passa a un piano superiore |
asset_daily_limit | 429 | È stato raggiunto il numero giornaliero di nuovi asset | Attendi le 00:00 UTC e rispetta Retry-After |
asset_storage_full | 503 | L'archiviazione dell'intera piattaforma è piena | Riprova più tardi |
asset_permission_denied | 403 | Il tuo ruolo nel team non consente questa modifica | Chiedi a un proprietario o amministratore del team, oppure usa una chiave il cui creatore abbia un ruolo con questo permesso |
asset_workspace_forbidden | 403 | Questa chiave non raggiunge quella libreria, per esempio perché il membro che ha creato la chiave di team ha lasciato il team | Crea una nuova chiave nello spazio di lavoro che ti serve |
Disponibilità dei modelli
code | HTTP | Significato | Riprovare? |
|---|---|---|---|
50301 | 503 | Il modello non è attualmente disponibile | Sì. Riprova più tardi oppure scegli un altro modello |
50302 | 503 | Una richiesta di testo, con o senza streaming, è fallita nel modello prima della consegna di una risposta; l'importo bloccato è stato restituito | Sì. Reinvia con un nuovo Idempotency-Key |
40004 | 400 | Il modello funziona, ma non con queste impostazioni | No. Cambia l'impostazione indicata in msg |
50301 significa che il modello non poteva accettare l'attività al momento dell'invio: non è elencato, non ha prezzi attivi o non ha capacità di generazione disponibile adesso. Problemi temporanei di capacità possono risolversi con un numero limitato di tentativi. Un modello non disponibile o privo di prezzo attivo non viene risolto da richieste ripetute. Aggiorna il catalogo e ottieni la conferma dell'utente prima di cambiare modello.
40004 e 50301 sono intenzionalmente diversi. 50301 significa che il modello non può accettare alcuna attività adesso: aspettare può aiutare. 40004 significa che funziona ma non può gestire questa combinazione di impostazioni; msg indica l'impostazione fra parentesi, per esempio This model cannot serve this parameter combination right now (…). Aspettare non aiuta; cambiare un valore sì. Nessuno dei due crea un'attività o blocca denaro.
Gli errori di generazione dopo l'accettazione compaiono come state: failed
Un errore di generazione dopo che createTask ha restituito un ID compare in recordInfo come state: "failed" con errorCode / errorMessage; l'importo bloccato viene normalmente liberato per intero, tranne alcuni rifiuti di contenuto esplicitamente dichiarati come addebitabili nella pagina del modello. La stessa situazione di 40004, rilevata soltanto dopo l'accettazione, compare come errorCode: "unsupported_combination". Non aggiungere rami per codici applicativi sincroni assenti da questa pagina.
Codici di errore delle attività
Un errore dopo l'accettazione finisce in errorCode su recordInfo. L'insieme è chiuso: la diagnostica interna non appare mai qui, quindi gestisci qualsiasi valore fuori da questo elenco come upstream_failed.
errorCode | Significato | Riprovare? |
|---|---|---|
invalid_request | I parametri non sono stati accettati | No. Correggili e invia di nuovo |
unsupported_combination | Questa combinazione non è supportata adesso | No. Scegli impostazioni diverse |
content_rejected | Il modello scelto ha rifiutato questo contenuto. SpicyAPI stesso non esamina le richieste: consulta Responsabilità sui contenuti e sull'utilizzo | No. Cambia il prompt oppure scegli un altro modello |
rate_limited | Il servizio di generazione era occupato | Sì. Attendi progressivamente e riprova |
upstream_unavailable | La generazione non è temporaneamente disponibile | Sì. Riprova fra poco |
invalid_asset | Una risorsa di riferimento non è più disponibile | Non con un nuovo tentativo della stessa attività. retry riutilizza l'input originale, quindi carica nuovamente la risorsa e crea una nuova attività con il nuovo URI |
generation_failed | La generazione è partita e non ha prodotto nulla | Forse. Gli stessi input di solito falliscono allo stesso modo |
timeout | L'attività ha superato la scadenza senza terminare | Forse. Prova durata più breve o risoluzione inferiore |
upstream_failed | Categoria generale per il resto | Sì. Attendi progressivamente e riprova |
Le attività fallite o scadute normalmente liberano l'intero importo bloccato. Alcuni rifiuti di contenuto vengono addebitati al preventivo accettato soltanto quando la pagina del modello dichiara esplicitamente questa eccezione. Controlla settled e cost per l'addebito finale. errorMessage è per le persone: descrive il motivo in prosa, può riportare il motivo del modello in inglese (vedi sopra) e la formulazione cambia: basa la logica soltanto su errorCode. Qui «riprovare» significa inviare una nuova attività, addebitata nuovamente se riesce.
Quali errori costano denaro
Lo stato dell'attività e quello della connessione sono fatti diversi.
Creazioni esplicitamente rifiutate, attività scadute e registrazioni storiche annullate non comportano addebiti di generazione. Le attività fallite sono normalmente gratuite, salvo alcuni rifiuti di contenuto esplicitamente dichiarati come addebitabili nella pagina del modello. Un timeout o una disconnessione del client non è un errore dell'attività. Le attività accettate non possono essere annullate: recupera l'originale ed esamina stato finale e regolamento dell'addebito. Uno stream di testo disconnesso può essere addebitato quando è già stato registrato un utilizzo valido.
| Situazione | Addebito? |
|---|---|
L'API rifiuta esplicitamente createTask senza creare un'attività (per esempio 40201, 40202, 40004 o 50301) | No. Creazione e blocco fondi condividono una transazione; un errore del gateway o una risposta mancante non dimostrano un rifiuto |
| L'invio ha un timeout, una scheda si chiude o la connessione si interrompe | Ancora sconosciuto. Recupera l'accettazione usando la chiave di idempotenza originale; non considerarlo un nuovo tentativo gratuito |
createTask restituisce 202, l'attività termina poi failed / expired | Normalmente nessun addebito. Alcuni esiti failed / content_rejected vengono addebitati al preventivo accettato quando la pagina del modello lo dichiara esplicitamente. Leggi settled e cost |
Un'attività storica è canceled | No. È uno stato di compatibilità, non un'azione attualmente disponibile |
createTask restituisce 202, l'attività è succeeded | Sì, in base all'utilizzo effettivo ma entro il blocco accettato. I fondi inutilizzati vengono liberati; non viene riscosso in seguito un importo oltre il blocco |
Qualsiasi errore da recordInfo, chat/credit o common/* | No. Questi endpoint non sono mai fatturati |
Limite di frequenza (429) oppure deduplicazione con chiave di idempotenza | No. Nel primo caso non è mai avvenuta l'accettazione; nel secondo viene restituita l'attività originale, addebitata una sola volta |
Il regolamento o lo sblocco si completano automaticamente, anche dopo l'interruzione di un processo. Gli errori ordinari liberano l'intero importo; alcuni rifiuti di contenuto esplicitamente dichiarati nella pagina del modello vengono addebitati al preventivo accettato. Controlla regolamento dell'attività e saldo per verificare l'esito: consulta Fatturazione.
Quando vale la pena riprovare un 409
| Caso | Riprovare? |
|---|---|
Raro conflitto simultaneo su createTask con la stessa chiave e payload invariato | Sì. Attendi qualche centinaio di millisecondi e riprova con la stessa chiave. Consulta Idempotenza |
| La stessa chiave è già stata usata per un altro payload o un'altra chiave API | No. Fermati e correggi la titolarità della chiave; riprovare non cambia il conflitto |
Immagini inline o conferma del caricamento ancora in elaborazione (Retry-After può essere presente) | Sì. Attendi Retry-After o un paio di secondi e reinvia con lo stesso Idempotency-Key |
| Prezzi o configurazione dell'account cambiati mentre la richiesta era in corso | Sì. Ottieni un nuovo preventivo se li usi, poi reinvia |
| Artefatto ancora in copia | Sì. Attendi qualche secondo e chiedi di nuovo il link di download |
jobs/retry su un'attività non finita o con regolamento non ancora concluso | Sì, più tardi. Attendi che la sorgente sia in stato finale e settled sia true, poi riprova |
jobs/retry su un'attività riuscita oppure con input già rimosso per conservazione | No. Crea una nuova attività con createTask |
| URL di caricamento o file caricato scaduto | No. Carica nuovamente il file |
Scrivere la logica dei nuovi tentativi
Non riprovare alla cieca
400, 401, 40004, 40201 ed errori analoghi di input o autorizzazione danno la stessa risposta qualunque sia il numero di tentativi. Ogni tentativo indicato come «No» sopra è uno spreco e consuma il tuo limite di frequenza.
Attesa esponenziale con una variazione casuale, così una serie di errori non torna a inviare richieste nello stesso istante:
// 409 is operation-specific and must be handled at the call site.
const RETRYABLE = new Set([429, 500, 503, 50301]);
async function callWithRetry(path, init, { attempts = 4 } = {}) {
for (let i = 0; i < attempts; i++) {
const res = await fetch(`https://api.spicyapi.ai/api/v1${path}`, init);
// A page from the edge network is not our JSON envelope
if (!res.headers.get('content-type')?.includes('json')) {
const text = await res.text();
throw new Error(`HTTP ${res.status}, not JSON: ${text.slice(0, 200)}`);
}
const body = await res.json();
if (body.code === 200) return body.data;
if (!RETRYABLE.has(body.code)) {
throw new Error(`${body.code} ${body.msg} (request_id=${body.request_id})`);
}
// When the server says how long to wait, trust it
const retryAfter = Number(res.headers.get('retry-after'));
const backoff = Number.isFinite(retryAfter) && retryAfter > 0
? retryAfter * 1000
: 2 ** i * 1000 + Math.random() * 500;
await new Promise((r) => setTimeout(r, backoff));
}
throw new Error('out of retries');
}import random, time, requests
# 409 is operation-specific and must be handled at the call site.
RETRYABLE = {429, 500, 503, 50301}
BASE = "https://api.spicyapi.ai/api/v1"
def call_with_retry(method: str, path: str, attempts: int = 4, **kwargs) -> dict:
for i in range(attempts):
res = requests.request(method, f"{BASE}{path}", **kwargs)
# A page from the edge network is not our JSON envelope
try:
body = res.json()
except ValueError:
raise RuntimeError(f"HTTP {res.status_code}, not JSON: {res.text[:200]}")
if body["code"] == 200:
return body["data"]
if body["code"] not in RETRYABLE:
raise RuntimeError(f'{body["code"]} {body["msg"]} (request_id={body.get("request_id")})')
# When the server says how long to wait, trust it
retry_after = res.headers.get("retry-after")
backoff = float(retry_after) if retry_after else 2**i + random.random() * 0.5
time.sleep(backoff)
raise RuntimeError("out of retries")package main
import (
"encoding/json"
"fmt"
"io"
"math/rand"
"net/http"
"strconv"
"time"
)
// retryable lists the codes worth another attempt. Anything else returns
// the same answer however many times you ask — and each ask eats your own
// rate limit.
// Whether the generation itself failed is read from recordInfo's state.
var retryable = map[int]bool{429: true, 500: true, 503: true, 50301: true}
type envelope struct {
Code int `json:"code"`
Msg string `json:"msg"`
Data json.RawMessage `json:"data"`
RequestID string `json:"request_id"`
}
func callWithRetry(newReq func() (*http.Request, error), attempts int, out any) error {
for i := 0; i < attempts; i++ {
req, err := newReq()
if err != nil {
return err
}
res, err := http.DefaultClient.Do(req)
if err != nil {
// Transport-level failure: the request may well have arrived.
// On createTask, send the same Idempotency-Key or this retry
// turns into a second generation and a second charge.
time.Sleep(backoff(i, ""))
continue
}
var env envelope
decErr := json.NewDecoder(res.Body).Decode(&env)
io.Copy(io.Discard, res.Body)
res.Body.Close()
if decErr != nil {
// Not our JSON envelope, for example a page from the edge network.
return fmt.Errorf("HTTP %d, response is not JSON: %w", res.StatusCode, decErr)
}
if env.Code == 200 {
return json.Unmarshal(env.Data, out)
}
if !retryable[env.Code] {
return fmt.Errorf("%d %s (request_id=%s)", env.Code, env.Msg, env.RequestID)
}
// When the server says how long to wait, trust it
time.Sleep(backoff(i, res.Header.Get("Retry-After")))
}
return fmt.Errorf("out of retries")
}
// backoff trusts Retry-After when present, otherwise backs off
// exponentially with jitter so a batch of failures does not come back
// as one thundering herd.
func backoff(attempt int, retryAfter string) time.Duration {
if s, err := strconv.Atoi(retryAfter); err == nil && s > 0 {
return time.Duration(s) * time.Second
}
base := time.Duration(1<<attempt) * time.Second
return base + time.Duration(rand.Intn(500))*time.Millisecond
}Quando riprovi createTask, invia sempre lo stesso Idempotency-Key; altrimenti «era riuscito, abbiamo soltanto perso la risposta» diventa una generazione duplicata e un addebito duplicato.
Diagnostica
Quando un errore sembra incomprensibile, percorri questo elenco:
request_id: forniscicelo e possiamo trovare la richiesta esatta.code: le tabelle sopra.- Log nella console: incolla l'ID dell'attività o
request_idnella ricerca della pagina Log per aprire l'attività: stato, costo, latenza, chiave che l'ha inviata, codice di errore e motivo. Parametri e risultati vengono mostrati soltanto mentre sono conservati e, in un team, soltanto ai ruoli autorizzati a leggere i contenuti.
Il msg per 500 è sempre un generico «servizio temporaneamente non disponibile» senza dettagli interni: nella risposta non compaiono mai struttura interna, nomi di componenti o stack trace. Questi possono essere rintracciati soltanto attraverso request_id.
Per contattarci, apri una conversazione sotto Assistenza nella console oppure scrivi a support@spicyapi.ai. Includi request_id o ID dell'attività, ora (UTC) e code. Non inviare mai una chiave API.
OpenAPI e indice degli endpoint
Contratto OpenAPI 3.1 scaricabile, tutti gli endpoint pubblici con le rispettive guide, header condivisi e origine API di produzione.
Limiti di frequenza
Come funzionano i limiti separati di lettura e scrittura, quali richieste conteggiano, gli altri limiti e perché la concorrenza è diversa.

