> ## Documentation Index
> Fetch the complete documentation index at: https://docs.solanatracker.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Connect to Solana Tracker Datastream WebSocket

> Connect to Solana Tracker Datastream, authenticate with an API key, subscribe to rooms, and handle JSON heartbeats and connection errors.

Connect once and join the rooms your app needs. Every Datastream subscription uses the same authentication, join/leave messages, and JSON heartbeat protocol.

<Info>
  **URL:** `wss://datastream.solanatracker.io/{apiKey}`\
  Use your Data API key in the path. Available on Premium, Business, and Enterprise plans.
</Info>

## Connection URL

Always connect to the canonical Datastream host:

```text theme={null}
wss://datastream.solanatracker.io/{apiKey}
```

Datastream authentication uses the API key in the WebSocket path. REST base URLs are separate from the Datastream host.

## Authentication failures

An invalid or missing API key may still complete the WebSocket handshake, then close almost immediately with:

* no JSON error payload
* no `joined` acknowledgment

If the connection closes immediately without a room acknowledgment, check the API key and URL before retrying.

## Join and leave

```javascript theme={null}
const ws = new WebSocket("wss://datastream.solanatracker.io/YOUR_API_KEY");

ws.onopen = () => {
  ws.send(JSON.stringify({ type: "join", room: "price-by-token:TOKEN_MINT" }));
};

ws.onmessage = (event) => {
  const msg = JSON.parse(event.data);

  if (msg.type === "joined") {
    console.log("Subscribed", msg.room);
  }

  if (msg.type === "message") {
    // Room payload in msg.data
  }

  // Application heartbeat (not WebSocket protocol ping/pong)
  if (msg.type === "ping") {
    ws.send(JSON.stringify({ type: "pong" }));
  }
};

// Call after joining when the app no longer needs this subscription.
// There is no separate `left` acknowledgment.
function unsubscribe() {
  if (ws.readyState === WebSocket.OPEN) {
    ws.send(JSON.stringify({ type: "leave", room: "price-by-token:TOKEN_MINT" }));
  }
}
```

| Envelope | Direction | Meaning |
| - | - | - |
| `{ "type": "join", "room" }` | Client → server | Subscribe |
| `{ "type": "joined", "room" }` | Server → client | Subscribe confirmed |
| `{ "type": "leave", "room" }` | Client → server | Unsubscribe (no `left` ack) |
| `{ "type": "message", "room", "data" }` | Server → client | Room data |
| `{ "type": "ping" }` | Server → client | App heartbeat |
| `{ "type": "pong" }` | Client → server | Heartbeat reply |

## Heartbeat (JSON ping/pong)

The server may emit a JSON control frame:

```json theme={null}
{ "type": "ping" }
```

Reply with:

```json theme={null}
{ "type": "pong" }
```

Reply to JSON heartbeats in your message handler. They are application messages, separate from WebSocket protocol ping/pong frames.

## Wallet balance amounts

On `wallet:{wallet}:balance` and `wallet:{wallet}:{token}:balance`:

* `amount` is a **UI decimal** (human units), not raw integer base units / lamports
* native SOL uses mint `So11111111111111111111111111111111111111112`

```json theme={null}
{
  "type": "message",
  "room": "wallet:WALLET:balance",
  "data": {
    "wallet": "WALLET",
    "token": "So11111111111111111111111111111111111111112",
    "amount": 1.2345
  }
}
```

## Limits and quotas

Premium+ Datastream includes **unlimited messages** (no per-message fees). There is **no limit** on concurrent WebSocket connections or rooms per connection.

REST monthly request quotas do **not** disconnect an established Datastream session when exhausted.

## Next

<CardGroup cols={2}>
  <Card title="Live prices" href="/guides/datastream-prices">
    Price and candle rooms.
  </Card>

  <Card title="Token launches" href="/guides/datastream-tokens">
    Launches, graduations, and market events.
  </Card>

  <Card title="Live risk signals" href="/guides/datastream-safety">
    Snipers, bundlers, and holder signals.
  </Card>

  <Card title="Live PnL" href="/guides/datastream-pnl">
    Live wallet and position PnL.
  </Card>
</CardGroup>
