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

# WebSocket

Betmatic provides an authenticated private WebSocket for instant account and odds responses.

> **Access required.** WebSocket access is not enabled by default. To request it, email <support@betmatic.io>.

***

## Connection

```
wss://betmatic.app/ws/private?token=YOUR_TOKEN
```

Use the API token returned by `POST /account/login/`. An invalid or missing token is rejected during the WebSocket handshake with HTTP `403`.

***

## Message categories

Messages are JSON objects. Inspect the `category` field to determine how to handle each message.

| Category             | Description                                     |
| -------------------- | ----------------------------------------------- |
| `bet_history`        | A bet-history update. The response is in `data` |
| `odds_update_anwser` | An odds response. The response is in `data`     |

The category `odds_update_anwser` is intentionally shown exactly as it is currently sent by the API.

Clients should ignore unknown fields and log unknown categories so they remain compatible as new events are added.

***

## POST /notification/odds/request/

Asks your own running bots to read the current odds for a race from their live sessions. The REST call only *requests* the odds — the answer arrives asynchronously on the private WebSocket, so this endpoint is only useful with WebSocket access.

### Request body

| Field          | Type    | Required | Description                                                     |
| -------------- | ------- | :------: | --------------------------------------------------------------- |
| `code`         | string  |    Yes   | `"Galloping"`, `"Harness"`, or `"Greyhounds"`                   |
| `competition`  | string  |    Yes   | Venue name from `GET /competition/namecodes/` e.g. `"HAMILTON"` |
| `event_number` | integer |    Yes   | Race number                                                     |

```bash
curl -X POST https://betmatic.app/api/notification/odds/request/ \
  -H "Authorization: Token YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"code": "Galloping", "competition": "HAMILTON", "event_number": 1}'
```

### Responses

| Status | Meaning                                                                               |
| :----: | ------------------------------------------------------------------------------------- |
|   200  | `"OK"` — the request was relayed to your bots                                         |
|   404  | `"Competition not found"` — the venue/code/race number doesn't match an upcoming race |

The synchronous `200 "OK"` only confirms the request was sent. The odds themselves come back over the WebSocket as a message with category `odds_update_anwser`, with the per-session odds in `data`. **A running bot with Online sessions is required** — the request is answered by your own bot machines, so with no bot connected the call still returns `"OK"` but no odds message ever arrives. See the complete example below.

***

## Complete Python example

This example logs in, connects to the private WebSocket, requests odds for a race, and handles both odds and bet-history responses.

```python
import _thread
import json

import rel
import requests
import websocket

EMAIL = "<EMAIL>"
PASSWORD = "<PASSWORD>"

API_BASE = "https://betmatic.app"
WEBSOCKET_URL = "wss://betmatic.app/ws/private?token="

token = None


def get_token():
    global token

    response = requests.post(
        f"{API_BASE}/api/account/login/",
        headers={"Content-Type": "application/json"},
        json={
            "email": EMAIL,
            "password": PASSWORD,
        },
    )
    response.raise_for_status()
    result = response.json()
    token = result["token"]
    return result


def request_odds(code, competition, event_number):
    response = requests.post(
        f"{API_BASE}/api/notification/odds/request/",
        headers={
            "Authorization": f"Token {token}",
            "Content-Type": "application/json",
        },
        json={
            "code": code,  # "Galloping", "Greyhounds", or "Harness"
            "competition": competition,  # For example, "HAMILTON"
            "event_number": event_number,
        },
    )
    response.raise_for_status()
    print("Odds request accepted:", response.text)


def on_message(ws, message):
    try:
        result = json.loads(message)
        category = result.get("category")

        if category == "bet_history":
            print("Received bet history:")
            print(result.get("data"))
        elif category == "odds_update_anwser":
            print("Received odds:")
            print(json.dumps(result.get("data"), indent=2))
        else:
            print("Unhandled message:", result)
    except (TypeError, ValueError) as error:
        print("Could not parse message:", error)


def on_error(ws, error):
    print("WebSocket error:", error)


def on_close(ws, close_status_code, close_message):
    print("WebSocket closed:", close_status_code, close_message)


def on_open(ws):
    print("WebSocket connected")
    _thread.start_new_thread(
        request_odds,
        ("Galloping", "HAMILTON", 1),
    )


def main():
    result = get_token()
    if "token" not in result:
        print("Could not log in with these credentials.")
        return

    ws = websocket.WebSocketApp(
        WEBSOCKET_URL + token,
        on_open=on_open,
        on_message=on_message,
        on_error=on_error,
        on_close=on_close,
    )

    ws.run_forever(dispatcher=rel, reconnect=5)
    rel.signal(2, rel.abort)
    rel.dispatch()


if __name__ == "__main__":
    main()
```

Install the dependencies with:

```bash
pip install requests websocket-client rel
```
