tutorial

Idempotency and safe retries for astrology APIs

How to retry astrology API requests safely without double-charging, using idempotency keys, exponential backoff, and stable client-generated identifiers in production.

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:

  1. Your client sends POST /api/v1/astrology/query.
  2. Vedika computes the chart, runs the AI interpretation, debits the wallet, and sends the response.
  3. The response is lost on the way back — a dropped connection, a load-balancer timeout, a 504 from an intermediary proxy.
  4. Your HTTP client sees a failure and retries.
  5. 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

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.

StatusMeaningRetry?
200SuccessNo — you're done
400 / 422Malformed request or invalid birth detailsNo — fix the payload first
401Bad or missing API keyNo — check the x-api-key header
402Insufficient wallet balanceNo — top up, then send a fresh request
429Rate limitedYes — honour Retry-After, then back off
502 / 503 / 504Transient server or gateway errorYes — 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.

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

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.

Build on the Vedika astrology API

700+ operations, Vedic + Western + KP, 30 languages, an open-source XALEN ephemeris, and a built-in LLM. Free sandbox — no signup.

Try the free sandbox