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.