# Authentication

> Authenticate with a Skylit API key, and how credits and rate limits work.

> **Beta.** The Skylit API and MCP server are in Beta with limited access. [Request access](https://www.skylit.ai/?waitlist=developer) and we'll let you know when yours opens.

The Skylit Public API uses **bearer authentication**. Send your API key in the
`Authorization` header on every request:

```bash
Authorization: Bearer <your-api-key>
```

> **Note:** REST endpoints read only the `Authorization` header. A request without it gets
> `401 Authorization field missing`; an invalid, revoked or expired key gets `403`.
> The MCP server uses the same header. `X-API-Key` and query-string keys are not
> read (`401`). The gateway also accepts the bare key without the `Bearer ` prefix;
> `Bearer` is the documented form.

> **Warning:** Treat API keys like passwords. Never commit them to source control or expose them in
> client-side code. Use environment variables and rotate keys if one leaks.

## Getting a key

Generate and manage keys on the [Developer page](https://app.skylit.ai/developer) (API keys tab). New accounts
are seeded with **5,000 credits**.

```bash cURL
curl "https://api.skylit.ai/v1/heatmap?symbols=SPY" \
  -H "Authorization: Bearer $SKYLIT_API_KEY"
```

```python Python
import os, requests

session = requests.Session()
session.headers["Authorization"] = f"Bearer {os.environ['SKYLIT_API_KEY']}"
print(session.get("https://api.skylit.ai/v1/heatmap", params={"symbols": "SPY"}).json())
```

```javascript Node
const skylit = (path) =>
  fetch(`https://api.skylit.ai${path}`, {
    headers: { Authorization: `Bearer ${process.env.SKYLIT_API_KEY}` },
  }).then((r) => r.json());

console.log(await skylit("/v1/heatmap?symbols=SPY"));
```

## Credits

Every chargeable request debits a fixed cost from your credit balance.

| Endpoint | Cost |
| --- | --: |
| `/v1/heatmap` | 1 |
| `/v1/gex/levels` | 1 |
| `/v1/historical` | 5 |
| `/v1/stream` | 1 to open + 1 per minute open |
| `/v1/account`, `/v1/openapi.json` | 0 |

Every chargeable response carries `X-Credits-Remaining: <balance>`.
**Failed calls are free:** a request answered with any `4xx` or `5xx` is refunded,
and its `X-Credits-Remaining` already reflects the refund.

- **402 insufficient_credits**: You're out of credits. Top up in the [account console](https://app.skylit.ai).
- **403 account_suspended**: The account has been administratively suspended.

## Rate limits

A safety ceiling of **600 requests / minute** per key is enforced by the Skylit gateway.
This is runaway protection, not your quota — credit metering does the per-customer accounting.
The `X-RateLimit-Limit`, `X-RateLimit-Remaining` and `X-RateLimit-Reset` headers describe the
key's quota, which is unlimited (`-1`, `0`, `0`). They don't count toward the per-minute
ceiling, so don't throttle on them.

| Status | Meaning |
| --- | --- |
| `401 Unauthorized` | Missing or invalid API key. |
| `403 Forbidden` | Key revoked/expired, or account suspended. |
| `429 Too Many Requests` | Rate ceiling hit. The gateway's `429` has no `Retry-After`: back off and retry after the current minute. `429`s from the API itself (concurrency or stream limits) carry `Retry-After`. |

- [Make your first call](https://www.skylit.ai/docs/api-reference/heatmap/live-per-strike-heatmap-one-or-more-symbols): Jump to **GET /v1/heatmap** and try it live in the playground.
