# Live SSE stream (one symbol per connection)

`GET https://api.skylit.ai/v1/stream`

API: Heatseeker. Credits: 1 to open + 1 per minute open.

A **Server-Sent Events** (`text/event-stream`) feed of live per-strike
heatmap updates for **one** symbol. Open one connection per symbol.

**Events:**
- `connected` — handshake, payload `{symbol, creditsRemaining}`.
- `initial_data` — current heatmap snapshot on connect.
- `snapshot_update` — full heatmap on each change.
- `velocity_update` — per-strike % change.
- `credits` — emitted every minute boundary, payload `{remaining}`.
- `closed` — stream terminates with `{reason: "insufficient_credits"
  | "account_suspended" | "credit_check_failed"}`.
- `reconnect` — server is recycling the connection (after ~1h),
  payload `{reason: "max_duration"}`. Reconnect to continue.
- `: keepalive` comment every 30s for proxy keepalive.

**Pricing.** 1 credit on connect (charged before the SSE upgrade —
an under-funded client gets a clean `402` HTTP response, not a
half-open stream), then 1 credit per minute open. The per-minute
ticker emits `event: credits {remaining: N}` after each successful
debit so clients can budget the next minute.

**Concurrency.** Up to 5 concurrent streams per customer per pod.
Exceeding the cap returns `429` `stream_limit_reached`.

**Expirations.** Frames carry every expiration in the window (an
`Expirations` array plus one matrix column per expiration), so select
expirations client-side. The `expirations` parameter is snapshot-only —
sending it here returns `400` `invalid_parameter` rather than quietly
meaning something narrower than it does on `/v1/heatmap`.

> OpenAPI is request/response-oriented and can't fully model an event
> stream. The connection costs 1 credit to open plus 1 per minute
> connected and closes after one hour; reconnect to continue.

## Authentication

Send your Skylit API key as a bearer token: `Authorization: Bearer <key>`. No other header is accepted.

## Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `symbol` | query | string | yes | Single ticker to stream (e.g. `SPY`). One symbol per connection. |
| `metric` | query | string | no | Which Greek exposure to return per strike. (one of `gamma`, `vanna`; default `gamma`) |
| `maxStrikes` | query | integer | no | Maximum number of strikes around spot to return. (default `92`; min 1; max 400) |
| `maxExpirations` | query | integer | no | How many of the nearest expirations to net into each strike's `value`. Ignored when `expirations` is set. (default `5`; min 1; max 60) |

## Example request

```bash
curl -N "https://api.skylit.ai/v1/stream?symbol=SPY" \
  -H "Authorization: Bearer $SKYLIT_API_KEY" \
  -H "Accept: text/event-stream"
```

## Responses

### 200

An SSE stream of heatmap events.

`text/event-stream`: SSE frames, e.g. `event: snapshot_update` then `data: {SymbolHeatmap}`. The snapshot_update `data` payload matches #/components/schemas/SymbolHeatmap.

### 400

Request validation failed.

### 401

Missing or invalid API key.

### 403

The API key is invalid, revoked or expired, or the account is suspended.

### 404

Unknown symbol, no data available, or none of the requested
`expirations` exist for the symbol (`code: expiration_not_found`).

### 429

Per-minute rate limit exceeded.

Headers: `Retry-After`.
