Błędy
API odpowiada zwykłymi kodami HTTP. Wszystko z zakresu 2xx oznacza sukces. Pozostałe kody przychodzą z ciałem JSON, w którym pole typu mówi, co się stało - na nim buduj obsługę po swojej stronie.
Jak wygląda błąd
Endpointy /v1/generate, /v1/tasks, /v1/models i /v1/balance zwracają jeden kształt:
{ "error": { "type": "insufficient_balance", "message": "Available balance is lower than the estimated cost of this task." } }Czat zachowuje format błędów z SDK, z którym jest zgodny. Dzięki temu obsługa, którą już masz w kodzie, działa bez przeróbek:
| Endpoint | Kształt błędu |
|---|---|
/v1/chat/completions | OpenAI: {"error": {"message": "...", "type": "..."}} |
/v1/messages | Anthropic: {"type": "error", "error": {"type": "...", "message": "..."}} |
Tekst w message piszemy dla człowieka i może się z czasem zmienić. Do decyzji w kodzie używaj statusu HTTP i pola type.
Lista typów
| HTTP | type | Co się stało | Twoja reakcja |
|---|---|---|---|
| 400 | invalid_request | Zapytanie ma złą budowę, np. zepsuty JSON albo brakuje model | Popraw zapytanie |
| 401 | authentication_error | Klucza nie ma, ma zły format albo został skasowany | Sprawdź klucz i nagłówek (Uwierzytelnianie) |
| 402 | insufficient_balance | Dostępne środki nie pokrywają kosztu albo blokady | Doładuj saldo |
| 403 | permission_error | Klucz wyczerpał miesięczny limit wydatków. Treść: This key reached its monthly spend limit. Create a new key with a higher limit in the dashboard or use another key. | Załóż klucz z wyższym limitem albo użyj innego |
| 404 | not_found | Nie ma takiego modelu albo zadania | Porównaj id z listą w Modelach |
| 422 | invalid_request | input łamie input_schema modelu | Komunikat wskaże pole do poprawy |
| 429 | rate_limit | Klucz wysyła za dużo zapytań | Zwolnij i ponawiaj z rosnącą przerwą |
| 429 | rate_limit_error | Konto ma 25 nieudanych generacji w ostatniej dobie, więc nowe /v1/generate są wstrzymane | Odczekaj tyle sekund, ile podaje Retry-After, i zmień prompt albo parametry |
| 502 | upstream_error | Błąd po stronie dostawcy modelu | Ponów po chwili |
| 504 | timeout | Model nie zdążył odpowiedzieć | Ponów; w czacie spróbuj krótszej odpowiedzi |
Limity na klucz
Domyślnie jeden klucz API może wysłać:
| Endpoint | Limit |
|---|---|
POST /v1/generate | 60 zapytań na minutę |
/v1/chat/completions, /v1/messages | 600 zapytań na minutę |
POST /v1/generate, generacje nieudane | 25 na konto w przesuwnym oknie 24 h, potem 429 rate_limit_error z Retry-After |
Przekroczenie kończy się 429 rate_limit. Rozciągnij zapytania w czasie albo rozdziel różne prace na osobne klucze.
Porażka zadania to coś innego niż błąd HTTP
Gdy generacja wystartuje, a potem się wywróci, nie zobaczysz kodu błędu HTTP. POST /v1/generate odpowiada 202, a zadanie dopiero później przechodzi w status: "failed" z obiektem error:
{ "id": "task_3xq1...", "status": "failed", "error": { "type": "upstream_error", "message": "..." }, "cost": { "amount": "0.00", "currency": "PLN" } }Zablokowana kwota wraca, koszt wynosi 0. Dlatego przy każdym zakończonym zadaniu sprawdzaj pole status (więcej w Zadaniach).
Co ponawiać, a czego nie
- Ponawiaj:
429,502,504- z przerwą rosnącą wykładniczo. - Nie ponawiaj bez zmiany po swojej stronie:
400,401,402,403,404,422. Identyczne zapytanie skończy się identycznie. - Nie wysyłaj w kółko zadań, które kończą się
status: "failed". Są bezpłatne, ale po 25 porażkach w ciągu doby konto dostaje429 rate_limit_errori nowe zlecenia czekają do upływuRetry-After. Przed kolejną próbą zmień prompt albo parametry.
Przykładowy wrapper z ponowieniami:
import os, random, time, requests
RETRY = {429, 502, 504}
def call(method, url, **kwargs):
key = os.environ["KADRARIS_API_KEY"]
headers = {"Authorization": f"Bearer {key}"}
for attempt in range(5):
r = requests.request(method, url, headers=headers,
timeout=130, **kwargs)
if r.status_code not in RETRY:
break
# 1, 2, 4, 8... s plus losowy rozrzut
time.sleep(min(2 ** attempt, 30) + random.random())
if r.status_code >= 400:
err = r.json().get("error", {})
raise RuntimeError(
f"{r.status_code} {err.get('type')}: {err.get('message')}")
return r
task = call("POST", "https://kadraris.com/v1/generate",
json={"model": "gpt-image-2.5",
"input": {"prompt": "Klaps filmowy na stole"}}).json()const RETRY = new Set([429, 502, 504]);
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
async function call(url, init = {}) {
let res;
for (let attempt = 0; attempt < 5; attempt++) {
res = await fetch(url, {
...init,
headers: {
Authorization: `Bearer ${process.env.KADRARIS_API_KEY}`,
"Content-Type": "application/json",
...init.headers,
},
});
if (!RETRY.has(res.status)) break;
const wait = Math.min(2 ** attempt * 1000, 30000);
await sleep(wait + Math.random() * 1000);
}
if (!res.ok) {
const { error } = await res.json();
throw new Error(`${res.status} ${error?.type}: ${error?.message}`);
}
return res.json();
}Jeśli używasz SDK OpenAI albo Anthropic, ponowienia masz w pakiecie: oba same powtarzają kilka razy zapytania odbite limitem albo błędem serwera. Liczbę prób zmieniasz parametrem max_retries (Python) lub maxRetries (JavaScript).
from openai import OpenAI
client = OpenAI(base_url="https://kadraris.com/v1",
api_key="sk-kd-...", max_retries=4)Ostrożnie z ponawianiem /v1/generate
Każde przyjęte wywołanie /v1/generate zakłada nowe zadanie i zamraża pod nie środki. Kiedy twoje połączenie przekroczyło czas i nie masz pewności, czy zapytanie dotarło, nie powtarzaj go automatycznie. Odnotuj zdarzenie w logach, a przy webhookach poczekaj na doręczenie, zanim zlecisz to samo drugi raz.























