Fehler
Alle Geschäftscodes, HTTP-Statuswerte, passende Maßnahmen und die Frage, welche Wiederholungen helfen.
Bei einem fehlgeschlagenen Aufruf nennt die Antwort zwei Dinge: eine Zahl in code, die die Problemart beschreibt, und einen lesbaren Satz in msg. Diese Seite erklärt jede Zahl und ob ein weiterer Versuch hilft. Für eine schnelle Lösung anhand eines Symptoms beginnen Sie mit Fehlerbehebung.
Die Antwortstruktur
JSON-Antworten der nativen Task-API nutzen dieselben vier umgebenden Felder. Lesen Sie /v1-Kompatibilitätsantworten und SSE nach deren eigenen Protokollen:
{ "code": 200, "msg": "success", "data": { }, "request_id": "req_…" }codeist ein Geschäftscode, kein HTTP-Status.200bedeutet Erfolg.- Bei Fehler fehlt
data; es ist nichtnull. Sie erhaltencode,msgundrequest_id. Die eine Ausnahme: Einige Fehler der Asset-Bibliothek fügendata.reasonhinzu. msgist für Menschen gedacht. Wortlaut und Sprache können sich ändern; siehe unten. Richten Sie Ihre Programmlogik nie danach aus, sondern nachcode.request_idsteht auch im HeaderX-Request-Id. Nennen Sie ihn bei Problemmeldungen.
Der HTTP-Status wird ebenfalls korrekt gesetzt. Verwenden Sie einen der beiden Werte, aber nur einen: Bei spezifischen Codes unterscheiden sie sich. Unzureichendes Guthaben ergibt etwa code: 40201 und HTTP 402. Vermischte Behandlung übersieht irgendwann einen Fall.
Wir empfehlen code: 404 sagt nur „nicht gefunden“, 40201 dagegen, dass das verfügbare Guthaben nicht reicht.
Nicht jede Antwort ist JSON
Eine nicht als JSON lesbare Antwort, etwa Klartext oder HTML aus unserem Edge-Netzwerk, hat keinen code. Behandeln Sie sie nach HTTP-Status und protokollieren Sie den Beginn des Bodys. Wiederholen Sie 403 nicht blind: Eine regionale Sperre lässt sich durch Wiederholung nicht ändern.
Sprache der Fehlermeldungen
Lesbare Fehlertexte können in Ihrer gewählten Sprache erscheinen. Standard ist Englisch. Nur diese drei Stellen folgen der Auswahl:
msgin der Antwortstruktur;errorMessageeines fehlgeschlagenen Tasks inrecordInfound Webhook-Callbacks, mit der unten genannten Ausnahme;error.messagein Fehlerobjekten kompatibler Endpunkte im OpenAI-, Anthropic- oder Google-Gemini-Format unter/v1beta.
Unterstützte Sprachcodes: en, ja, ko, de, fr, es, pt-BR, ru, it, pl.
Die Ausnahme: Nennt das Modell selbst einen konkreten Fehlergrund, etwa ein nicht lesbares Referenzbild oder eine Urheberrechtsbeschränkung für generiertes Audio, enthält errorMessage diesen Grund auf Englisch. Interne Details wie Servicenamen, Hosts, URLs und Anfrage-IDs werden entfernt, damit Sie den tatsächlichen Fehler verstehen können. Gründe zum Konto- oder Abrechnungszustand werden nie durchgereicht; stattdessen erhalten Sie unsere eigene Meldung. Content-Language beschreibt weiterhin msg; Ihre Programmlogik richtet sich immer nach errorCode.
Sprache auswählen
Die erste zutreffende Ebene gewinnt:
- Anfrageheader
Accept-Language, nur für diese Anfrage. Die übliche HTTP-Gewichtung gilt: Wir verwenden die erkannte Sprache mit dem höchstenq-Wert. Regionale Varianten werden zugeordnet, alsode-DEzudeundpt-PTzupt-BR. Chinesisch wird nicht angeboten; chinesische Tags treffen daher nichts und der nächste Kandidat wird geprüft.*bedeutet keine Präferenz. - Kontoeinstellung unter Einstellungen → API-Fehlersprache. Sie gilt für alle Anfragen mit einem API-Key des Kontos ohne erkennbares
Accept-Language. Die meisten serverseitigen SDKs und curl senden standardmäßig keinen solchen Header; daher verlassen sich viele Integrationen darauf. Webhook-Callbacks haben keinen Anfrageheader zur Sprachauswahl und folgen immer der Kontoeinstellung. - Andernfalls Englisch.
Fehlerantworten enthalten Content-Language mit der tatsächlich verwendeten Meldungssprache.
Nur der lesbare Text ändert sich
code, errorCode, OpenAI-kompatibles type und code, Gemini-kompatibles code und status sowie alle Feldnamen bleiben in jeder Sprache identisch. Verwenden Sie Codes und analysieren Sie nie den Wortlaut; erfassen Sie code und request_id in Logs und Warnmeldungen.
Beispiel
Derselbe Fehler 40201 für unzureichendes Guthaben erscheint standardmäßig auf Englisch:
HTTP/1.1 402 Payment Required
Content-Language: en
{ "code": 40201, "msg": "Insufficient balance", "request_id": "req_…" }Fügen Sie den Header Accept-Language: ja hinzu. Der Rest entspricht der Taskerstellung im Schnellstart:
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 ist nun Japanisch; code bleibt 40201:
HTTP/1.1 402 Payment Required
Content-Language: ja
{ "code": 40201, "msg": "残高が不足しています", "request_id": "req_…" }Allgemeine Codes
code | HTTP | Bedeutung | Erneut versuchen? |
|---|---|---|---|
200 | 200 | Erfolg | — |
400 | 400 | Fehlerhafte Anfrage oder ungültige Parameter | Nein. Korrigieren und erneut senden |
40003 | 400 | Größe, Typ oder Inhalt einer hochgeladenen Datei entspricht nicht den Angaben bei Anforderung der Upload-URL | Nein. Neue Upload-URL mit korrektem contentType und Größe anfordern, erneut hochladen und Commit durchführen |
40004 | 400 | Anfrage ist gültig, aber das Modell kann diese Einstellungskombination derzeit nicht bedienen | Nein. In msg genannte Einstellung ändern, etwa Auflösung oder Seitenverhältnis, oder anderes Modell wählen; siehe unten |
401 | 401 | Authentifizierung fehlgeschlagen; msg nennt einen von drei Fällen | Nein. Header oder Key korrigieren |
403 | 403 | Nicht erlaubt. Bei gültigem API-Key ist das Konto eingeschränkt; siehe unten. Eine unbestätigte E-Mail ergibt stattdessen 40310 | Nein |
404 | 404 | Existiert nicht oder gehört Ihnen nicht | Nein |
409 | 409 | Konflikt mit aktuellem Zustand | Manchmal; siehe unten |
40901 | 409 | Preisangebot abgelaufen, Anfrage oder Preis geändert | Preis erneut abfragen und bestätigen; angenommene Anfragen mit ursprünglichem Idempotenzschlüssel wiederfinden |
413 | 413 | Anfragebody überschreitet die akzeptierte Servergröße | Nein. Body verkleinern oder Medien über den Uploadablauf senden |
429 | 429 | Zu viele Anfragen | Meistens. Nach Retry-After warten. Ein bestimmter 429 betrifft nicht die Geschwindigkeit; siehe Ratenbegrenzungen |
500 | 500 | Fehler auf unserer Seite | Ja. Mit Abstand erneut versuchen |
503 | 503 | Hilfsdienst für Preisabfragen, Inline-Bildspeicherung oder Medienuploads kurzzeitig nicht verfügbar | Ja. Vorhandenes Retry-After abwarten und erneut versuchen; bei createTask mit demselben Idempotency-Key. Hält es ohne Retry-After an, folgen Sie msg, etwa öffentliche HTTPS-URL statt Inline-Bild verwenden |
503 und 50301 teilen einen HTTP-Status, sind aber verschiedene Geschäftscodes: 503 betrifft einen Hilfsdienst, 50301 das Modell selbst.
Die drei 401-Fälle unterscheiden
Drei unterschiedliche Fehler führen bei der Authentifizierung zu 401 und benötigen verschiedene Lösungen. code bleibt jeweils 401; msg erklärt den Unterschied.
msg auf Englisch | Tatsächlicher Vorgang | Änderung |
|---|---|---|
Missing credentials: send your API key in the Authorization header as "Bearer sk-spicy-..." (an kompatiblen /v1- und /v1beta-Endpunkten: …as "Authorization: Bearer sk-spicy-...", x-api-key, or x-goog-api-key) | Anfrage kam ganz ohne Zugangsdaten an | Ihr Client verwirft den Header. Prüfen Sie Interceptor, Proxy und ob die Umgebungsvariable im laufenden Prozess geladen wurde |
API key format is not recognized: keys begin with sk-spicy- | Zugangsdaten kamen an, stammen aber nicht von uns | Sie haben einen Konsolen-Sitzungswert oder Key eines anderen Anbieters eingefügt. SpicyAPI-Keys beginnen mit sk-spicy-; echo "${SPICY_API_KEY:0:9}" muss genau dies ausgeben |
Credentials are invalid or expired | Key hat unser Format, existiert aber nicht, wurde widerrufen oder ist abgelaufen | Prüfen Sie ihn auf der API-Key-Seite. Bei Widerruf oder Ablauf erstellen Sie einen Ersatz. Wiederholen wird nie zu 200 führen |
Der mittlere Fall wird ohne Datenbankabfrage erkannt. Deshalb kann er etwas sagen, was der dritte nicht kann: Die Zeichenfolge konnte nie funktionieren. Die dritte Meldung ist für nicht vorhandene, widerrufene und abgelaufene Keys bewusst identisch, damit die Antwort nicht zur Prüfung existierender Keys genutzt werden kann.
Hier stehen die Zeichenfolgen auf Englisch; msg folgt der beschriebenen Sprachauswahl. Sein Wortlaut ist keine vertragliche Zusage. Verwenden Sie code für die Logik und die Meldung zum Lesen, Anzeigen oder Protokollieren, nicht zum Zeichenfolgenvergleich.
Konto kann die API nicht aufrufen
In beiden Fällen ist der Key gültig; das Konto kann die API noch nicht aufrufen.
40310 bedeutet unbestätigte E-Mail-Adresse. Die Meldung lautet Verify your account email to run models and call the API…:
- Öffnen Sie die Konsole. Ein Banner oben weist auf die unbestätigte Adresse hin und kann die Bestätigungsmail erneut senden.
- Folgen Sie dem Link in der Bestätigungsmail; prüfen Sie auch den Spamordner.
- Wiederholen Sie denselben Aufruf. Ein neuer Key ist nicht nötig.
Die Konsole funktioniert während der API-Sperre weiter, sodass Sie Ihren Verlauf sehen können.
Ein einfaches 403 mit This account cannot call the API right now bedeutet ein eingeschränktes Konto. Kontaktieren Sie den Support mit request_id.
Guthaben und Limits
code | HTTP | Bedeutung | Maßnahme |
|---|---|---|---|
40201 | 402 | Guthaben deckt die geschätzten Kosten nicht | Unter Abrechnung in der Konsole aufladen; siehe Kontoeinrichtung. GET /api/v1/chat/credit prüfen |
40202 | 402 | Ausgabenlimit erreicht | msg zeigt welches. Keylimit mit This key has reached its …: Auf API-Key-Seite erhöhen oder Zurücksetzung abwarten. Teamlimit mit Workspace … budget has been reached: Teaminhaber, Administrator oder Budgetverantwortlichen um Erhöhung auf der Teamseite bitten. Plattformlimit mit Platform quota for today is exhausted…: Nicht selbst änderbar; nach 00:00 UTC erneut versuchen. Tageslimits starten um 00:00 UTC, Monatslimits am 1. um 00:00 UTC neu; Gesamtlimits werden nie zurückgesetzt |
Keinen der beiden Fehler behebt bloßes Wiederholen. Guthaben und Limits ändern sich nicht durch erneute Anfrage. Siehe Ausgabenlimits.
Berechtigungen
code | HTTP | Bedeutung | Maßnahme |
|---|---|---|---|
40301 | 403 | Dieser Key darf das Modell nicht aufrufen | Modellfreigabeliste des Keys in der Konsole erweitern |
40302 | 403 | Quelladresse fehlt in der IP-Freigabeliste | Hinzufügen oder IP-Beschränkung dieses Keys entfernen |
40303 | 403 | Dienst wird in dieser Region nicht angeboten | Nicht umgehbar; siehe verfügbare Regionen |
40310 | 403 | E-Mail des Kontos noch nicht bestätigt; Modelle können nicht ausgeführt werden | Link in Registrierungsmail öffnen oder neue Mail aus der Konsole senden. Nach Bestätigung funktioniert der Key ohne weitere Änderung |
Keiner dieser Fehler lässt sich durch Wiederholung lösen.
Ursachencodes der Asset-Bibliothek
Fehler an den Endpunkten der Asset-Bibliothek (/api/v1/assets…) können dem üblichen Umschlag ein maschinenlesbares Feld hinzufügen, data.reason:
{ "code": 400, "msg": "…", "data": { "reason": "asset_library_locked" }, "request_id": "req_…" }code, HTTP-Status, msg und der Task-errorCode sind mit oder ohne Grund gleich, bestehende Behandlung funktioniert also weiter. Der Grund erscheint nur an den Endpunkten der Asset-Bibliothek, nie an anderen nativen Endpunkten, und die OpenAI-kompatible und andere Kompatibilitätsschichten behalten ihre eigenen Fehlerformen. Die Liste kann wachsen: Behandeln Sie einen unbekannten Wert wie einen fehlenden Grund.
data.reason | HTTP | Bedeutung | Was zu tun ist |
|---|---|---|---|
asset_library_locked | 400 | Die Asset-Bibliothek wird mit der ersten Aufladung freigeschaltet | Laden Sie auf. Bereits gespeicherte Assets bleiben nutzbar |
asset_library_full | 400 | Das Speicherkontingent (Anzahl oder Bytes) ist voll | Löschen Sie Assets oder wechseln Sie in einen höheren Plan; GET /api/v1/assets/usage zeigt den nächsten |
asset_file_too_large | 400 | Die Datei überschreitet das Limit pro Datei des Plans | Verwenden Sie eine kleinere Datei oder wechseln Sie in einen höheren Plan |
asset_daily_limit | 429 | Die tägliche Anzahl neuer Assets ist erreicht | Warten Sie bis 00:00 UTC und beachten Sie Retry-After |
asset_storage_full | 503 | Der Speicher der gesamten Plattform ist voll | Versuchen Sie es später erneut |
asset_permission_denied | 403 | Ihre Teamrolle erlaubt diese Änderung nicht | Bitten Sie einen Team-Inhaber oder Admin, oder verwenden Sie einen Key, dessen Ersteller eine Rolle mit dieser Berechtigung hat |
asset_workspace_forbidden | 403 | Dieser Key erreicht diese Bibliothek nicht, zum Beispiel weil das Mitglied, das den Team-Key erstellt hat, das Team verlassen hat | Erstellen Sie einen neuen Key im benötigten Workspace |
asset_entity_name_taken | 409 | Die Bibliothek hat bereits eine Figur mit diesem Namen (ohne Beachtung von Groß- und Kleinschreibung). Figuren benennen Sie auf der Seite Assets der Web-App; die schreibgeschützte API zeigt sie derzeit nicht an | Wählen Sie einen anderen Namen |
Modellverfügbarkeit
code | HTTP | Bedeutung | Erneut versuchen? |
|---|---|---|---|
50301 | 503 | Modell derzeit nicht verfügbar | Ja. Später versuchen oder anderes Modell wählen |
50302 | 503 | Textanfrage mit oder ohne Streaming scheiterte am Modell vor Antwortauslieferung; Reservierung wurde zurückgegeben | Ja. Mit neuem Idempotency-Key erneut senden |
40004 | 400 | Modell läuft, jedoch nicht mit diesen Einstellungen | Nein. Die in msg genannte Einstellung ändern |
50301 bedeutet, dass das Modell den Task bei Einreichung nicht annehmen konnte: Es ist nicht gelistet, hat keinen aktiven Preis oder derzeit keine Generierungskapazität. Vorübergehende Kapazitätsprobleme können begrenzte Wiederholungen lösen. Ein nicht verfügbares Modell oder fehlender aktiver Preis wird durch wiederholte Anfragen nicht repariert. Aktualisieren Sie den Katalog und holen Sie vor einem Modellwechsel die Bestätigung des Nutzers ein.
40004 und 50301 unterscheiden sich bewusst. Bei 50301 kann das Modell derzeit keinen Task annehmen; Warten kann helfen. Bei 40004 arbeitet das Modell, kann aber diese Kombination nicht bedienen. msg nennt die Einstellung in Klammern, etwa This model cannot serve this parameter combination right now (…). Warten hilft nicht, eine Wertänderung schon. Keiner der beiden Fälle erstellt einen Task oder reserviert Geld.
Generierungsfehler nach Annahme erscheinen als state: failed
Scheitert die Generierung nach Rückgabe einer Task-ID durch createTask, zeigt recordInfo den Zustand state: "failed" mit errorCode / errorMessage. Die Reservierung wird normalerweise vollständig freigegeben, außer bei ausdrücklich auf der Modellseite als kostenpflichtig genannten Inhaltsablehnungen. Wird die Situation von 40004 erst nach Annahme erkannt, erscheint sie als errorCode: "unsupported_combination". Ergänzen Sie keine synchronen Geschäftscodefälle, die auf dieser Seite nicht vorkommen.
Fehlercodes eines Tasks
Fehler nach Taskannahme stehen in errorCode von recordInfo. Die Menge ist abgeschlossen: Interne Diagnosen erscheinen hier nie. Behandeln Sie unbekannte Werte wie upstream_failed.
errorCode | Bedeutung | Erneut versuchen? |
|---|---|---|
invalid_request | Parameter nicht akzeptiert | Nein. Korrigieren und neu einreichen |
unsupported_combination | Kombination derzeit nicht unterstützt | Nein. Andere Einstellungen wählen |
content_rejected | Gewähltes Modell lehnt den Inhalt ab. SpicyAPI selbst prüft Anfragen nicht; siehe Inhalte und Nutzungsverantwortung | Nein. Prompt ändern oder anderes Modell wählen |
rate_limited | Generierungsdienst ausgelastet | Ja. Mit Abstand erneut versuchen |
upstream_unavailable | Generierung vorübergehend nicht verfügbar | Ja. In Kürze erneut versuchen |
invalid_asset | Referenzdatei nicht mehr verfügbar | Nicht als Retry. retry verwendet ursprüngliche Eingabe; Datei neu hochladen und Task mit neuer URI erstellen |
generation_failed | Generierung startete und erzeugte nichts | Vielleicht. Gleiche Eingaben scheitern meist gleich |
timeout | Task überschritt seine Frist ohne Abschluss | Vielleicht. Kürzere Dauer oder geringere Auflösung versuchen |
upstream_failed | Sammelcode für andere Fehler | Ja. Mit Abstand erneut versuchen |
Fehlgeschlagene oder abgelaufene Tasks geben normalerweise die gesamte Reservierung frei. Bestimmte Inhaltsablehnungen werden nur bei ausdrücklich auf der Modellseite genannter Ausnahme zum angenommenen Angebotspreis berechnet. Prüfen Sie settled und cost für die endgültigen Kosten. errorMessage ist für Menschen: Es beschreibt den Grund, kann den modellseitigen Grund auf Englisch wiedergeben, siehe oben, und sein Wortlaut ändert sich. Verwenden Sie ausschließlich errorCode für Ihre Logik. „Erneut versuchen“ bedeutet hier einen neuen Task, der bei Erfolg erneut berechnet wird.
Welche Fehler Geld kosten
Taskzustand und Verbindungszustand sind verschiedene Tatsachen
Ausdrücklich abgelehnte Erstellungen, abgelaufene Tasks und historische Abbruchdatensätze verursachen keine Generierungskosten. Fehlgeschlagene Tasks sind normalerweise kostenlos, außer bei bestimmten ausdrücklich als kostenpflichtig genannten Inhaltsablehnungen. Ein Clienttimeout oder Verbindungsabbruch ist kein Taskfehler. Angenommene Tasks sind nicht abbrechbar: Finden Sie den ursprünglichen Task wieder und prüfen Sie Endzustand und Abrechnung. Ein unterbrochener Textstream kann berechnet werden, wenn gültige Nutzung bereits erfasst wurde.
| Situation | Kostenpflichtig? |
|---|---|
API lehnt createTask ausdrücklich ohne Taskerstellung ab, etwa mit 40201, 40202, 40004 oder 50301 | Nein. Erstellung und Reservierung erfolgen in einer Transaktion; Gatewayfehler oder fehlende Antwort beweisen keine Ablehnung |
| Einreichung läuft in Timeout, Tab wird geschlossen oder Verbindung bricht ab | Noch unbekannt. Annahme mit ursprünglichem Idempotenzschlüssel wiederfinden; nicht als kostenlosen Retry behandeln |
createTask liefert 202, später failed / expired | Normalerweise kostenlos. Bestimmte failed / content_rejected-Ergebnisse kosten den angenommenen Angebotspreis, wenn die Modellseite dies ausdrücklich nennt. settled und cost lesen |
Historischer Task mit canceled | Nein. Kompatibilitätszustand, keine derzeit verfügbare Aktion |
createTask liefert 202, Task ist succeeded | Ja, tatsächliche Nutzung bis höchstens zur angenommenen Reservierung. Rest wird freigegeben; keine darüber hinausgehende Nachbelastung |
Beliebiger Fehler aus recordInfo, chat/credit oder common/* | Nein. Diese Endpunkte kosten nie Geld |
Ratenbegrenzung 429 oder Deduplizierung durch Idempotenzschlüssel | Nein. Erster Fall wurde nicht angenommen; zweiter liefert den einmal berechneten ursprünglichen Task |
Abrechnung oder Freigabe endet automatisch, auch nach Prozessunterbrechung. Gewöhnliche Fehler geben die gesamte Reservierung frei; bestimmte ausdrücklich auf der Modellseite genannte Inhaltsablehnungen werden zum angenommenen Angebotspreis berechnet. Prüfen Sie Taskabrechnung und Guthaben für das Ergebnis; siehe Abrechnung.
Wann Wiederholung bei 409 hilft
| Fall | Erneut versuchen? |
|---|---|
Seltener gleichzeitiger createTask-Konflikt mit gleichem Schlüssel und unveränderten Nutzdaten | Ja. Einige hundert Millisekunden warten und mit demselben Schlüssel wiederholen; siehe Idempotenz |
| Schlüssel wurde bereits für andere Nutzdaten oder anderen API-Key verwendet | Nein. Anhalten und Zuordnung des Schlüssels korrigieren; Wiederholung ändert keinen Konflikt |
Inline-Bilder oder Upload-Commit werden noch verarbeitet, eventuell mit Retry-After | Ja. Retry-After oder einige Sekunden abwarten und mit demselben Idempotency-Key senden |
| Kontopreise oder Konfiguration änderten sich während der Anfrage | Ja. Bei Nutzung von Angeboten Preis erneut abfragen, dann neu senden |
| Datei wird noch kopiert | Ja. Einige Sekunden warten und Downloadlink erneut abfragen |
jobs/retry für noch nicht abgeschlossenen oder noch nicht abgerechneten Task | Ja, später. Auf Endzustand und settled gleich true des ursprünglichen Tasks warten |
jobs/retry für erfolgreichen Task oder bereits nach Aufbewahrungsfrist gelöschte Eingabe | Nein. Mit createTask neuen Task erstellen |
| Upload-URL oder hochgeladene Datei abgelaufen | Nein. Datei erneut hochladen |
Wiederholungslogik schreiben
Wiederholen Sie nicht blind
400, 401, 40004, 40201 und ähnliche Eingabe- oder Berechtigungsfehler liefern bei jedem Versuch dieselbe Antwort. Alle oben mit „Nein“ markierten Versuche sind unnötig und verbrauchen Ihr eigenes Anfragekontingent.
Exponentiell wachsende Wartezeiten mit zufälliger Streuung verhindern, dass viele fehlgeschlagene Anfragen im selben Moment zurückkehren:
// 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
}Senden Sie beim Wiederholen von createTask immer denselben Idempotency-Key. Sonst führt „eigentlich erfolgreich, nur die Antwort verloren“ zu doppelter Generierung und Belastung.
Fehlersuche
Wenn ein Fehler unverständlich ist, gehen Sie diese Liste durch:
request_id: Damit können wir die genaue Anfrage finden.code: Prüfen Sie die Tabellen oben.- Logs in der Konsole: Task-ID oder
request_idim Suchfeld unter Logs öffnet den Task mit Status, Kosten, Latenz, verwendendem Key, Fehlercode und Grund. Parameter und Ergebnisse erscheinen nur während ihrer Aufbewahrung; im Team nur für Rollen mit Leseberechtigung für Inhalte.
msg für 500 bleibt eine allgemeine Meldung „Dienst vorübergehend nicht verfügbar“ ohne interne Details. Interne Struktur, Komponentennamen und Stacktraces erscheinen nie in einer Antwort; sie lassen sich nur über request_id verfolgen.
Öffnen Sie unter Support eine Unterhaltung oder schreiben Sie an support@spicyapi.ai. Nennen Sie request_id oder Task-ID, Zeit in UTC und code. Senden Sie niemals einen API-Key.
OpenAPI und Endpunktübersicht
Herunterladbarer OpenAPI-3.1-Vertrag, öffentliche Endpunkte mit passenden Leitfäden, gemeinsame Anfrageheader und produktiver API-Ursprung.
Ratenbegrenzungen
Getrennte Kontingente für Lese- und Schreibanfragen, ihre Nutzung, weitere Grenzen und der Unterschied zur Parallelität.

