Errors
Every error comes back in a single shape — ok: false plus an error object:
{ "ok": false, "error": { "code": "INVALID_API_KEY", "message": "Invalid API key" }}code— machine-readableUPPER_SNAKE_CASEcode, stable, never changes without a major bump. Switch on this, not onmessage.message— human-readable explanation in English. Wording may be refined without a bump.details— optional. ForINVALID_INPUTit’s an array of{ path, message }for each failed field.request_id— added only to5xxandBAD_JSONresponses; quote it when contacting support.
HTTP status codes
Section titled “HTTP status codes”| Status | Meaning |
|---|---|
200 OK | Success |
400 Bad Request | Malformed JSON, missing fields, or failed Zod validation |
401 Unauthorized | Missing or invalid X-Api-Key |
402 Payment Required | A higher plan / add-on pack is required, or the subscription is inactive |
403 Forbidden | Key is valid but lacks permission (scope, origin, suspended) |
404 Not Found | Resource, key, or job does not exist |
422 Unprocessable Entity | Input is valid but the engine could not compute it |
429 Too Many Requests | Rate limit exceeded, credits exhausted, or spend cap hit |
500 Internal Server Error | Our fault — quote request_id |
503 Service Unavailable | AI gateway / queue temporarily down |
Error codes
Section titled “Error codes”Authentication (401)
Section titled “Authentication (401)”| Code | Meaning |
|---|---|
MISSING_API_KEY | The X-Api-Key header was not sent |
INVALID_API_KEY | Key does not exist or was revoked |
MISSING_BEARER | Authorization: Bearer scheme selected but token missing |
Permissions (403)
Section titled “Permissions (403)”| Code | Meaning |
|---|---|
INSUFFICIENT_SCOPE | Key lacks permission for this action |
DOMAIN_MISMATCH / FORBIDDEN_ORIGIN | Request origin is not in the key’s allow-list |
KEY_SUSPENDED | Key has been suspended |
Billing (402)
Section titled “Billing (402)”| Code | Meaning |
|---|---|
PLAN_UPGRADE_REQUIRED | Endpoint requires a higher plan or an add-on pack |
PLAN_PACK_MISMATCH | Endpoint is in a pack the key doesn’t have |
SUBSCRIPTION_EXPIRED | Subscription cancelled or expired |
Rate limiting & credits (429)
Section titled “Rate limiting & credits (429)”| Code | Meaning |
|---|---|
RATE_LIMIT | RPM limit exceeded. Response carries a Retry-After: <sec> header |
CREDITS_EXHAUSTED | Monthly credit budget used up |
SPEND_CAP_REACHED | AI-endpoint spend cap reached |
Validation (400)
Section titled “Validation (400)”| Code | Meaning |
|---|---|
INVALID_INPUT | One or more fields failed Zod validation. details is an array of { path, message } |
MISSING_FIELDS | A required field is missing |
BAD_JSON | Malformed JSON in the request body |
Example 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" } ] }}Calculation errors (422)
Section titled “Calculation errors (422)”| Code | Meaning |
|---|---|
CALCULATION_ERROR | Input is valid but the engine could not compute (e.g. a polar latitude for Placidus). Try Whole Sign / Equal or a different date. |
Server
Section titled “Server”| Code | HTTP | Meaning |
|---|---|---|
INTERNAL_ERROR | 500 | Unexpected error. Logged with a stack trace — quote request_id |
GATEWAY_UNAVAILABLE / AI_UNAVAILABLE / LLM_UNAVAILABLE | 503 | AI gateway down. Retry with exponential backoff |
QUEUE_UNAVAILABLE | 503 | Background-job queue down |
Handling in client code
Section titled “Handling in client code”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})` : ''}`); }}Switch on code, not on message. The official SDKs (@astroway/sdk, astroway) throw typed error subclasses by HTTP status (AuthenticationError, RateLimitError, BadRequestError, …), so you won’t need to write this yourself.
Contacting support
Section titled “Contacting support”To open a ticket, email support@astroway.info:
- The
request_idfrom the response (for5xx) - The endpoint and a trimmed JSON body (no secrets)
- Expected vs actual behavior
- Timezone and approximate request time (UTC)
Replies land within your plan’s SLA (48 h Starter, 24 h Pro, custom for Enterprise).
Was this helpful?
Thanks for the feedback.