Błędy
Jantaris używa standardowych kodów HTTP. 2xx znaczy, że zapytanie przeszło. Każdy inny kod ma ciało JSON z typem błędu, na którym możesz oprzeć logikę.
Format
/v1/generate, /v1/tasks, /v1/models i /v1/balance zwracają:
{ "error": { "type": "insufficient_balance", "message": "Available balance is lower than the estimated cost of this task." } }Endpointy czatu zachowują format SDK, z którym są zgodne, więc istniejąca obsługa błędów dalej działa:
| Endpoint | Format błędu |
|---|---|
/v1/chat/completions | OpenAI: {"error": {"message": "...", "type": "..."}} |
/v1/messages | Anthropic: {"type": "error", "error": {"type": "...", "message": "..."}} |
message jest dla ludzi i może się zmienić. Logikę opieraj na kodzie HTTP i polu type.
Typy błędów
| HTTP | type | Znaczenie | Co zrobić |
|---|---|---|---|
| 400 | invalid_request | Źle zbudowane zapytanie, np. uszkodzony JSON albo brak model | Popraw zapytanie |
| 401 | authentication_error | Brak klucza, zły format albo klucz usunięty | Sprawdź klucz i nagłówek, patrz Uwierzytelnianie |
| 402 | insufficient_balance | Dostępne saldo niższe niż koszt albo blokada | Doładuj saldo |
| 403 | permission_error | Klucz osiągnął miesięczny limit wydatków. Komunikat: This key reached its monthly spend limit. Create a new key with a higher limit in the dashboard or use another key. | Utwórz nowy klucz z wyższym limitem albo użyj innego klucza |
| 404 | not_found | Nieznane id modelu albo zadania | Sprawdź id w Modelach |
| 422 | invalid_request | input nie pasuje do input_schema modelu | Przeczytaj komunikat, wskazuje pole |
| 429 | rate_limit | Za dużo zapytań na ten klucz | Zwolnij i ponów z rosnącym odstępem |
| 429 | rate_limit_error | 25 nieudanych generacji na koncie w ciągu ostatnich 24 h; nowe zlecenia /v1/generate są wstrzymane | Poczekaj tyle sekund, ile podaje nagłówek Retry-After, i popraw prompt albo parametry |
| 502 | upstream_error | Dostawca modelu zwrócił błąd | Ponów po krótkiej przerwie |
| 504 | timeout | Model nie odpowiedział na czas | Ponów, przy czacie rozważ krótszą odpowiedź |
Limity zapytań
Domyślne limity na klucz API:
| Endpoint | Limit |
|---|---|
POST /v1/generate | 60 zapytań na minutę |
/v1/chat/completions, /v1/messages | 600 zapytań na minutę |
POST /v1/generate, nieudane generacje | 25 na konto w ciągu 24 h (okno przesuwne), potem 429 rate_limit_error z nagłówkiem Retry-After |
Po przekroczeniu limitu dostajesz 429 rate_limit. Rozłóż zapytania w czasie albo daj osobne klucze osobnym zadaniom.
Nieudane zadanie to nie błąd HTTP
Generowanie, które wystartowało i potem się wysypało, nie daje błędu HTTP. POST /v1/generate zwraca 202, a później zadanie ma status: "failed" i obiekt error:
{ "id": "task_3xq1...", "status": "failed", "error": { "type": "upstream_error", "message": "..." }, "cost": { "amount": "0.00", "currency": "PLN" } }Blokada wraca na saldo, płacisz 0. Sprawdzaj status każdego zakończonego zadania, patrz Zadania.
Kiedy ponawiać
Ponawiaj 429, 502 i 504 z wykładniczo rosnącym odstępem. Nie ponawiaj 400, 401, 402, 403, 404 ani 422 bez zmiany czegoś po swojej stronie, bo to samo zapytanie skończy się tak samo.
Nie ponawiaj w pętli zadań, które kończą się status: "failed". Nieudane generacje nic nie kosztują, ale po 25 porażkach w ciągu doby konto dostaje 429 rate_limit_error i nowe zlecenia czekają, aż minie czas z nagłówka Retry-After. Zanim ponowisz, zmień prompt albo parametry.
import os, random, time, requests
RETRY = {429, 502, 504}
def call(method: str, url: str, **kwargs) -> requests.Response:
headers = {"Authorization": f"Bearer {os.environ['JANTARIS_API_KEY']}"}
for attempt in range(5):
r = requests.request(method, url, headers=headers, timeout=130, **kwargs)
if r.status_code not in RETRY:
break
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://jantaris.com/v1/generate",
json={"model": "gpt-image-2.5", "input": {"prompt": "Czerwony rower"}}).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.JANTARIS_API_KEY}`, "Content-Type": "application/json", ...init.headers },
});
if (!RETRY.has(res.status)) break;
await sleep(Math.min(2 ** attempt * 1000, 30000) + Math.random() * 1000);
}
if (!res.ok) {
const { error } = await res.json();
throw new Error(`${res.status} ${error?.type}: ${error?.message}`);
}
return res.json();
}SDK OpenAI i Anthropic mają ponowienia wbudowane. Oba domyślnie kilka razy ponawiają limity i błędy serwera, a liczbę prób ustawiasz przez max_retries (Python) albo maxRetries (JavaScript).
from openai import OpenAI
client = OpenAI(base_url="https://jantaris.com/v1", api_key="sk-jt-...", max_retries=4)Bezpieczne ponawianie /v1/generate
Każde udane wywołanie /v1/generate tworzy nowe zadanie i blokuje pod nie środki. Jeśli zapytanie przekroczyło czas po twojej stronie i nie wiesz, czy doszło do serwera, nie wysyłaj go w pętli na ślepo. Zapisz sytuację w logach, a jeśli korzystasz z webhooków, poczekaj na doręczenie, zanim uruchomisz duplikat.









