Errori
Tutti gli errori vengono restituiti in un formato unico: ok: false più un oggetto error:
{ "ok": false, "error": { "code": "INVALID_API_KEY", "message": "Invalid API key" }}code: codiceUPPER_SNAKE_CASEleggibile dalle macchine, stabile, non cambia senza un bump della versione major. Fai lo switch su di esso, non sumessage.message: spiegazione leggibile dalle persone, in inglese. Il testo può essere precisato senza bump.details: facoltativo. PerINVALID_INPUTè un array di{ path, message }per ogni campo non valido. PerINVALID_FIELDa ogni elemento si aggiungeexpectedcon il nome canonico del campo.docs: facoltativo, link alla sezione di questa pagina quando per il codice c’è una spiegazione più dettagliata.request_id: aggiunto solo alle risposte5xxeBAD_JSON; citalo quando contatti il supporto.
Codici HTTP
Sezione intitolata “Codici HTTP”| Stato | Significato |
|---|---|
200 OK | Successo |
400 Bad Request | JSON malformato, campi mancanti o validazione Zod fallita |
401 Unauthorized | X-Api-Key mancante o non valida |
402 Payment Required | Serve un piano o un pack superiore, oppure l’abbonamento non è attivo |
403 Forbidden | La chiave è valida ma mancano i permessi (scope, origin, sospensione) |
404 Not Found | La risorsa, la chiave o il job non esiste |
422 Unprocessable Entity | L’input è valido, ma il motore non è riuscito a calcolare |
429 Too Many Requests | Rate limit superato, crediti o spend cap esauriti |
500 Internal Server Error | Errore interno, dalla nostra parte: cita il request_id |
503 Service Unavailable | Gateway AI / coda temporaneamente non disponibili |
Codici di errore
Sezione intitolata “Codici di errore”Autenticazione (401)
Sezione intitolata “Autenticazione (401)”| Codice | Significato |
|---|---|
MISSING_API_KEY | L’header X-Api-Key non è stato inviato |
INVALID_API_KEY | La chiave non esiste o è stata revocata |
MISSING_BEARER | È stato scelto lo schema Authorization: Bearer, ma il token manca |
WRONG_AUTH_METHOD | Su /v1/me/* (solo Bearer) è stata inviata X-Api-Key. Per verificare la chiave usa GET /v1/auth/keys/me; per le chiamate API qualsiasi /v1/* con X-Api-Key |
Permessi (403)
Sezione intitolata “Permessi (403)”| Codice | Significato |
|---|---|
INSUFFICIENT_SCOPE | La chiave non ha i permessi per questa azione |
DOMAIN_MISMATCH / FORBIDDEN_ORIGIN | L’origin della richiesta non è nell’allow-list della chiave |
ENDPOINT_NOT_IN_SCOPE | La chiave è limitata a un elenco di endpoint (allowed_endpoints), e questo percorso non è nell’elenco. details riporta l’elenco |
KEY_SUSPENDED | La chiave è sospesa |
Fatturazione (402)
Sezione intitolata “Fatturazione (402)”| Codice | Significato |
|---|---|
PLAN_UPGRADE_REQUIRED | L’endpoint richiede un piano superiore o un pack add-on |
PLAN_PACK_MISMATCH | L’endpoint fa parte di un pack che la chiave non ha |
SUBSCRIPTION_EXPIRED | Il periodo pagato è terminato da più di 3 giorni. Nei primi 3 giorni dopo la scadenza le richieste vengono ancora servite secondo il piano e portano l’header X-Subscription-Grace-Ends. details contiene expired_at, grace_ended_at e renew_url |
Rate limiting e crediti (429)
Sezione intitolata “Rate limiting e crediti (429)”| Codice | Significato |
|---|---|
RATE_LIMIT | Limite RPM superato. La risposta contiene l’header Retry-After: <sec> |
CREDITS_EXHAUSTED | Budget mensile di crediti esaurito |
SPEND_CAP_REACHED | Raggiunto il limite di spesa per gli endpoint AI |
KEY_BUDGET_EXHAUSTED | La chiave ha esaurito il proprio budget (credits_cap_cycle). L’account può comunque avere ancora crediti: il limite riguarda solo questa chiave |
Validazione (400)
Sezione intitolata “Validazione (400)”| Codice | Significato |
|---|---|
INVALID_INPUT | Uno o più campi non hanno superato la validazione Zod. details è un array di { path, message } |
INVALID_FIELD | Il body contiene un nome di campo abbreviato invece di quello canonico. Vedi sotto |
MISSING_FIELDS | Manca un campo obbligatorio |
BAD_JSON | JSON malformato nel body della richiesta |
Esempio di 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
Sezione intitolata “INVALID_FIELD”Gli endpoint delle carte accettano solo i nomi completi dei campi. Le forme abbreviate vengono rifiutate con 400, non lette in silenzio:
| Inviato | Campo canonico | Cosa ci si aspetta |
|---|---|---|
lat | latitude | gradi decimali, nord positivo |
lng, lon, long | longitude | gradi decimali, est positivo |
tz con un numero, tzOffset, utcOffset, timezone_offset | timezoneOffset | numero di ore da UTC, ad es. 5.75 |
tz con un nome, timeZone, time_zone | timezone | nome di fuso IANA, ad es. Europe/Kyiv, oppure auto |
La risposta elenca tutti i campi trovati in una sola volta, nell’ordine del body della richiesta, quindi la rinomina si fa in un unico passaggio:
{ "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 indica il percorso completo, quindi nella sinastria si vede di quale carta si tratta: chart1.lat, non semplicemente lat.
Lo stesso codice viene restituito quando timezone non si riesce a interpretare: valore vuoto, un’abbreviazione come EST (che è un -5 fisso tutto l’anno, quindi New York a luglio risulterà sbagliata di un’ora), un nome che non esiste nel database dei fusi orari, uno scostamento scritto come testo, tipo +03:00, oppure auto senza latitude e longitude. Il messaggio suggerisce cosa inviare invece, e path indica l’oggetto, ad esempio chart2.timezone.
Due cose da tenere presenti.
Non è una deprecazione. I nomi abbreviati non sono mai stati nel contratto e non comparivano in /openapi.json come campi del body. Fino al 2026-08-01 non funzionavano, ma venivano ignorati in silenzio: le coordinate hanno zero come default, quindi la carta veniva calcolata per 0°N 0°E in UTC. L’ascendente, le case e tutto ciò che ne deriva erano sbagliati, e la risposta non lo segnalava. Per questo le forme abbreviate non vengono riaccettate: due nomi per un solo campo per sempre, e in più tz è ambiguo (numero o nome di fuso).
Il point annidato è un’altra cosa. In /v1/acg-zones, /v1/acg/by-category, /v1/ccg-analysis e /v1/eclipse-analysis l’oggetto point: { lat, lng } usa nomi di campo dichiarati, e passano.
Coordinate mancanti: 400 dal 2026-11-09
Sezione intitolata “Coordinate mancanti: 400 dal 2026-11-09”Il nome corretto del campo è metà del lavoro. Anche un body senza latitude e longitude dava 200, perché le coordinate avevano zero come default, e la risposta descriveva 0°N 0°E.
Fino al 2026-11-09 una richiesta del genere funziona, ma la risposta porta Deprecation: true, Sunset, Warning e il campo _warning nel body. Dal 2026-11-09 è un 400 INVALID_INPUT con latitude is required, in decimal degrees.
latitude: 0, longitude: 0 espliciti restano validi: il problema è il campo mancante, non il valore. timezoneOffset mantiene lo zero come default, perché lì zero significa UTC. city non sostituisce le coordinate: nessuno lo geocodifica. Il controllo si applica solo agli oggetti che portano contemporaneamente date e time.
Errori di calcolo (422)
Sezione intitolata “Errori di calcolo (422)”| Codice | Significato |
|---|---|
CALCULATION_ERROR | L’input è valido, ma il motore non è riuscito a calcolare (ad esempio, una latitudine polare con Placidus). Prova Whole Sign / Equal o un’altra data. |
Errori del server
Sezione intitolata “Errori del server”| Codice | HTTP | Significato |
|---|---|---|
INTERNAL_ERROR | 500 | Errore imprevisto. Lo registriamo con lo stack trace, cita il request_id |
GATEWAY_UNAVAILABLE / AI_UNAVAILABLE / LLM_UNAVAILABLE | 503 | Gateway AI non disponibile. Riprova con backoff esponenziale |
QUEUE_UNAVAILABLE | 503 | Coda dei job in background non disponibile |
Gestione nel codice client
Sezione intitolata “Gestione nel codice client”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})` : ''}`); }}Fai lo switch su code, non su message. Gli SDK ufficiali (@astroway/sdk, astroway) lanciano sottoclassi di errore tipizzate in base allo stato HTTP (AuthenticationError, RateLimitError, BadRequestError …), quindi il codice di gestione non dovrai scriverlo tu.
Richiesta di supporto
Sezione intitolata “Richiesta di supporto”Per contattarci, invia a support@astroway.info:
- Il
request_iddalla risposta (se è un5xx) - L’endpoint e il body JSON abbreviato (senza segreti)
- Comportamento atteso vs effettivo
- Fuso orario e ora approssimativa della richiesta (UTC)
La risposta arriva entro lo SLA del tuo piano (48 h Starter, 24 h Pro, personalizzato per Enterprise).