Erori
Toate erorile sunt returnate într-un format unic - ok: false plus obiectul error:
{ "ok": false, "error": { "code": "INVALID_API_KEY", "message": "Invalid API key" }}code: codUPPER_SNAKE_CASEcitibil de mașini, stabil, nu se schimbă fără o versiune major. Fă switch după el, nu dupămessage.message: explicație citibilă de oameni, în engleză. Textul poate fi precizat fără schimbare de versiune.details: opțional. PentruINVALID_INPUTeste un array{ path, message }pentru fiecare câmp picat. PentruINVALID_FIELD, fiecărui element i se adaugăexpectedcu numele canonic al câmpului.docs: opțional, un link către secțiunea acestei pagini, când pentru cod există o explicație mai detaliată.request_id: se adaugă doar la răspunsurile5xxșiBAD_JSON; citează-l când contactezi suportul.
Coduri HTTP
Section titled “Coduri HTTP”| Status | Semnificație |
|---|---|
200 OK | Succes |
400 Bad Request | JSON invalid, câmpuri lipsă sau validare Zod picată |
401 Unauthorized | X-Api-Key lipsă sau invalid |
402 Payment Required | E nevoie de un plan / pachet superior, sau abonamentul este inactiv |
403 Forbidden | Cheia este validă, dar nu are drepturi (scope, origin, suspended) |
404 Not Found | Resursa, cheia sau job-ul nu există |
422 Unprocessable Entity | Intrarea este validă, dar motorul nu a putut calcula |
429 Too Many Requests | Rate limit depășit, credite epuizate sau spend-cap atins |
500 Internal Server Error | Eroare internă - din partea noastră, citează request_id |
503 Service Unavailable | Gateway-ul AI / coada sunt temporar indisponibile |
Coduri de eroare
Section titled “Coduri de eroare”Autentificare (401)
Section titled “Autentificare (401)”| Cod | Semnificație |
|---|---|
MISSING_API_KEY | Antetul X-Api-Key nu a fost trimis |
INVALID_API_KEY | Cheia nu există sau a fost revocată |
MISSING_BEARER | Schema Authorization: Bearer a fost aleasă, dar tokenul lipsește |
WRONG_AUTH_METHOD | Pe /v1/me/* (doar Bearer) s-a trimis X-Api-Key. Pentru verificarea cheii - GET /v1/auth/keys/me; pentru apeluri API - orice /v1/* cu X-Api-Key |
Drepturi (403)
Section titled “Drepturi (403)”| Cod | Semnificație |
|---|---|
INSUFFICIENT_SCOPE | Cheia nu are drepturi pentru această acțiune |
DOMAIN_MISMATCH / FORBIDDEN_ORIGIN | Originea cererii nu este în allow-list-ul cheii |
ENDPOINT_NOT_IN_SCOPE | Cheia este limitată la o listă de endpoint-uri (allowed_endpoints), iar această cale nu e în listă. details numește lista |
KEY_SUSPENDED | Cheia este suspendată |
Facturare (402)
Section titled “Facturare (402)”| Cod | Semnificație |
|---|---|
PLAN_UPGRADE_REQUIRED | Endpoint-ul necesită un plan superior sau un pachet add-on |
PLAN_PACK_MISMATCH | Endpoint-ul face parte dintr-un pachet pe care cheia nu îl are |
SUBSCRIPTION_EXPIRED | Perioada plătită s-a încheiat acum mai mult de 3 zile. În primele 3 zile după expirare cererile sunt încă servite conform planului și poartă antetul X-Subscription-Grace-Ends. details conține expired_at, grace_ended_at și renew_url |
Rate limiting și credite (429)
Section titled “Rate limiting și credite (429)”| Cod | Semnificație |
|---|---|
RATE_LIMIT | Limita RPM a fost depășită. Răspunsul conține antetul Retry-After: <sec> |
CREDITS_EXHAUSTED | Bugetul lunar de credite este epuizat |
SPEND_CAP_REACHED | S-a atins limita de cheltuieli pentru endpoint-urile AI |
KEY_BUDGET_EXHAUSTED | Cheia și-a epuizat propriul buget (credits_cap_cycle). În cont pot rămâne credite: restricția se aplică doar acestei chei |
Validare (400)
Section titled “Validare (400)”| Cod | Semnificație |
|---|---|
INVALID_INPUT | Unul sau mai multe câmpuri au picat validarea Zod. details - array { path, message } |
INVALID_FIELD | Corpul conține un nume scurt de câmp în locul celui canonic. Vezi mai jos |
MISSING_FIELDS | Lipsește un câmp obligatoriu |
BAD_JSON | JSON invalid în corpul cererii |
Exemplu de 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
Section titled “INVALID_FIELD”Endpoint-urile de hartă acceptă doar nume complete de câmpuri. Formele scurte sunt respinse cu 400, nu citite pe tăcute:
| Trimis | Câmpul canonic | Ce se așteaptă |
|---|---|---|
lat | latitude | grade zecimale, nordul pozitiv |
lng, lon, long | longitude | grade zecimale, estul pozitiv |
tz cu număr, tzOffset, utcOffset, timezone_offset | timezoneOffset | număr de ore față de UTC, de ex. 5.75 |
tz cu nume, timeZone, time_zone | timezone | numele zonei IANA, de ex. Europe/Kyiv, sau auto |
Răspunsul enumeră toate câmpurile găsite dintr-odată, în ordinea din corpul cererii, deci redenumirea se face dintr-o singură trecere:
{ "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 indică calea completă, deci în synastry se vede exact a cui hartă: chart1.lat, nu doar lat.
Același cod este returnat când timezone nu poate fi interpretat: valoare goală, o abreviere precum EST (care înseamnă -5 fix tot anul, deci New York-ul în iulie ar ieși cu o oră greșit), un nume care nu există în baza fusurilor orare, un decalaj scris ca text, precum +03:00, sau auto fără latitude și longitude. Mesajul sugerează ce să trimiți în loc, iar path indică obiectul, de exemplu chart2.timezone.
Două lucruri de care merită să ții cont.
Nu este o depreciere. Numele scurte nu au fost niciodată în contract și nu au figurat în /openapi.json ca și câmpuri ale corpului. Până la 2026-08-01 nu funcționau, ci erau ignorate pe tăcute: coordonatele au implicit zero, deci harta se calcula pentru 0°N 0°E în UTC. Ascendentul, casele și tot ce derivă din ele erau greșite, iar răspunsul nu spunea asta. De aceea formele scurte nu sunt reacceptate: două nume pentru același câmp pentru totdeauna, iar tz e pe deasupra ambiguu (număr sau nume de zonă).
Un point imbricat este altceva. În /v1/acg-zones, /v1/acg/by-category, /v1/ccg-analysis și /v1/eclipse-analysis, obiectul point: { lat, lng } are nume de câmpuri declarate, iar acestea trec.
Coordonate lipsă: 400 de la 2026-11-09
Section titled “Coordonate lipsă: 400 de la 2026-11-09”Numele corect al câmpului este doar jumătate din problemă. Un corp fără latitude și longitude dădea și el 200, pentru că coordonatele aveau implicit zero, iar răspunsul descria 0°N 0°E.
Până la 2026-11-09 o astfel de cerere funcționează, dar răspunsul poartă Deprecation: true, Sunset, Warning și câmpul _warning în corp. De la 2026-11-09 este 400 INVALID_INPUT cu latitude is required, in decimal degrees.
latitude: 0, longitude: 0 explicite rămân valide: problema e câmpul lipsă, nu valoarea. timezoneOffset își păstrează zero-ul implicit, pentru că acolo zero înseamnă UTC. city nu înlocuiește coordonatele: nimeni nu îl geocodează. Verificarea se aplică doar obiectelor care conțin simultan date și time.
Erori de calcul (422)
Section titled “Erori de calcul (422)”| Cod | Semnificație |
|---|---|
CALCULATION_ERROR | Intrarea este validă, dar motorul nu a putut calcula (de exemplu, latitudine polară pentru Placidus). Încearcă Whole Sign / Equal sau altă dată. |
Erori de server
Section titled “Erori de server”| Cod | HTTP | Semnificație |
|---|---|---|
INTERNAL_ERROR | 500 | Eroare neașteptată. O logăm cu stack trace, citează request_id |
GATEWAY_UNAVAILABLE / AI_UNAVAILABLE / LLM_UNAVAILABLE | 503 | Gateway-ul AI este indisponibil. Retry cu backoff exponențial |
QUEUE_UNAVAILABLE | 503 | Coada sarcinilor de fundal este indisponibilă |
Tratarea în codul clientului
Section titled “Tratarea în codul clientului”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})` : ''}`); }}Fă switch după code, nu după message. SDK-urile oficiale (@astroway/sdk, astroway) aruncă subclase de erori tipizate după statusul HTTP (AuthenticationError, RateLimitError, BadRequestError …), deci nu va trebui să scrii cod de tratare.
Cerere de suport
Section titled “Cerere de suport”Pentru a ne contacta, trimite la support@astroway.info:
request_iddin răspuns (dacă este5xx)- Endpoint-ul și corpul JSON prescurtat (fără secrete)
- Comportamentul așteptat vs cel real
- Fusul orar și ora aproximativă a cererii (UTC)
Răspunsul vine în limitele SLA-ului planului tău (48 h Starter, 24 h Pro, propriu pentru Enterprise).