Skip to main content

4.1 Offers Stream

Endpoint:

GET /ws/offers

This endpoint upgrades to a WebSocket connection. Once connected, MollyBet receives real-time offer messages from you. Traffic is one-way: we send nothing but protocol-level pings.

Contents:

4.1.1 Connecting​

We connect over wss:// and authenticate with the same HTTP Basic Authorization header used on the REST endpoints. The full URL is agreed with you during onboarding.

Two connection-level rules matter:

  • Send text frames only. Every message must be a UTF-8 JSON text frame. A binary frame — or any other non-text frame — is treated as a fatal protocol error and we drop the connection.
  • Expect pings. We enable a WebSocket heartbeat with an 8 second interval. Your server must answer protocol-level ping frames with pongs, which any standard WebSocket library does automatically.

4.1.2 Message envelope​

Every message on the stream is wrapped in the same envelope. There is no bare payload.

ts [ string ]:

When the message was generated, in UTC ISO 8601.

type [ string ]:

Which kind of message this is. One of SelectionPrices, DeleteEventMarket, DeleteEvent or Error. Note the exact casing.

payload [ object ]:

The body, whose shape is determined by type.

{
"ts": "2024-04-13T16:30:00.000Z",
"type": "SelectionPrices",
"payload": { }
}

type and payload are validated together — a payload whose shape does not match its type is rejected:

typepayload shape
SelectionPricesA full event, exactly as returned by /events, plus inrunning.
DeleteEventMarketevent_id and market_id.
DeleteEventevent_id only.
Errormessage and details.

4.1.3 SelectionPrices​

Publishes prices for one or more selections. The payload is a complete event object — the same normal event or special event shape used by /events, carrying sport, league, start_time, home and away (or runners and event for a multirunner), and offers.

Because the payload is a whole event, this message can introduce an event we have not seen before as well as update one we already know about.

inrunning [ boolean ] (required):

Whether the event is currently in-play. This field is optional on the REST `/events` response, where we infer it from the type you were polled for — but over the WebSocket there is no such context, so it is mandatory here. A message whose inrunning is null or absent is rejected and the connection is dropped.

offers [ array ]:

The selections being updated. Each entry is applied independently, so a message may carry more than one, though normally you send only the selection that changed.

An offer's quotes list replaces whatever we currently hold for that selection:

  • A non-empty list becomes the prices and liquidity you are standing behind right now.
  • Sending fewer quotes than before removes the ones you left out.
  • An empty list removes all prices for that selection, suspending it without withdrawing the market.

Example — updating a price:

{
"ts": "2024-04-13T16:30:00.000Z",
"type": "SelectionPrices",
"payload": {
"sport": "fb",
"league": {"name": "Spain | La Liga", "id": 123},
"start_time": "2024-04-13T16:30:00.000Z",
"home": {"name": "FC Barcelona", "id": 456},
"away": {"name": "Real Madrid", "id": 789},
"inrunning": false,
"offers": [
{
"bet_type": "for,ah,h,-10",
"selection_id": "123|28276122|456|for",
"quotes": [
{"price": 2.345, "min_stake": ["EUR", 1], "max_stake": ["EUR", 500]}
]
}
]
}
}

Example — suspending a selection:

{
"ts": "2024-04-13T16:30:00.000Z",
"type": "SelectionPrices",
"payload": {
"sport": "fb",
"league": {"name": "Spain | La Liga", "id": 123},
"start_time": "2024-04-13T16:30:00.000Z",
"home": {"name": "FC Barcelona", "id": 456},
"away": {"name": "Real Madrid", "id": 789},
"inrunning": false,
"offers": [
{
"bet_type": "for,ah,h,-10",
"selection_id": "123|28276122|456|for",
"quotes": []
}
]
}
}

4.1.4 DeleteEventMarket​

Withdraws a single market from an event, along with every selection in it.

event_id [ string ]:

The event, identified by the first segment of the selection IDs you publish for it.

market_id [ string ]:

The market to remove, identified by the second segment of those selection IDs.

Both fields are required for this message type. See A note on identifiers.

{
"ts": "2024-04-13T16:30:00.000Z",
"type": "DeleteEventMarket",
"payload": {
"event_id": "123",
"market_id": "28276122"
}
}

4.1.5 DeleteEvent​

Withdraws an entire event and every market on it.

event_id [ string ]:

The event to remove, identified by the first segment of the selection IDs you publish for it.

Send event_id alone. The presence or absence of market_id is what distinguishes this message from DeleteEventMarket, so do not include it.

{
"ts": "2024-04-13T16:30:00.000Z",
"type": "DeleteEvent",
"payload": {
"event_id": "123"
}
}

4.1.6 Error​

Reports that something has gone wrong on your side.

message [ string ]:

What went wrong.

details [ any ]:

Any supporting detail. Free-form — an object, a string, or null.

{
"ts": "2024-04-13T16:30:00.000Z",
"type": "Error",
"payload": {
"message": "Upstream feed unavailable",
"details": null
}
}
caution

An Error message terminates the session. MollyBet logs it, treats the connection as no longer authenticated, and disconnects. It is not a warning channel — do not use it to report recoverable or per-selection problems.

To withdraw prices, send SelectionPrices with an empty quotes list, or a delete message. Reserve Error for a genuine session-level failure.

4.1.7 A note on identifiers​

The delete messages address events and markets by the segments of the selection ID, whose format is:

<event_id>|<market_id>|<selection_id>|<bet_side>

So a selection ID of 123|28276122|456|for has event ID 123 and market ID 28276122 — the values used in the delete examples above. This is the reason the selection ID format matters even if you never parse it yourself: it is what makes targeted withdrawal possible, and we split on | to route every offer you publish.