To retry astrology API requests safely, send a stable Idempotency-Key header that stays identical across every retry of the same logical operation, and only retry on transient failures (429 and 5xx) using exponential backoff with jitter. On Vedika, a duplicate request carrying a key the server has already seen returns the original stored result instead of running a second billed computation — so a flaky network never turns one chart into two charges. This guide shows the exact request shapes, a retry policy that won't double-charge, and the edge cases that bite teams in production.
Why retries are dangerous for a billed astrology API
Every call to the Vedika API costs money — roughly $0.01 to $0.05 per query depending on the path and plan, drawn from your wallet balance. There is no free tier on production keys; the free sandbox exists precisely so you can build retry logic without spending anything. That billing model makes naive retries risky. The classic failure looks like this:
- Your client sends
POST /api/v1/astrology/query. - Vedika computes the chart, runs the AI interpretation, debits the wallet, and sends the response.
- The response is lost on the way back — a dropped connection, a load-balancer timeout, a 504 from an intermediary proxy.
- Your HTTP client sees a failure and retries.
- Without protection, the server treats the retry as a brand-new request: it computes again and debits again.
The work succeeded the first time; only the acknowledgement was lost. Charging twice for one user action is the single most common integration bug we see, and it is entirely preventable.
Idempotency keys: one logical request, one charge
An idempotency key is a client-generated identifier you attach to a request so the server can recognise duplicates. The contract is simple: the first time the server sees a key, it does the work and remembers the outcome against that key. Any later request carrying the same key — within the retention window — gets the stored response replayed back, with no recomputation and no second debit.
Generate the key once, reuse it on every retry
The mistake that defeats the whole mechanism is generating a fresh key inside the retry loop. The key must be created once per logical operation and reused across every transport-level retry of that operation. A new key is only appropriate when the user genuinely wants a new query.
KEY=$(uuidgen)
curl -sS https://api.vedika.io/api/v1/astrology/query \
-H "x-api-key: vk_live_YOUR_API_KEY" \
-H "Idempotency-Key: $KEY" \
-H "Content-Type: application/json" \
-d '{
"question": "When is a favourable window for changing jobs?",
"birthDetails": {
"datetime": "1991-03-14T07:25:00",
"latitude": 18.5204,
"longitude": 73.8567,
"timezone": "Asia/Kolkata"
}
}'
If that curl fails with a network error, rerun the exact same command with the same $KEY. If the first attempt actually reached the server and completed, the second call returns the same answer and your wallet is debited once. If the first attempt never landed, the second call runs normally. Either way you pay for exactly one chart.
What makes a good key
- Unique per operation. A UUID (v4) is the safe default. Tie it to the user action that triggered the request — a button press, a queued job — not to wall-clock time.
- Stable across retries. Persist it alongside the in-flight operation so a process restart or a worker handoff reuses the same value.
- Not derived from the payload alone. Two legitimately distinct queries can carry identical birth details; let the key represent the intent to run once, not the inputs.
Which failures are safe to retry
Idempotency protects you from double-charging, but it doesn't tell you when to retry. Retry only on transient conditions. Hammering the API on a permanent error wastes quota and delays the real fix.
| Status | Meaning | Retry? |
|---|---|---|
| 200 | Success | No — you're done |
| 400 / 422 | Malformed request or invalid birth details | No — fix the payload first |
| 401 | Bad or missing API key | No — check the x-api-key header |
| 402 | Insufficient wallet balance | No — top up, then send a fresh request |
| 429 | Rate limited | Yes — honour Retry-After, then back off |
| 502 / 503 / 504 | Transient server or gateway error | Yes — back off and retry with the same key |
A subtle case: a 4xx that you retry with the same idempotency key still won't succeed, because the request body is the problem, not the network. Reserve retries for 429 and 5xx, and resolve 4xx by correcting input.
Exponential backoff with jitter
Immediate retries make outages worse — every client reconnecting in lockstep produces a thundering herd. Space attempts out with exponential backoff, and add randomised jitter so clients don't synchronise. A small, bounded loop is enough.
// Generic fetch client — swap in your own HTTP library as needed.
import { randomUUID } from 'node:crypto';
async function queryWithRetry(body, { maxRetries = 4 } = {}) {
const idempotencyKey = randomUUID(); // once per logical operation
for (let attempt = 0; attempt <= maxRetries; attempt++) {
const res = await fetch('https://api.vedika.io/api/v1/astrology/query', {
method: 'POST',
headers: {
'x-api-key': process.env.VEDIKA_API_KEY,
'Idempotency-Key': idempotencyKey,
'Content-Type': 'application/json',
},
body: JSON.stringify(body),
});
if (res.ok) return res.json();
const retryable = res.status === 429 || res.status >= 500;
if (!retryable || attempt === maxRetries) {
throw new Error(`Vedika query failed: ${res.status}`);
}
// Honour Retry-After on 429; otherwise exponential backoff + jitter.
const headerWait = Number(res.headers.get('retry-after')) * 1000;
const backoff = Math.min(2 ** attempt * 500, 8000);
const jitter = Math.random() * 250;
await new Promise(r => setTimeout(r, (headerWait || backoff) + jitter));
}
}
queryWithRetry({
question: 'Is this a good year for property investment?',
birthDetails: {
datetime: '1991-03-14T07:25:00',
latitude: 18.5204,
longitude: 73.8567,
timezone: 'Asia/Kolkata',
},
}).then(console.log);
The key is created outside the loop, so all four attempts share it. The backoff caps at eight seconds; on a 429 it defers to the server's Retry-After value when present.
Python equivalent
import os, time, uuid, random, requests
def query_with_retry(body, max_retries=4):
key = str(uuid.uuid4()) # once per logical operation
headers = {
"x-api-key": os.environ["VEDIKA_API_KEY"],
"Idempotency-Key": key,
"Content-Type": "application/json",
}
for attempt in range(max_retries + 1):
r = requests.post(
"https://api.vedika.io/api/v1/astrology/query",
json=body, headers=headers, timeout=30,
)
if r.ok:
return r.json()
if r.status_code != 429 and r.status_code < 500:
r.raise_for_status() # 4xx — don't retry
if attempt == max_retries:
r.raise_for_status()
wait = float(r.headers.get("retry-after", 0)) or min(2 ** attempt * 0.5, 8)
time.sleep(wait + random.random() * 0.25)
Computation endpoints, streaming, and where idempotency matters most
Vedika exposes 700+ operations across 25 domains (704 enumerated as of June 2026), and the retry strategy differs by endpoint class.
- AI query —
POST /api/v1/astrology/queryis the billed, interpretation-heavy path where double-charging is the real risk. Always attach an idempotency key here. - V2 computation — the
/v2/astrology/*family takes flatdatetime,latitude,longitude,timezonefields and returns deterministic chart math (positions, dashas, divisional charts). These are cheap and pure; retrying is low-risk, though keys still keep your billing tidy. - Streaming —
POST /api/v1/astrology/query/streamemits Server-Sent Events. Because the output is incremental, you can't simply replay a partial stream. On disconnect, reconcile on your side or restart; for billing-critical calls prefer the non-streaming endpoint with a key, then render the full payload when it lands.
All paths authenticate with x-api-key: vk_live_* over the base URL https://api.vedika.io. The same three astrology systems — Vedic sidereal, Western tropical, and KP — are served from one API, so a single idempotency discipline covers every system you call.
A note on determinism
Chart mathematics is deterministic: the underlying XALEN Ephemeris (Vedika's own open-source engine, validated against JPL DE440 and swetest with no chart deviating beyond 0.1° across a five-million-chart test) returns identical planetary positions for identical inputs. That ephemeris precision means a retried computation endpoint yields byte-stable geometry. The AI interpretation layer, by contrast, is what you pay per call for and what idempotency keys protect — a replayed key returns the same stored interpretation, not a freshly generated one.
Key facts
- Attach a stable
Idempotency-Keyheader to every billed request; reuse it across all retries of the same logical operation. - The server replays the stored response for a duplicate key within the retention window — one charge, not two.
- Retry only on 429 and 5xx; never retry 400, 401, 402, or 422.
- Use exponential backoff capped at a few seconds, plus random jitter, and honour
Retry-Afteron 429. - Generate the key once per operation — a fresh key per retry defeats the protection.
- Prototype your retry loop for free against the no-key sandbox before pointing at
vk_live_*. - Per-query cost runs $0.01–$0.05; plans start at $12/mo — see pricing.
Putting the pieces in place
Safe retries come down to two independent decisions. Idempotency answers “what if the work already happened?” — solved by a stable client key. Retry policy answers “when should I try again?” — solved by classifying the status code and backing off. Get both right and a lost response, a 503 during a deploy, or a momentary rate limit becomes a non-event for your users and your wallet. Build the loop once against the sandbox, confirm a deliberately repeated key returns one charge, and ship it. For the full endpoint catalogue and request schemas, see the API documentation.