> For the complete documentation index, see [llms.txt](https://naviprotocol.gitbook.io/astros/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://naviprotocol.gitbook.io/astros/astros-perpetual/astros-api/websocket.md).

# WebSocket

Endpoint: `wss://api.astros.ag/api/market/ws`

All WebSocket subscriptions require API key authentication. Sign the **timestamp string** with HMAC-SHA256 using `app_secret` (lowercase hex), the same algorithm as REST.

### Keepalive (ping / pong)

**Clients should implement an application-level ping/pong heartbeat.** The server closes idle connections after about **120 seconds** with no inbound traffic. Send a ping on a fixed interval (recommended **every 15–30 seconds**) and reconnect if no pong arrives.

#### Text ping / pong (recommended)

Send the plain text frame `ping` (exactly four characters, case-insensitive). The server replies with the plain text frame `pong`.

| Direction       | Frame type | Payload |
| --------------- | ---------- | ------- |
| Client → server | Text       | `ping`  |
| Server → client | Text       | `pong`  |

**Example (browser / Node)**

```javascript
const ws = new WebSocket('wss://api.astros.ag/api/market/ws');

ws.onopen = () => {
  // Heartbeat: ping every 20s
  setInterval(() => {
    if (ws.readyState === WebSocket.OPEN) {
      ws.send('ping');
    }
  }, 20_000);
};

ws.onmessage = (event) => {
  if (event.data === 'pong') {
    // connection is healthy
    return;
  }
  // handle subscribe ack / channel pushes...
  console.log(JSON.parse(event.data));
};
```

**Example (Python)**

```python
import asyncio
import websockets

async def run():
    uri = 'wss://api.astros.ag/api/market/ws'
    async with websockets.connect(uri) as ws:
        async def heartbeat():
            while True:
                await ws.send('ping')
                await asyncio.sleep(20)

        asyncio.create_task(heartbeat())

        async for message in ws:
            if message == 'pong':
                continue
            # handle JSON subscribe / push messages
            print(message)

asyncio.run(run())
```

#### Protocol-level WebSocket ping

The server also answers standard WebSocket **Ping** control frames with a **Pong** control frame. Prefer the text `ping` / `pong` exchange above when your client library makes application messages easier to handle than control frames.

#### Client recommendations

* Start the heartbeat as soon as the socket opens, before or after subscribe.
* Treat a missed pong (for example no reply within 10 seconds) as a dead connection: close and reconnect with backoff.
* Do not rely on subscribe traffic alone to keep the socket alive during quiet markets.

### Subscription envelope

Every subscribe request shares these fields:

| Name        | Type   | Required | Description                         |
| ----------- | ------ | -------- | ----------------------------------- |
| `method`    | String | Yes      | `SUBSCRIBE`                         |
| `event`     | String | Yes      | Channel name, e.g. `api_account`    |
| `apiKey`    | String | Yes      | Your API key                        |
| `timestamp` | Number | Yes      | Unix time in milliseconds           |
| `signature` | String | Yes      | HMAC-SHA256 of the timestamp string |

Channel-specific fields (such as `symbol`, `pair`, `period`) are documented per event below.

#### Sign a subscribe request

1. Take the current timestamp, e.g. `1657157345159`
2. Sign the string `"1657157345159"` with HMAC-SHA256
3. Attach `apiKey`, `timestamp`, and `signature` to the subscribe payload

```json
{
  "symbol": "ETH",
  "method": "SUBSCRIBE",
  "apiKey": "xx",
  "event": "api_account",
  "timestamp": 1657157345159,
  "signature": "f74f11beb8e24cf8a1c5bc8af2fba15446c7806cc12ae09e0dee5b2dd8eef76d"
}
```

***

### Account

`event`: `api_account`

**Extra request fields**

| Name     | Type   | Required | Description                 |
| -------- | ------ | -------- | --------------------------- |
| `symbol` | String | Yes      | Settlement coin, e.g. `USD` |

**Subscribe**

```json
{
  "symbol": "USD",
  "method": "SUBSCRIBE",
  "event": "api_account",
  "apiKey": "xx",
  "signature": "cea09113cb6af891dd620315dcf4316a557ddac28377cb341920d8b0b50eb872",
  "timestamp": 1715049419673
}
```

**Ack**

```json
{
  "event": "api_account_res",
  "method": "SUBSCRIBE",
  "msg": "subscribe success: api_account",
  "success": true,
  "symbol": "USD"
}
```

**Push**

```json
{
  "data": {
    "availableAmount": "707.729782",
    "coinId": 4,
    "frozenAmount": "1682.041309",
    "logId": 0,
    "operateAmount": "0.00",
    "symbol": "USD"
  },
  "event": "api_account",
  "success": true,
  "symbol": "USD"
}
```

***

### Order updates

`event`: `api_entrust`

**Extra request fields**

| Name   | Type   | Required | Description                  |
| ------ | ------ | -------- | ---------------------------- |
| `pair` | String | Yes      | Trading pair, e.g. `ETH-USD` |

**Subscribe**

```json
{
  "pair": "ETH-USD",
  "method": "SUBSCRIBE",
  "apiKey": "xx",
  "signature": "dcbfb54c06c04b214de4b63d86b761c1cd3d7427c829e36684b7b2fafa7492c3",
  "event": "api_entrust",
  "timestamp": 1715050200673
}
```

**Push**

```json
{
  "data": {
    "changeType": "TRADE",
    "clientOrderId": "17163700712771",
    "contractPairId": 1,
    "currentEntrustId": 78315117,
    "dealAmount": "0",
    "dealQuantity": "0.01",
    "direction": "LONG",
    "feeAmt": "0.022602",
    "feeCoin": "USD",
    "isClose": false,
    "isMarket": true,
    "isTaker": true,
    "lever": 4,
    "matchType": 1,
    "nowDealQuantity": "0.01",
    "price": "3768.953535",
    "quantity": "0.01",
    "symbol": "ETH-USD"
  },
  "event": "api_entrust",
  "success": true
}
```

***

### Position updates

`event`: `api_position`

**Extra request fields**

| Name   | Type   | Required | Description  |
| ------ | ------ | -------- | ------------ |
| `pair` | String | Yes      | Trading pair |

**Push**

```json
{
  "data": {
    "canClosedQuantity": "0.07",
    "changeType": "NEW",
    "contractPairId": 1,
    "currentQuantity": "0.07",
    "direction": "SHORT",
    "marginAmount": "10.907512",
    "marginCallAmount": "0",
    "openingPrice": "3116.431999",
    "operateQuantity": "0",
    "positionId": 1088968,
    "positionType": 3,
    "symbol": "ETH-USD"
  },
  "event": "api_position",
  "success": true
}
```

***

### Kline

`event`: `api_kline`

**Extra request fields**

| Name     | Type   | Required | Description  |
| -------- | ------ | -------- | ------------ |
| `pair`   | String | Yes      | Trading pair |
| `period` | String | Yes      | e.g. `1MIN`  |

**Push**

```json
{
  "contractPairId": 1,
  "data": {
    "amount": 0,
    "close": 3084.91,
    "contractPairId": 1,
    "count": 0,
    "hight": 3084.91,
    "low": 3084.91,
    "open": 3084.91,
    "period": "1MIN",
    "quantity": 0,
    "time": 1715050620000
  },
  "event": "api_kline",
  "period": "1MIN",
  "success": true
}
```

***

### Order book

`event`: `api_depth`

**Extra request fields**

| Name   | Type   | Required | Description  |
| ------ | ------ | -------- | ------------ |
| `pair` | String | Yes      | Trading pair |

**Push**

```json
{
  "data": {
    "asks": [
      { "price": "3123.86", "quantity": "12.641" },
      { "price": "3165.74", "quantity": "7.358" }
    ],
    "bids": [
      { "price": "3047.05", "quantity": "10.592" },
      { "price": "2983.75", "quantity": "7.868" }
    ]
  },
  "event": "api_depth",
  "pair": "ETH-USD",
  "success": true
}
```

***

### Recent trades

`event`: `api_trade`

**Extra request fields**

| Name     | Type   | Required | Description                          |
| -------- | ------ | -------- | ------------------------------------ |
| `pair`   | String | Yes      | Trading pair                         |
| `period` | String | No       | Channel period if required by server |

**Push**

```json
{
  "data": [
    {
      "amount": 2306.9816,
      "contractMatchPairId": 7890741,
      "contractPairId": 1,
      "isLong": false,
      "pair": "ETH-USD",
      "price": 3084.2,
      "quantity": 0.748,
      "time": "03:00:25",
      "timestamp": 1715050825000
    }
  ],
  "event": "api_trade",
  "pair": "ETH-USD",
  "success": true
}
```

***

### Live trade prints

`event`: `api_spot_deals`

**Extra request fields**

| Name   | Type   | Required | Description  |
| ------ | ------ | -------- | ------------ |
| `pair` | String | Yes      | Trading pair |

**Push**

```json
{
  "contractPairId": 1,
  "data": {
    "amount": 2300.01498,
    "contractMatchPairId": 7891127,
    "contractPairId": 1,
    "isLong": true,
    "pair": "ETH-USD",
    "price": 3083.13,
    "quantity": 0.746,
    "time": "03:09:05",
    "timestamp": 1715051345000
  },
  "event": "api_spot_deals",
  "pair": "ETH-USD",
  "success": true
}
```

***

### User fills

`event`: `api_deal_user`

**Extra request fields**

| Name   | Type   | Required | Description  |
| ------ | ------ | -------- | ------------ |
| `pair` | String | Yes      | Trading pair |

**Push**

```json
{
  "data": {
    "contractPairId": 1,
    "currentEntrustId": 78312005,
    "dealAmount": "37.6773",
    "dealPrice": "3767.73",
    "dealQuantity": "0.01",
    "dealTime": "1716369742538",
    "direction": "LONG",
    "feeAmt": "0.022606",
    "feeCoin": "USD",
    "isClose": false,
    "isTaker": true,
    "matchId": 9280079,
    "matchType": 1,
    "symbol": "ETH-USD"
  },
  "event": "api_deal_user",
  "success": true
}
```
