# AGENTS.md — Occult API

Machine-readable brief for coding agents. If you are writing code against
Occult API, read this before you write the first request.

Human version: https://occultapi.com/agents
OpenAPI 3.1 spec: https://occultapi.com/openapi.json
Endpoint index for LLMs: https://occultapi.com/llms.txt (full detail: https://occultapi.com/llms-full.txt)

## The API in six lines

- Base URL: `https://api.occultapi.com`
- Auth: `X-API-Key: yt_live_...` header on every request. No OAuth.
- Almost everything is `POST` with a JSON body. 309 endpoints.
- Every JSON response: `{ data, status, is_error, message }`. Branch on `is_error`.
- One credit per SUCCESSFUL call. Errors cost nothing.
- Response headers report spend: `X-Credits-Charged`, `X-Credits-Remaining`.

## Rule 1 — Batch with `keys`, do not loop

Many endpoints take a `keys` array naming which calculations to return. Every
key in one request is covered by the SAME single credit.

```json
{
  "date_time": "2026-09-16T06:00:00+05:30",
  "latitude": 26.9124,
  "longitude": 75.7873,
  "timezone_as_float": 5.5,
  "keys": ["tithi", "nakshatra", "yogam", "karana", "sunrise"]
}
```

Calling the same endpoint once per key costs five credits for what one credit
buys. If you need several calculations for one moment and place, put them in
one request. Valid keys are listed per endpoint in the OpenAPI spec under the
operation description.

## Rule 2 — Do not retry a 402, and do not regenerate the key

Error codes arrive as a stable string in `message`. Treat them differently:

| Status | `message` | Retry? | Action |
|---|---|---|---|
| 400 | (field errors) | No | Fix the body. `message` names each bad field. |
| 401 | `invalid_api_key` | No | The key is wrong/revoked. Do not loop. |
| 402 | `insufficient_credits` | No | Balance is empty. The KEY IS FINE — regenerating it does nothing. Stop and tell the user to recharge. |
| 403 | `endpoint_not_allowed` | No | Not enabled for API keys. |
| 403 | `scope_not_allowed` | No | Key lacks the scope. |
| 429 | `api_key_rate_limited` | Yes | Wait the `Retry-After` seconds, then retry. |
| 5xx | — | Yes | Back off exponentially. Never charged. |

Only 429 and 5xx are retryable. Retrying anything else burns wall-clock and
fixes nothing.

## Rule 3 — The key is server-side only

Never put `yt_live_...` in browser JavaScript, a mobile bundle, a React client
component, or a committed file. Read it from an environment variable and call
the API from a server route. If the user asks you to call it from the front
end, say why you are not going to and put the call behind their own endpoint.

## Rule 4 — Read the spec, do not guess field names

Fetch `https://occultapi.com/openapi.json` and use it. It carries every endpoint, every
field with its type and allowed values, and a VERIFIED example request and
response captured from a live server. Inventing a plausible field name produces
a 400 that costs nothing but wastes a turn; the spec is one fetch.

To generate a typed client rather than hand-writing one, that spec works with
openapi-generator, openapi-typescript, or your generator of choice.

## Working example

```bash
curl -X POST https://api.occultapi.com/api/astro/panchanga/ \
  -H "X-API-Key: $OCCULT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"date_time":"2026-09-16T06:00:00+05:30","latitude":26.9124,"longitude":75.7873,"timezone_as_float":5.5,"keys":["tithi","nakshatra"]}'
```

```typescript
async function occult<T>(route: string, body: unknown): Promise<T> {
  const response = await fetch(`https://api.occultapi.com${route}`, {
    method: "POST",
    headers: {
      "X-API-Key": process.env.OCCULT_API_KEY!,   // server-side only
      "Content-Type": "application/json",
    },
    body: JSON.stringify(body),
  });

  const json = await response.json();
  // Branch on is_error, not on response.ok — the envelope is the contract.
  if (json.is_error) throw new Error(`${response.status}: ${json.message}`);
  return json.data as T;
}
```

## Verified endpoints to start from

- `/api/astro/abhijit-muhurat/` — Abhijit muhurat
- `/api/astro/activities_day_of_week/` — Weekday activities
- `/api/astro/amrit-kaal/` — Amrit kaal
- `/api/astro/argala/` — Argala and virodhargala
- `/api/astro/ashtakoota/` — Ashtakoota matching

## Coverage

- AI & chat (1)
- PDF reports (2)
- Panchang (19)
- Muhurta & timings (15)
- Sunrise & moonrise (2)
- Lunar (3)
- Calendars (13)
- Festivals & vrats (3)
- Birth chart & houses (11)
- Divisional charts (23)
- Jaimini (5)
- KP (Krishnamurti Paddhati) (2)
- Chakras (1)
- Planets & positions (11)
- Strength (bala) (5)
- Ashtakavarga (5)
- Dashas — nakshatra (planet) (12)
- Dashas — rashi (Jaimini) (20)
- Dasha tools (6)
- Yogas & doshas (4)
- Lal Kitab (5)
- Matching & compatibility (6)
- Transits & annual charts (9)
- Tajika & varshaphala (11)
- Scans & transitions (8)
- Predictions & readings (9)
- Prashna (5)
- Avatar chakra (1)
- Tarot (4)
- Pancha Pakshi (2)
- Returns (5)
- Eclipses (8)
- Mundane astrology (10)
- Fixed stars (4)
- Traditional & Hellenistic (5)
- Western astrology (12)
- Numerology (15)
- Chinese astrology (4)
- Astrocartography (4)
- Human Design (3)
- Celebrity data (2)
- Natural hazards (5)
- Utilities (3)
- Astrology (2)
- prashnavali (4)

## If you can speak MCP

There is an MCP server (https://occultapi.com/mcp). It exposes five tools covering every
endpoint, and searching and reading schemas through it costs NO credits — only
an actual call does. If you are an agent with MCP support, prefer it: you can
explore the whole surface for free and only spend when you compute something.

## What this API does not do

It returns numbers and classical classifications. It writes no interpretive
prose, and nothing it returns is medical, legal or financial advice. If the
product you are building presents output as guidance, that is the user's
editorial decision — do not add a disclaimer in our name, and do not claim our
output is certified or endorsed.

## Testing without an account

https://occultapi.com/playground makes real calls with no signup and no key. Useful for
checking a response shape before you write code against it.
