# Authentication

Almost all AstroWay API requests authenticate via the `X-Api-Key` header. Exceptions: the 14 `/v1/reference/*` endpoints (signs, planets, houses, aspects, …) are public — no key required, capped at 30 requests / hour per IP.

<GetApiKeyBlock variant="compact" /> No other methods — no OAuth, JWT, or session cookies at the API level. Reference dictionaries `/v1/reference/*` are public and free (canonical lookup tables).

## Key format

```
aw_live_aB3xY7pQ9rN2mK4jH8vC5tL6wZ1fD0eR
aw_test_aB3xY7pQ9rN2mK4jH8vC5tL6wZ1fD0eR
```

- `aw_live_` — production, deducts credits from balance
- `aw_test_` — sandbox, returns stub responses (or minimal real calculations), **does not consume credits**, tracked separately in dashboard
- After the prefix: 32 base62 characters, cryptographically random
- Key is shown in plain text **only once** at creation. Dashboard shows only first and last 4 characters afterward.

## Request header

```http
POST /v1/chart HTTP/1.1
Host: api.astroway.info
X-Api-Key: aw_live_aB3xY7pQ9rN2mK4jH8vC5tL6wZ1fD0eR
Content-Type: application/json
```

## Postman collection

If Postman is more convenient than curl/SDK — there's a ready-made collection with all {siteMeta.endpoints} endpoints, grouped into 55 folders by OpenAPI tag, with example bodies pre-filled. Auth is configured at collection level: paste your key into the `apiKey` variable once and every request picks it up.

- **Published docs:** [documenter.getpostman.com/view/54689779/2sBXqNmy3n](https://documenter.getpostman.com/view/54689779/2sBXqNmy3n)
- **Public workspace:** [postman.com/astroway-info/astroway-api](https://www.postman.com/astroway-info/astroway-api)
- **Download JSON:** [astroway-api.json](/postman/astroway-api.json) (≈ 5 MB) — import directly into Postman or Insomnia

The collection is auto-generated from the [OpenAPI spec](https://api.astroway.info/v1/openapi.json) — refreshed on every deploy.

## Creating and managing keys

Via dashboard:
- [api.astroway.info/dashboard/keys](https://api.astroway.info/dashboard/keys)
- Up to **10 active keys** per account at once
- Each key has a label (e.g. `production-backend`, `ci-tests`, `local-dev`)

Via API (for self-service integrations):

<Tabs syncKey="lang">
  <TabItem label="cURL">
    ```bash frame="terminal"
    curl -X POST https://api.astroway.info/v1/keys \
      -H "X-Api-Key: aw_live_master_key" \
      -H "Content-Type: application/json" \
      -d '{"label": "ci-tests", "mode": "test"}'
    ```
  </TabItem>
  <TabItem label="Node.js">
    ```ts
    // Equivalent via @astroway/sdk: aw.client.POST('/keys', { body: { label, mode } })
    const r = await fetch('https://api.astroway.info/v1/keys', {
      method: 'POST',
      headers: {
        'X-Api-Key': process.env.ASTROWAY_MASTER_KEY!,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({ label: 'ci-tests', mode: 'test' }),
    });

    const { data: created } = await r.json();
    console.log(created.key); // shown once — store it now
    ```
  </TabItem>
  <TabItem label="Python">
    ```python
    # Equivalent via `astroway` (PyPI): aw.post('/keys', body={'label': ..., 'mode': ...})

    r = requests.post(
        'https://api.astroway.info/v1/keys',
        headers={'X-Api-Key': os.environ['ASTROWAY_MASTER_KEY']},
        json={'label': 'ci-tests', 'mode': 'test'},
    )
    created = r.json()['data']
    print(created['key'])  # shown once — store it now
    ```
  </TabItem>
  <TabItem label="PHP">
    ```php
    <?php
    use GuzzleHttp\Client;

    $aw = new Client(['base_uri' => 'https://api.astroway.info/v1/']);
    $r = $aw->post('keys', [
        'headers' => ['X-Api-Key' => getenv('ASTROWAY_MASTER_KEY')],
        'json' => ['label' => 'ci-tests', 'mode' => 'test'],
    ]);

    $created = json_decode($r->getBody(), true);
    echo $created['key']; // shown once — store it now
    ```
  </TabItem>
</Tabs>

<Aside type="tip">
The master key (the one you created in dashboard) can manage other keys via `/v1/keys`. Regular keys cannot. This is encoded in the key's `scope`.
</Aside>

## Best practices

### 1. Never put the key in frontend

The key is a backend-level secret. If it lands in JS that runs in the user's browser — it's visible in DevTools and will leak.

**Correct:** your backend holds the key, frontend calls your backend, your backend calls AstroWay API.

**Incorrect:**

```ts
// DO NOT DO THIS
fetch('https://api.astroway.info/v1/chart', {
  headers: { 'X-Api-Key': 'aw_live_...' }, // visible in browser
});
```

### 2. Environment variables, not hardcode

```bash
# .env (in .gitignore)
ASTROWAY_API_KEY=aw_live_...
```

```ts
// in code
const key = process.env.ASTROWAY_API_KEY;
if (!key) throw new Error('ASTROWAY_API_KEY is not set');
```

### 3. Separate keys per environment

- `production-backend` — production
- `staging-backend` — staging
- `ci-tests` — CI/CD pipeline
- `local-dev-alice` — local development per developer

If one key is compromised — revoke just that one, others keep working.

### 4. Rotation

Recommended frequency — **every 90 days**. Or immediately on suspected leak.

Zero-downtime rotation:
1. Create a new key with the same label + `-v2`
2. Deploy it to production (in parallel with old one)
3. Confirm traffic flows to new key (dashboard)
4. Revoke the old one

### 5. Monitor usage

Dashboard shows requests per key separately. If one key suddenly starts making 10× more requests than usual — investigate.

Set an alert: `Settings → Notifications → Alert when daily credits > X`.

## Authentication errors

| Code | Meaning |
|---|---|
| `401 Unauthorized` | `X-Api-Key` header missing or key invalid |
| `403 Forbidden` | Key valid but lacks scope (e.g. regular key managing other keys) |
| `402 Payment Required` | Credits exhausted (Free) or subscription inactive |
| `429 Too Many Requests` | Rate limit exceeded (10/60/300 req/min per plan) |

Example `401`:

```json
{
  "error": {
    "code": "invalid_api_key",
    "message": "The API key provided is invalid or has been revoked.",
    "request_id": "01HXY2A7ZM..."
  }
}
```

## Sandbox mode

Keys with `aw_test_` prefix run in sandbox:

- No credit consumption
- Return stub or minimal real responses
- Don't affect production statistics
- All endpoints available

Useful for CI/CD, e2e tests, and local development. Created in dashboard with one click.

## Next

- [Credits & Rate Limits](/en/rate-limits-credits/) — how billing and rate limiting work
- [Errors](/en/errors/) — full error code reference
