# Credits & Rate Limits

AstroWay uses a **credit-based model** instead of a simple request counter.

<GetApiKeyBlock variant="compact" /> This is fairer: lightweight endpoints (`/planets`) cost 10 credits, heavy ones (`/rectification`) — 500.

## Two kinds of limits

### 1. Monthly credit budget

Credits per month depend on your plan:

| Plan | Credits/mo | Rate limit |
|---|---|---|
| Free | 10,000 | 10 req/min |
| Indie | 50,000 | 30 req/min |
| Starter | 200,000 | 120 req/min |
| Pro | 800,000 | 400 req/min |
| Business | 3,500,000 | 1,000 req/min |
| Enterprise | Custom | Custom |

When credits run out — subsequent requests return `402 Payment Required` (Free) or trigger overage billing (Indie+).

### 2. Rate limit (requests per minute)

Rate limit uses a sliding window. If you fire 120 requests in 10 seconds, the next 50 seconds on that key return `429`.

<Aside type="tip">
Rate limit and credit budget are independent. You can have credits but fail rate limit. And vice versa.
</Aside>

### 3. Public endpoints (no key)

The 14 reference dictionaries `/v1/reference/*` (signs, planets, houses, aspects, elements, modalities, polarities, dignities, decans, nakshatras, lots, asteroids, zodiac-systems, glyphs) are served **publicly without an `X-Api-Key`** and cost **0 credits**. Limit — 30 requests / hour per IP. The same data is also exposed as MCP Resources at `astroway://reference/<slug>` for LLM agents.

## Pricing table

**Full transparent per-endpoint table** with all {siteMeta.endpoints}+ endpoints and their costs — at [Per-endpoint Cost](/en/credits/). DivineAPI / AstrologyAPI / Prokerala don't publish anything similar. Quick reference:

| Tier | Credits | Typical time | Examples |
|---|---|---|---|
| 1 | 10 | &lt; 50 ms | `/planets`, `/moon-voc`, `/iching` |
| 2 | 20 | 50–200 ms | `/chart`, `/harmonics`, `/horary` |
| 3 | 50 | 200–500 ms | `/synastry`, `/progressions`, `/acg` |
| 4 | 100 | &gt; 500 ms | `/transit-calendar`, `/forecast-calendar` |
| 5 | 250 | seconds | `/rectification/trutine` |
| 6 | 500 | up to 120s | `/rectification` |

## Response headers

Every response includes:

```http
X-Credits-Used: 20
X-Credits-Remaining: 9980
X-Credits-Limit: 10000
X-Credits-Reset: 2026-05-01T00:00:00Z
X-RateLimit-Limit: 120
X-RateLimit-Remaining: 118
X-RateLimit-Reset: 1714521600
X-Request-Id: 01HXY2A7ZM...
```

- `X-Credits-Used` — credits charged for this request
- `X-Credits-Remaining` — remainder of monthly budget
- `X-Credits-Limit` — full monthly credit budget (per plan)
- `X-Credits-Reset` — ISO-8601 balance reset date
- `X-RateLimit-*` — current sliding window state (standard format)
- `X-Request-Id` — ULID for this request, use in support requests

## Checking your usage — three ways

You can check your current credit balance three ways — from fastest to most detailed:

