# Connecting

> How messages, subscriptions, updates and reconnects work on the WebSocket.

This page is part of the Intear API docs. The Intear API is at `https://api.intea.rs` and `wss://api.intea.rs/ws`, with no API key. https://docs.intear.tech/api/llms-full.txt has all of its pages in one file, and https://docs.intear.tech/llms.txt lists every page of the Intear docs.

Connect to `wss://api.intea.rs/ws`. Every message, both ways, is one JSON text message.

## Requests and replies

The client sends requests:

```json
{ "id": 1, "method": "tokens.get", "params": { "fields": ["symbol"], "ids": ["wrap.near"] } }
```

- `id` is a number or a string you pick. The reply has the same `id`.
- `params` can be left out when a method takes none.
- Replies can come in any order.

The server replies with a `result` or an `error`:

```json
{ "id": 1, "result": { ... }, "credits_used": 1 }
{ "id": 1, "error": { "code": "unknown_field", "message": "..." }, "credits_used": 0 }
```

See [Messages](https://docs.intear.tech/api/reference/messages.md) for every message the server sends, and [Errors](https://docs.intear.tech/api/reference/errors.md) for the error codes.

:::caution
Invalid messages close the connection with code `1008`.
:::

## The first message

The server's first message is `hello`, it's sent once right after the connection is established, with the latest block and the [limits](https://docs.intear.tech/api/reference/limits.md) of the connection:

```json
{ "hello": { "version": 2, "height": 219969289, "timestamp_millis": 1792003517185, "limits": { ... } }, "credits_used": 0 }
```

If the server restarts and your client reconnects to it, `hello` may come in a few seconds, so wait for it before counting on replies.

## Subscriptions

Methods ending in `.subscribe` make a subscription. It's named by the `id` of the request that made it, and every update it sends, called a push, has that `id` as `sub`:

```json
{ "sub": 1, "height": 219969292, "timestamp_millis": 1792003519000, "set": { ... }, "credits_used": 1 }
```

- The initial data comes before the first push, with nothing missed or repeated between them.
- Pushes come in block order across all of a connection's subscriptions, at most one per subscription per block, and none for a block that changed nothing the subscription follows.
- Changes made outside blocks, like token metadata being refreshed, come with the next block. If no block comes within a second, they come in a push of their own, with the `height` of the block before them.
- An `id` can't be reused while subscription is active. End a subscription with `unsubscribe`, which replies `{}`:

  ```json
  { "id": 7, "method": "unsubscribe", "params": { "sub": 1 } }
  ```

If a request fails, the subscription it would have made doesn't exist, so you don't get partial data.

## Heights

Every result and push has `height` and `timestamp_millis`: the latest block it accounts for. The results of `unsubscribe` and `schema` don't, and neither do errors and `end` messages. Use `height` to pick up after a reconnect, see [below](#after-a-reconnect).

## Idle connections

After 15 seconds with nothing else sent, the server sends:

```json
{ "idle": { "height": 219969300, "timestamp_millis": 1792003525000 }, "credits_used": 0 }
```

so you can tell a quiet connection from a dead one. If nothing at all arrives for much longer than 15 seconds, reconnect.

## Slow clients

A client that can't read pushes as fast as they come isn't disconnected. Each of its subscriptions catches up in one push instead:
- Token subscriptions send everything that changed since the last push.
- Event subscriptions (trades, transfers) send every event after the last one sent.

Some subscriptions can't catch up and end with `too_far_behind` instead:
- An event subscription that falls more than 10,000 rows behind.
- Any `balances.subscribe`, since balance changes can't be read back.

That's why you should filter the fields you get updates for, and use thresholds when using the API on client side, since mobile networks are often limited in bandwidth.

When the server ends a subscription, it sends:

```json
{ "sub": 1, "end": { "code": "too_far_behind", "message": "..." }, "credits_used": 0 }
```

Make the subscription again to carry on.

## Tables

Tokens, trades, transfers, balances, holders, positions, traders and `accounts.search` come as a table, so each field name is sent once instead of in every row:

```json
{
  "columns": ["account_id", "symbol", "price_usd"],
  "rows": [
    ["wrap.near", "wNEAR", "4.81"],
    ["usdt.tether-token.near", "USDt", "1"]
  ]
}
```

The first column comes first in every row (`account_id` for tokens, `id` for trades), then the fields you asked for, in the order you asked for them. It's a unique key, except in balances, where a token has a row per source, and positions, where a token has a row per position.

## After a reconnect

Connections drop. Each kind of subscription has a way to carry on from where it left off without downloading everything again:

| Subscription | How to resume |
| --- | --- |
| `tokens.subscribe` | Same params, with `resume_from` set to the `height` of the last reply or push. See [Live token lists](https://docs.intear.tech/api/guides/live-token-lists.md#after-a-reconnect) |
| `trades.subscribe`, `transfers.subscribe` | `after_id` set to the newest `id` you have. For transfers, it needs the same [filters as history](https://docs.intear.tech/api/reference/transfers.md#filters). See [History, then live](https://docs.intear.tech/api/guides/history-then-live.md) |
| `candles.subscribe`, `holders.subscribe`, `balances.subscribe` | Subscribe again: the reply has the current state |
