Errors¶
Every error response uses the same envelope so clients can implement a single parser.
{
"error": {
"code": "invalid_request",
"message_key": "errors.common.invalid_request",
"message": "Request validation failed.",
"details": { ... },
"request_id": "019e32b5-..."
}
}
| Field | Type | Purpose |
|---|---|---|
code |
string | Stable, machine-readable identifier. Safe to switch on. |
message_key |
string | i18n key. Translate on the client; never display the raw value. |
message |
string | Plain-English fallback. Useful for logs, not for end users. |
details |
object | null | Error-specific structured payload. Schema documented per error. |
request_id |
string | null | Mirrors the X-Request-Id header for correlation with server logs / traces. |
Error catalog¶
error.code is stable; branch on it, not on message. Codes below are taken from the
source of the API. Endpoint pages list the codes specific to each route.
Authentication and access¶
| HTTP | code |
When |
|---|---|---|
| 401 | unauthorized |
Missing, malformed, expired or revoked credential. details.reason says which. |
| 401 | no_principal |
The route needs a caller and none was authenticated. |
| 403 | missing_scope |
The credential lacks the scope the route needs. details.required names it. |
| 403 | admin_scope_forbidden |
A non-admin tried to create a key with the admin scope. |
| 403 | plan_feature_unavailable |
The endpoint uses an LLM and your plan has none (Free). details.plan, details.feature. See Paid (LLM) endpoints. |
| 403 | plan_not_available |
Switching to a plan that is not self-service. |
| 403 | api_key_limit_reached |
Your plan's active-key limit is reached. details.plan, details.limit. Revoke a key first. |
Request problems¶
| HTTP | code |
When |
|---|---|---|
| 422 | invalid_request |
Body or query failed validation. details.errors[] has the field breakdown. |
| 422 | confirmation_required |
DELETE /v1/tenant/data without the exact confirmation phrase. |
| 422 | invalid_date_range, invalid_range, range_too_long, range_too_large, invalid_window |
Range bounds reversed, missing a timezone, or longer than the route allows. |
| 422 | invalid_datetime_timezone, naive_datetime, at_must_be_timezone_aware, event_date_must_be_timezone_aware, sunrise_must_be_timezone_aware |
A datetime without a timezone offset. |
| 422 | unsupported_astrology_system |
A system-specific route got a chart of the other system. |
| 422 | unsupported_house_system |
house_system is not supported by the route. |
| 422 | duplicate_relationship_charts |
Both sides of a relationship request are the same chart. |
| 422 | invalid_return_location |
Solar/lunar return relocation with only one coordinate. |
| 422 | invalid_plan_tier, invalid_scope |
Unknown plan tier or key scope. |
| 422 | unknown_planet, unknown_theme, house_number_out_of_range, unsupported_muhurta_activity_type |
A path or body value outside the allowed set. |
| 422 | rectification_grid_input_invalid |
The rectification grid request does not describe a valid window. |
| 422 | chart_missing_moon, moon_not_in_chart, missing_ascendant_or_moon |
The chart lacks a body the calculation needs. |
| 422 | <technique>_failed (e.g. panchanga_failed, muhurta_failed, varna_profile_failed, theo_house_strength_failed, vaishnava_calendar_failed) |
The calculation could not be done for these inputs (for example no sunrise at an extreme latitude). The message says why. |
| 501 | not_implemented |
The route exists but the feature is not available yet. |
Not found and conflicts¶
| HTTP | code |
When |
|---|---|---|
| 404 | chart_not_found |
Chart not found, or owned by another account. |
| 404 | report_not_found, compatibility_report_not_found, interpretation_not_found, feedback_not_found, api_key_not_found, tenant_not_found |
That object is not in your account. |
| 404 | http_error |
Some routes answer a missing object with this generic code and a message naming it (for example an unknown chart on /profile/varna or /compatibility/pair-facts, or an unknown statement_id on POST /v1/feedback). |
| 409 | idempotency_conflict |
Idempotency-Key reused with a different body. |
Limits¶
| HTTP | code |
When |
|---|---|---|
| 429 | rate_limited |
Burst bucket empty. Respect Retry-After. |
| 429 | too_many_requests |
Daily or monthly quota used up (details.reason). |
| 429 | llm_budget_exceeded |
Paid plan: hourly LLM spend cap reached. |
| 429 | llm_tier_rate_limited |
Too many LLM requests for one quality tier. |
| 429 | demo_rate_limit_exceeded, anonymous_auth_rate_limit_exceeded |
Too many anonymous demo or sign-in attempts from one address. |
Server and providers¶
| HTTP | code |
When |
|---|---|---|
| 500 | internal_error |
Unexpected failure. Reported automatically; include request_id if you contact us. |
| 503 | llm_all_providers_failed, llm_timeout, llm_unavailable, llm_rate_limited |
The LLM provider failed. Safe to retry later. |
| 503 | gazetteer_unavailable |
The place database is unavailable; let the user enter coordinates. |
Correlation IDs¶
Every response — error or success — echoes X-Request-Id. Pass that
header in your support tickets; server logs and OTel traces are keyed
on it.
If the client provides X-Request-Id on the inbound request the
server honours that value end-to-end. Otherwise a fresh UUIDv7 is
generated.
Idempotency¶
Mutating endpoints accept an optional Idempotency-Key header. The
first request with a given key is processed and its response cached;
subsequent requests with the same key return the cached response (HTTP
status included).
If a request with the same Idempotency-Key carries a different
body, the server returns HTTP 409 idempotency_conflict rather than
silently overwriting. Keys live for 24 hours.
Use idempotency for any client-driven retry path — most notably
POST /v1/reports and POST /v1/interpretations.