1. **On every response** — the `X-Credits-Remaining` / `X-Credits-Used` / `X-Credits-Limit` headers come back on **any** request (even errors). Nothing extra to call — your balance is always in front of you.
2. **On demand** — `GET /v1/keys/usage` with your `X-Api-Key` returns `credits` (used / remaining / limit / reset), `usage` (requests for today / week / month), and your top-20 endpoints over 30 days. One call, no separate admin key.
3. **In the dashboard** — [api.astroway.info/dashboard/billing](https://api.astroway.info/dashboard/billing) shows your balance, a usage chart, and a per-endpoint breakdown in real time.

```bash frame="terminal"
curl https://api.astroway.info/v1/keys/usage \
  -H "X-Api-Key: $AW_KEY"
```

<Aside type="tip">
Unlike most APIs (Anthropic, OpenAI), AstroWay returns your **remaining balance right in the header of every response** (`X-Credits-Remaining`) — no need to open a dashboard or a separate usage API to find out how much is left.
</Aside>

## Caching

Identical requests within 5 minutes return from cache **without consuming credits**. This is a separate header:

```http
X-Cache: HIT
X-Credits-Used: 0
```

What counts as "identical":
- Same endpoint
- Same JSON body (byte-for-byte after whitespace normalization)
- Same API key

This is safe because all endpoints are deterministic — same input always produces same output.

## Streaming and webhooks

### Streaming (`/stream/*`)

The 10 streaming endpoints are priced as **Tier 1 — 10 credits per call**. Each call recomputes planetary positions, so it is a live compute, not a cached lookup — there is no discounted "per-tick" rate.

Every response carries two fields for efficient polling:

- `tickSeconds` — recommended poll interval;
- `nextEventAt` — when the state next changes.

Polling faster than `tickSeconds` burns credits for nothing: the state won't change until `nextEventAt`.

<Aside type="caution">
Budget example: one stream polled every 60 s = 1,440 calls/day × 10 credits = 14,400 credits/day ≈ 3.5 days of one stream on Indie (50,000/mo). For continuous streaming, use Starter+.
</Aside>

### Webhooks (`/webhooks/*`)

Subscription-management CRUD calls (`/webhooks/subscribe`, list, get, delete, `/test`) cost **Tier 1 — 10 credits** each. These are made rarely.

**Webhook delivery (server → your URL) is not billed** — credits are charged only for the CRUD management calls. 12 event types are supported (`report-ready`, `transit-alert`, `eclipse-alert`, `dasha-change`, `sign-ingress`, etc.).

Webhooks are available on the **Pro plan and above**.

## What to do when limits are hit

### `402 Payment Required` — out of credits

```json
{
  "error": {
    "code": "credits_exhausted",
    "message": "Monthly credit budget exhausted. Upgrade plan or wait for reset.",
    "credits_remaining": 0,
    "credits_reset": "2026-05-01T00:00:00Z",
    "upgrade_url": "https://api.astroway.info/dashboard/billing"
  }
}
```

Options:
- **Upgrade plan** — immediate new budget
- **Overage** (Starter/Pro) — auto-enabled unless disabled in settings
- **Wait for reset** (Free) — 1st of next month

### `429 Too Many Requests` — RPM exceeded

```json
{
  "error": {
    "code": "rate_limit_exceeded",
    "message": "Too many requests. Retry after 12 seconds.",
    "retry_after_seconds": 12
  }
}
```

`Retry-After: 12` header — standard HTTP, your HTTP client should understand it.

**Correct handling:**

```ts
async function callWithRetry(endpoint: string, body: object) {
  const response = await fetch(endpoint, {
    method: 'POST',
    headers: { 'X-Api-Key': key, 'Content-Type': 'application/json' },
    body: JSON.stringify(body),
  });

  if (response.status === 429) {
    const retryAfter = Number(response.headers.get('Retry-After') ?? 1);
    await new Promise((r) => setTimeout(r, retryAfter * 1000));
    return callWithRetry(endpoint, body);
  }

  return response.json();
}
```

<Aside type="caution">
Don't do exponential backoff without a cap — the `Retry-After` header already contains the exact number. Trust it.
</Aside>

## Budget estimation in advance

Before integrating, estimate monthly credits:

```
monthly_credits = daily_active_users × calls_per_user × avg_cost_per_call × 30
```

Example: app with 500 DAU, each making 3 chart calculations + 1 synastry per day:

```
500 × (3 × 20 + 1 × 50) × 30 = 500 × 110 × 30 = 1,650,000 credits/mo
→ Enterprise (custom)
```

Budget calculator — on the [Pricing](/en/pricing/) page.

## Next

- [Errors](/en/errors/) — full error code reference
- [Idempotency](/en/idempotency/) — how to retry safely
