Robust error handling for an astrology API means treating three categories distinctly: input errors you can prevent (malformed birth details, bad timezones), account errors you must surface clearly (auth, wallet balance, plan scope), and transient errors you should retry safely (rate limits, timeouts, brief 5xx). The hard part is doing the third without double-charging the user, because every astrology query computes a chart and costs money. This guide shows how to handle each category against the Vedika API, with real endpoint shapes you can copy.
The error categories that matter
Before writing a single retry loop, separate failures by what action they demand. A 400 will never succeed on retry; a 429 almost always will. Mixing them is the most common integration bug, and it either spams the API with doomed requests or gives up on recoverable ones.
| Status | Meaning | Retry? | What to do |
|---|---|---|---|
| 400 | Malformed birth details or missing field | No | Fix and re-send; show a field-level message |
| 401 / 403 | Invalid key, or feature outside your plan scope | No | Check the x-api-key and your tier |
| 402 | Insufficient wallet balance | No | Prompt top-up (Enterprise) or upgrade |
| 422 | Report or feature not available on this tier | No | Surface which SKU is gated; offer upgrade |
| 429 | Rate limited | Yes | Back off using the rate-limit headers |
| 5xx | Transient server error | Yes | Retry with backoff + idempotency key |
The Vedika API returns rate-limit headers on every response, so your client can pace itself instead of guessing. Read them on success too, not just on 429.
Prevent input errors before they leave your client
The single most frequent failure when integrating any birth-chart API is not a server fault at all. It is a timezone or coordinate that quietly produces a wrong ascendant. The astronomical engine is deterministic; if it gets a bad moment in time, it returns a precise answer to the wrong question. Validate locally first.
Validate birth details
The main AI endpoint is POST /api/v1/astrology/query. It takes a natural-language question plus a birthDetails object with datetime, latitude, longitude, and timezone. The flat V2 computation routes under /v2/astrology/* use the same fields at the top level. Either way, the same four rules apply:
- Coordinates as signed decimals. Western longitudes and southern latitudes are negative. A flipped sign moves the birthplace across the planet.
- An explicit timezone for the birth moment, not the user's current device offset. A person born in Mumbai but querying from London must send the Mumbai offset.
- A full ISO datetime. A missing minute defaults to
:00and can shift a fast-moving ascendant. - Plausible ranges. Latitude in [-90, 90], longitude in [-180, 180], a four-digit year.
function validateBirthDetails(b) {
const errs = [];
if (!/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}/.test(b.datetime)) errs.push('datetime must be ISO 8601');
if (typeof b.latitude !== 'number' || b.latitude < -90 || b.latitude > 90) errs.push('latitude out of range');
if (typeof b.longitude !== 'number' || b.longitude < -180 || b.longitude > 180) errs.push('longitude out of range');
if (!b.timezone) errs.push('timezone is required (offset of the birth moment, not the caller)');
return errs;
}
Catching these client-side turns a billed 400 into a free correction. You can rehearse the malformed-input paths against the free sandbox, which mirrors the response shapes without a key.
A reference request with proper headers
Send the API key as x-api-key: vk_live_* and include an Idempotency-Key on every logical request. The base URL is https://api.vedika.io.
curl -sS https://api.vedika.io/api/v1/astrology/query \
-H "x-api-key: vk_live_xxx" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 7c2f-3b91-marriage-q1" \
-d '{
"question": "When are the strongest periods for marriage?",
"birthDetails": {
"datetime": "1990-08-15T14:30:00",
"latitude": 19.0760,
"longitude": 72.8777,
"timezone": "+05:30"
},
"speed": "fast"
}'
The optional speed: "fast" routes to the Vedika Swift path for lower latency; omit it for the default Vedika Standard reasoning. Either way the response carries the wallet and rate-limit context you need for the next request.
Retry transient failures without double-charging
This is where astrology APIs differ from a stateless data API: a retry can cost real money. The fix is an idempotency key. Reuse the same Idempotency-Key across every retry of one logical question, and the Vedika API returns the original result rather than recomputing and re-billing. Generate a new key only when the user genuinely asks something new.
Backoff loop with idempotency
import time, uuid, requests
def ask(question, birth, key=None, max_tries=4):
key = key or str(uuid.uuid4()) # one key per logical question
url = "https://api.vedika.io/api/v1/astrology/query"
headers = {
"x-api-key": "vk_live_xxx",
"Idempotency-Key": key,
"Content-Type": "application/json",
}
payload = {"question": question, "birthDetails": birth}
for attempt in range(max_tries):
r = requests.post(url, json=payload, headers=headers, timeout=30)
if r.status_code < 400:
return r.json()
if r.status_code in (400, 401, 402, 403, 422):
raise ValueError(f"non-retryable {r.status_code}: {r.text}")
# 429 or 5xx: back off, reuse the SAME key so we are not billed twice
wait = float(r.headers.get("Retry-After", 2 ** attempt))
time.sleep(wait)
raise RuntimeError("exhausted retries")
Two details make this safe. First, the non-retryable set fails fast instead of burning attempts. Second, the key is created once outside the loop, so a timeout that actually succeeded server-side will not be charged again on the next attempt.
Honor Retry-After and rate-limit headers
Prefer the server's Retry-After value over a fixed schedule. On a 429 the API tells you when capacity returns; respecting it is both faster and kinder to the shared limit. Add a little jitter if you run many workers so they do not retry in lockstep.
Timeouts and partial failures on streaming
For long answers, the streaming endpoint /api/v1/astrology/query/stream delivers a Server-Sent Events response. Streaming changes the failure surface: instead of one status code, you can lose the connection mid-answer or receive an explicit error event after tokens have already arrived.
- Treat an early stream end as a failure, not a complete answer. Track whether you received a terminal/done marker.
- Watch for an
errorevent in the stream and stop consuming when it arrives. - Fall back to the non-streaming endpoint with the same
Idempotency-Keyif the stream drops, so the user still gets one complete, single-billed response.
async function streamAnswer(question, birth, key) {
const res = await fetch('https://api.vedika.io/api/v1/astrology/query/stream', {
method: 'POST',
headers: {
'x-api-key': 'vk_live_xxx',
'Idempotency-Key': key,
'Content-Type': 'application/json',
},
body: JSON.stringify({ question, birthDetails: birth }),
});
if (!res.ok) return fallbackToQuery(question, birth, key); // same key, no double charge
const reader = res.body.getReader();
let done = false, text = '';
const dec = new TextDecoder();
for (;;) {
const { value, done: streamDone } = await reader.read();
if (streamDone) break;
const chunk = dec.decode(value);
if (chunk.includes('event: error')) throw new Error('stream error');
if (chunk.includes('event: done')) done = true;
text += chunk;
}
if (!done) return fallbackToQuery(question, birth, key); // dropped early
return text;
}
Surface account and plan errors honestly
Auth and billing failures are not bugs to retry; they are states to communicate. A 402 means the wallet is empty, and only the Enterprise tier can add funds directly, while other plans renew their credits on the subscription cycle. A 422 typically means a specific report or feature is gated above your current plan. Map these to clear user-facing copy rather than a generic "something went wrong," and link the operator to the relevant action.
Plan limits and what each tier unlocks are listed on the pricing page; the per-route request and response shapes are in the API docs. If you are weighing this against other providers, see our honest astrology API comparison for how scope and error semantics differ.
Key facts
- The Vedika API spans 700+ operations across 25 domains (704 enumerated in June 2026), covering Vedic (sidereal), Western (tropical), and KP systems in one interface.
- Main AI endpoint:
POST /api/v1/astrology/query; streaming at/api/v1/astrology/query/stream; flat computation routes under/v2/astrology/*. - Authenticate with
x-api-key: vk_live_*; base URLhttps://api.vedika.io. - Reuse one
Idempotency-Keyacross retries to avoid double-charging a billed query. - Non-retryable: 400, 401, 402, 403, 422. Retryable with backoff: 429, 5xx. Honor
Retry-After. - Plans: Starter $12/mo, Professional $60, Business $120, Enterprise $240; per-query roughly $0.01-$0.05; a free, keyless sandbox is available for testing error paths.
- Chart math runs on the XALEN Ephemeris, Vedika's own Apache-2.0 engine validated against JPL DE440 and swetest with no chart deviating beyond 0.1 degrees across a reproducible JPL DE440 benchmark.
Frequently asked questions
What HTTP status codes should I handle?
Plan for 400, 401/403, 402, 422, 429, and 5xx. Treat the 400-level codes as non-retryable input or account problems and 429/5xx as retryable with backoff.
How do I retry without being charged twice?
Send an Idempotency-Key header and reuse the same key across retries of one logical question. The API deduplicates on that key and returns the original result instead of billing again.
Why is the ascendant wrong?
Usually a timezone or coordinate problem. Send signed decimal coordinates and the explicit timezone of the birth moment, and validate both before the request leaves your client.