# Commands

## Client Commands

This document describes the commands that clients can send to the WebSocket server.

### Command Format

All commands are JSON objects with an `action` field:

```json
{
  "action": "command_name",
  ...additional fields
}
```

### Available Commands

| Command     | Description                |
| ----------- | -------------------------- |
| subscribe   | Subscribe to a channel     |
| unsubscribe | Unsubscribe from a channel |
| ping        | Keep connection alive      |

***

### Subscribe

Subscribe to a channel to start receiving events.

#### Request

```json
{
  "action": "subscribe",
  "channel": "MAIN",
  "scope": "all"
}
```

#### Parameters

<table><thead><tr><th width="108.60009765625">Field</th><th width="106.5999755859375">Type</th><th width="114">Required</th><th>Description</th></tr></thead><tbody><tr><td><code>action</code></td><td>string</td><td>Yes</td><td>Must be <code>"subscribe"</code></td></tr><tr><td><code>channel</code></td><td>string</td><td>Yes</td><td>Channel name (MAIN, MAIN_REALTIME, STATS, ODDS, STATS_REALTIME, ODDS_REALTIME)</td></tr><tr><td><code>scope</code></td><td>string</td><td>Yes</td><td>Subscription scope: <code>"all"</code>, <code>"match"</code>, or <code>"league"</code></td></tr><tr><td><code>id</code></td><td>string</td><td>Conditional</td><td>Required when scope is <code>"match"</code> or <code>"league"</code></td></tr></tbody></table>

#### Success Response

```json
{
  "type": "subscribed",
  "channel": "MAIN",
  "scope": "all",
  "id": null,
  "topicKey": "MAIN:all",
  "message": "Successfully subscribed"
}
```

When subscribing with a specific scope:

```json
{
  "type": "subscribed",
  "channel": "STATS",
  "scope": "match",
  "id": "12345",
  "topicKey": "STATS:match:12345",
  "message": "Successfully subscribed"
}
```

#### Error Response

```json
{
  "type": "error",
  "code": "CHANNEL_NOT_ALLOWED",
  "message": "Channel STATS_REALTIME is not available for your plan"
}
```

#### Error Codes

| Code                  | Description                            |
| --------------------- | -------------------------------------- |
| `CHANNEL_NOT_ALLOWED` | Your plan doesn't include this channel |
| `SUBSCRIPTION_LIMIT`  | Maximum subscriptions reached          |
| `INVALID_CHANNEL`     | Channel name is not valid              |
| `INVALID_SCOPE`       | Scope format is not valid              |

#### Examples

```json
// Subscribe to all main events
{"action": "subscribe", "channel": "MAIN", "scope": "all"}

// Subscribe to stats for a specific match
{"action": "subscribe", "channel": "STATS", "scope": "match", "id": "12345"}

// Subscribe to odds for an entire league
{"action": "subscribe", "channel": "ODDS", "scope": "league", "id": "67890"}

// Subscribe to real-time stats updates (MEGA plan only)
{"action": "subscribe", "channel": "STATS_REALTIME", "scope": "match", "id": "12345"}
```

***

### Unsubscribe

Unsubscribe from a channel to stop receiving events.

#### Request

```json
{
  "action": "unsubscribe",
  "channel": "MAIN",
  "scope": "all"
}
```

#### Parameters

<table><thead><tr><th width="112.5999755859375">Field</th><th width="125.4000244140625">Type</th><th width="111">Required</th><th>Description</th></tr></thead><tbody><tr><td><code>action</code></td><td>string</td><td>Yes</td><td>Must be <code>"unsubscribe"</code></td></tr><tr><td><code>channel</code></td><td>string</td><td>Yes</td><td>Channel name to unsubscribe from</td></tr><tr><td><code>scope</code></td><td>string</td><td>Yes</td><td>Scope to unsubscribe from: <code>"all"</code>, <code>"match"</code>, or <code>"league"</code></td></tr><tr><td><code>id</code></td><td>string</td><td>Conditional</td><td>Required when scope is <code>"match"</code> or <code>"league"</code></td></tr></tbody></table>

#### Success Response

```json
{
  "type": "unsubscribed",
  "channel": "MAIN",
  "scope": "all",
  "id": null,
  "topicKey": "MAIN:all",
  "message": "Successfully unsubscribed"
}
```

#### Error Response

```json
{
  "type": "error",
  "code": "NOT_SUBSCRIBED",
  "message": "Not subscribed to this channel/scope"
}
```

#### Notes

* Unsubscribing frees up a subscription slot
* You must specify the exact channel and scope used when subscribing
* Unsubscribing from a non-existent subscription returns an error but doesn't affect other subscriptions

***

### Ping

Send a ping to keep the connection alive and verify connectivity.

#### Request

```json
{
  "action": "ping"
}
```

#### Response

```json
{
  "type": "pong",
  "ts": 1705312800000
}
```

#### Response Fields

| Field  | Type    | Description                      |
| ------ | ------- | -------------------------------- |
| `type` | string  | Always `"pong"`                  |
| `ts`   | integer | Server timestamp in milliseconds |

#### Best Practices

* Send a ping every 30 seconds to maintain the connection
* Use the response timestamp to measure latency
* If no pong is received within 10 seconds, consider the connection dead and reconnect

#### Example Implementation

```javascript
// JavaScript ping implementation
setInterval(() => {
  if (ws.readyState === WebSocket.OPEN) {
    ws.send(JSON.stringify({ action: 'ping' }));
  }
}, 30000);
```

```python
# Python ping implementation
async def keep_alive(ws):
    while True:
        await asyncio.sleep(30)
        await ws.send(json.dumps({"action": "ping"}))
```

***

### Invalid Command Handling

If you send an unrecognized command, the server responds with:

```json
{
  "type": "error",
  "code": "INVALID_ACTION",
  "message": "Unknown action: foobar"
}
```

### Rate Limiting

* Commands are rate-limited to prevent abuse
* Maximum 10 commands per second per connection
* Exceeding the limit results in temporary throttling


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://info.soccerfootball.info/websocket/commands.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
