Błędy
Wszystkie błędy są zwracane w jednym formacie - ok: false plus obiekt error:
{ "ok": false, "error": { "code": "INVALID_API_KEY", "message": "Invalid API key" }}code: kodUPPER_SNAKE_CASEdo odczytu maszynowego, stabilny, nie zmienia się bez podbicia wersji major. Rób switch po nim, nie pomessage.message: czytelne dla człowieka wyjaśnienie, po angielsku. Tekst może być doprecyzowywany bez podbicia wersji.details: opcjonalne. DlaINVALID_INPUTto tablica{ path, message }dla każdego pola, które nie przeszło walidacji. DlaINVALID_FIELDdo każdego elementu dodawane jestexpectedz kanoniczną nazwą pola.docs: opcjonalne, link do sekcji tej strony, gdy dla kodu istnieje dokładniejsze wyjaśnienie.request_id: dodawane tylko do odpowiedzi5xxiBAD_JSON; podawaj go, zgłaszając się do wsparcia.
Kody HTTP
Dział zatytułowany „Kody HTTP”| Status | Znaczenie |
|---|---|
200 OK | Sukces |
400 Bad Request | Uszkodzony JSON, brakujące pola lub nieudana walidacja Zod |
401 Unauthorized | Brak lub nieprawidłowy X-Api-Key |
402 Payment Required | Potrzebny wyższy plan / pakiet albo subskrypcja jest nieaktywna |
403 Forbidden | Klucz jest prawidłowy, ale brak uprawnień (scope, origin, suspended) |
404 Not Found | Zasób, klucz lub job nie istnieje |
422 Unprocessable Entity | Wejście jest poprawne, ale silnik nie zdołał policzyć |
429 Too Many Requests | Przekroczono rate limit, wyczerpano kredyty lub spend-cap |
500 Internal Server Error | Błąd wewnętrzny - po naszej stronie, podaj request_id |
503 Service Unavailable | Bramka AI / kolejka chwilowo niedostępne |
Kody błędów
Dział zatytułowany „Kody błędów”Uwierzytelnianie (401)
Dział zatytułowany „Uwierzytelnianie (401)”| Kod | Znaczenie |
|---|---|
MISSING_API_KEY | Nie wysłano nagłówka X-Api-Key |
INVALID_API_KEY | Klucz nie istnieje lub został unieważniony |
MISSING_BEARER | Wybrano schemat Authorization: Bearer, ale brakuje tokenu |
WRONG_AUTH_METHOD | Na /v1/me/* (tylko Bearer) wysłano X-Api-Key. Do sprawdzenia klucza - GET /v1/auth/keys/me; do wywołań API - dowolny /v1/* z X-Api-Key |
Uprawnienia (403)
Dział zatytułowany „Uprawnienia (403)”| Kod | Znaczenie |
|---|---|
INSUFFICIENT_SCOPE | Klucz nie ma uprawnień do tej akcji |
DOMAIN_MISMATCH / FORBIDDEN_ORIGIN | Origin żądania nie jest na liście dozwolonych klucza |
ENDPOINT_NOT_IN_SCOPE | Klucz jest ograniczony listą endpointów (allowed_endpoints), a tej ścieżki na liście nie ma. details podaje listę |
KEY_SUSPENDED | Klucz jest zawieszony |
Rozliczenia (402)
Dział zatytułowany „Rozliczenia (402)”| Kod | Znaczenie |
|---|---|
PLAN_UPGRADE_REQUIRED | Endpoint wymaga wyższego planu lub pakietu add-on |
PLAN_PACK_MISMATCH | Endpoint należy do pakietu, którego nie ma na kluczu |
SUBSCRIPTION_EXPIRED | Opłacony okres skończył się ponad 3 dni temu. Przez pierwsze 3 dni po wygaśnięciu żądania są nadal obsługiwane według planu i niosą nagłówek X-Subscription-Grace-Ends. details zawiera expired_at, grace_ended_at i renew_url |
Rate limiting i kredyty (429)
Dział zatytułowany „Rate limiting i kredyty (429)”| Kod | Znaczenie |
|---|---|
RATE_LIMIT | Przekroczono limit RPM. Odpowiedź zawiera nagłówek Retry-After: <sec> |
CREDITS_EXHAUSTED | Miesięczny budżet kredytów wyczerpany |
SPEND_CAP_REACHED | Osiągnięto limit wydatków na endpointy AI |
KEY_BUDGET_EXHAUSTED | Klucz wyczerpał własny budżet (credits_cap_cycle). Kredyty na koncie mogą przy tym pozostać: ograniczenie dotyczy właśnie tego klucza |
Walidacja (400)
Dział zatytułowany „Walidacja (400)”| Kod | Znaczenie |
|---|---|
INVALID_INPUT | Jedno lub więcej pól nie przeszło walidacji Zod. details - tablica { path, message } |
INVALID_FIELD | Body zawiera krótką nazwę pola zamiast kanonicznej. Patrz niżej |
MISSING_FIELDS | Brakuje wymaganego pola |
BAD_JSON | Uszkodzony JSON w body żądania |
Przykład 400:
{ "ok": false, "error": { "code": "INVALID_INPUT", "message": "Validation failed: date: Invalid input: expected string, received undefined", "details": [ { "path": "date", "message": "Invalid input: expected string, received undefined" }, { "path": "time", "message": "Invalid input: expected string, received undefined" } ] }}INVALID_FIELD
Dział zatytułowany „INVALID_FIELD”Endpointy horoskopów przyjmują tylko pełne nazwy pól. Krótkie formy są odrzucane z 400, a nie czytane po cichu:
| Wysłano | Pole kanoniczne | Czego się oczekuje |
|---|---|---|
lat | latitude | stopnie dziesiętne, północ dodatnia |
lng, lon, long | longitude | stopnie dziesiętne, wschód dodatni |
tz z liczbą, tzOffset, utcOffset, timezone_offset | timezoneOffset | liczba godzin od UTC, np. 5.75 |
tz z nazwą, timeZone, time_zone | timezone | nazwa strefy IANA, np. Europe/Kyiv, albo auto |
Odpowiedź wymienia wszystkie znalezione pola naraz, w kolejności body żądania, więc zmianę nazw robi się w jednym przejściu:
{ "ok": false, "error": { "code": "INVALID_FIELD", "message": "Unsupported fields: \"tz\", \"lat\", \"lng\". Rename them to \"timezoneOffset\", \"latitude\", \"longitude\". See details for what each one expects.", "details": [ { "path": "tz", "expected": "timezoneOffset", "message": "numeric hours from UTC, e.g. 5.75; a zone name goes in timezone" }, { "path": "lat", "expected": "latitude", "message": "decimal degrees, north positive" }, { "path": "lng", "expected": "longitude", "message": "decimal degrees, east positive" } ], "docs": "https://api.astroway.info/en/errors/#invalid_field" }}path podaje pełną ścieżkę, więc w synastry widać, czyj to horoskop: chart1.lat, a nie po prostu lat.
Ten sam kod jest zwracany, gdy nie da się sparsować timezone: pusta wartość, skrót w rodzaju EST (to stałe -5 przez cały rok, więc Nowy Jork w lipcu wyjdzie o godzinę błędnie), nazwa, której nie ma w bazie stref czasowych, przesunięcie zapisane tekstem, jak +03:00, albo auto bez latitude i longitude. Komunikat podpowiada, co wysłać zamiast tego, a path wskazuje obiekt, na przykład chart2.timezone.
Dwie rzeczy, na które warto zwrócić uwagę.
To nie jest deprecjacja. Krótkie nazwy nigdy nie były w kontrakcie i nie figurowały w /openapi.json jako pola body. Do 2026-08-01 nie działały, tylko były po cichu ignorowane: współrzędne mają domyślnie zero, więc horoskop liczył się dla 0°N 0°E w UTC. Ascendent, domy i wszystko, co z nich wynika, było błędne, a odpowiedź o tym nie informowała. Dlatego krótkie formy nie wracają: dwie nazwy na jedno pole na zawsze, a tz jest do tego niejednoznaczne (liczba czy nazwa strefy).
Zagnieżdżony point to inna sprawa. W /v1/acg-zones, /v1/acg/by-category, /v1/ccg-analysis i /v1/eclipse-analysis obiekt point: { lat, lng } to zadeklarowane nazwy pól i one przechodzą.
Brakujące współrzędne: 400 od 2026-11-09
Dział zatytułowany „Brakujące współrzędne: 400 od 2026-11-09”Poprawna nazwa pola to połowa sprawy. Body bez latitude i longitude też dawało 200, bo współrzędne miały domyślnie zero, a odpowiedź opisywała 0°N 0°E.
Do 2026-11-09 takie żądanie działa, ale odpowiedź niesie Deprecation: true, Sunset, Warning i pole _warning w body. Od 2026-11-09 to 400 INVALID_INPUT z latitude is required, in decimal degrees.
Jawne latitude: 0, longitude: 0 pozostają poprawne: problem leży w brakującym polu, a nie w wartości. timezoneOffset zachowuje domyślne zero, bo zero oznacza tam UTC. city nie zastępuje współrzędnych: nikt go nie geokoduje. Sprawdzenie stosuje się tylko do obiektów, które niosą jednocześnie date i time.
Błędy obliczeniowe (422)
Dział zatytułowany „Błędy obliczeniowe (422)”| Kod | Znaczenie |
|---|---|
CALCULATION_ERROR | Wejście jest poprawne, ale silnik nie zdołał policzyć (na przykład szerokość polarna dla Placidusa). Spróbuj Whole Sign / Equal albo innej daty. |
Serwerowe
Dział zatytułowany „Serwerowe”| Kod | HTTP | Znaczenie |
|---|---|---|
INTERNAL_ERROR | 500 | Nieoczekiwany błąd. Logujemy ze stack trace, podaj request_id |
GATEWAY_UNAVAILABLE / AI_UNAVAILABLE / LLM_UNAVAILABLE | 503 | Bramka AI niedostępna. Retry z wykładniczym backoffem |
QUEUE_UNAVAILABLE | 503 | Kolejka zadań w tle niedostępna |
Obsługa w kodzie klienta
Dział zatytułowany „Obsługa w kodzie klienta”type ApiError = { ok: false; error: { code: string; message: string; details?: unknown; request_id?: string; };};
async function chart(input: ChartInput) { const res = await fetch('https://api.astroway.info/v1/chart', { method: 'POST', headers: { 'X-Api-Key': process.env.ASTROWAY_API_KEY!, 'Content-Type': 'application/json', }, body: JSON.stringify(input), });
const body = await res.json(); if (!body.ok) { throw new AstrowayError(body.error.code, body.error.message, body.error.request_id); }
return body.data;}
class AstrowayError extends Error { constructor( public code: string, message: string, public requestId?: string, ) { super(`[${code}] ${message}${requestId ? ` (request_id=${requestId})` : ''}`); }}Rób switch po code, nie po message. Oficjalne SDK (@astroway/sdk, astroway) rzucają typowane podklasy błędów według statusu HTTP (AuthenticationError, RateLimitError, BadRequestError …), więc kodu obsługi nie trzeba będzie pisać.
Zgłoszenie do wsparcia
Dział zatytułowany „Zgłoszenie do wsparcia”Zgłaszając się, wyślij na support@astroway.info:
request_idz odpowiedzi (jeśli to5xx)- Endpoint i skrócone body JSON (bez sekretów)
- Oczekiwane zachowanie vs faktyczne
- Strefę czasową i przybliżony czas żądania (UTC)
Odpowiedź - w ramach SLA Twojego planu (48 h Starter, 24 h Pro, indywidualne dla Enterprise).