Weight of Money API#
Odds tell you the price a book is willing to offer. This API tells you something different: how much money is actually sitting on each selection, and what share of the market that is.
Every football fixture we cover returns its markets broken down by selection, each with the money matched on it, its share of that market's total, the price attached to it, and the previous price. Captures are stored as a series, so you can also ask how the split got to where it is.
Base URL: https://sports.bzzoiro.com/wom/api/
In-play fixtures are refreshed every 15 seconds and prematch fixtures every 60 seconds — the cadence the numbers actually move at source. The long-tail markets (corners, cards, correct score) refresh hourly.
Football only today. Other sports will land on the same routes with a sport
parameter; existing responses will not change shape when they do.
Access#
Authentication is the standard BSD token from your dashboard — the same one the football, odds and WebSocket products use. Send it on every request:
Authorization: Token YOUR_API_KEY
Access requires an active Weight of Money subscription — $20/month. Accounts holding the Founder badge (granted once to every account that existed before 2026-08-03) pay $10/month instead, and keep that price for as long as the subscription stays active. The badge cannot be earned by subscribing. This addon is separate from the Sports Addon and from the Odds API. Without a token you get 401; with a valid token but no subscription you get 402:
{"error": "active Weight of Money subscription required",
"subscribe_url": "/wom/pricing/"}
GET /wom/api/coverage/ is the exception: any authenticated token may call it,
so you can size the dataset before paying for it.
Subscribe at /wom/ or /addons/. Card subscriptions renew automatically; PayPal and crypto are one-time payments that stack months onto your expiry instead — pay a multiple of your monthly price to buy several at once.
Event identifiers#
Every row is keyed to a BSD event id — the same id used by
/api/v2/events/{id}/, the odds API's bsd_id and the live WebSocket. There is
no second identifier space and no mapping table to maintain.
A fixture appears in this API only once it resolves to a BSD event. See Coverage and limits.
Market codes#
Markets follow the same code format as the odds API: KIND[_LINE]_SELECTION_PERIOD.
| Code | Meaning |
|---|---|
1X2_HOME_FT |
Home win, full time |
1X2_DRAW_FT |
Draw, full time |
1X2_AWAY_FT |
Away win, full time |
1X2_HOME_1H |
Home win, first half |
OU_2.5_OVER_FT |
Over 2.5 goals, full time |
OU_1.5_UNDER_1H |
Under 1.5 first-half goals |
BTTS_YES_FT |
Both teams to score |
DNB_HOME_FT |
Draw no bet, home |
CORNERS_10.5_OVER_FT |
Over 10.5 corners |
CARDS_3.5_UNDER_FT |
Under 3.5 cards |
CS_2-1_FT |
Correct score 2-1 |
HTFT_HOME-AWAY_FT |
Home at half time, away at full time |
GET /wom/api/markets/ returns the live catalog.
Endpoints#
| Endpoint | Description |
|---|---|
GET /wom/api/coverage/ |
Size and freshness of the dataset. Any token. |
GET /wom/api/events/ |
Fixtures with their latest money split |
GET /wom/api/events/{id}/ |
Every market for one fixture |
GET /wom/api/events/{id}/history/ |
Volume and share over time |
GET /wom/api/movers/ |
Where money share and implied probability disagree |
GET /wom/api/markets/ |
Market catalog |
GET /wom/api/events/#
Fixtures with the latest capture for each of their markets.
| Param | Default | Description |
|---|---|---|
date |
— | YYYY-MM-DD. Omit for everything kicking off from 3 hours ago onward |
status |
— | live, upcoming or finished |
market |
— | A market code (OU_2.5_OVER_FT) or a kind (OU, 1X2, BTTS) |
limit |
20 | Max 50 |
offset |
0 | Pagination offset |
curl -H "Authorization: Token $BSD_TOKEN" \
"https://sports.bzzoiro.com/wom/api/events/?date=2026-08-04&market=1X2"
{
"count": 42,
"next": "...",
"results": [
{
"id": 35898868,
"home_team": "Jeju SK",
"away_team": "FC Bayern München",
"league": "Elite Friendlies",
"kickoff": "2026-08-04T11:00:00+00:00",
"status": "inprogress",
"total_volume": 143388.0,
"money": [
{
"market": "1X2_AWAY_FT",
"kind": "1X2",
"line": null,
"selection": "AWAY",
"period": "FT",
"name": "Match Odds 2",
"volume": 135838.0,
"market_volume": 143388.0,
"share": 94.73,
"league_avg_volume": null,
"price": 1.11,
"previous_price": 1.2,
"implied_probability": 90.09,
"is_live": true,
"captured_at": "2026-08-04T11:42:07+00:00"
}
]
}
]
}
Field notes
| Field | Meaning |
|---|---|
volume |
Money matched on this selection, in EUR |
market_volume |
Money matched across every selection of this market |
share |
volume / market_volume as a percentage |
league_avg_volume |
Typical market volume for this competition, or null when there is too little history. Use it to tell a busy market from a quiet one |
price / previous_price |
Decimal odds now and at the previous capture |
implied_probability |
100 / price. Includes the book's margin — it does not sum to 100 across a market |
captured_at |
When this row was sampled. Not a live tick; see below |
GET /wom/api/events/{id}/history/#
The series behind a fixture. Ordered oldest first, ready to plot.
| Param | Description |
|---|---|
market |
Restrict to one market code. Omit for every market on the fixture |
{
"event_id": 35898868,
"series": {
"1X2_AWAY_FT": [
{"captured_at": "2026-08-04T09:00:00+00:00", "volume": 40120.0, "share": 88.1, "price": 1.2, "market_volume": 45540.0, "is_live": false},
{"captured_at": "2026-08-04T11:42:07+00:00", "volume": 135838.0, "share": 94.73, "price": 1.11, "market_volume": 143388.0, "is_live": true}
]
}
}
A row exists only where the numbers changed, so gaps between points are periods when nothing moved, not missing data.
GET /wom/api/movers/#
Selections whose share of the money is furthest from what their price implies.
| Param | Default | Description |
|---|---|---|
min_volume |
5000 | Skip markets thinner than this |
min_divergence |
10 | Minimum absolute divergence, in points |
status |
— | live, upcoming or finished |
limit |
25 | Max 100 |
{
"count": 1,
"movers": [
{
"event": {"id": 35898868, "home_team": "Jeju SK", "away_team": "FC Bayern München"},
"market": "1X2_AWAY_FT",
"share": 94.73,
"implied_probability": 90.09,
"divergence": 4.64,
"volume": 135838.0,
"market_volume": 143388.0,
"price": 1.11
}
]
}
divergence is share − implied_probability, in points. Positive means more
money is on the selection than its price implies.
Coverage and limits#
Read this before you build against the data.
It describes, it does not predict. A selection holding 90% of the money is not a selection that wins 90% of the time. We publish the split and the divergence as measurements. We make no claim that either is an edge and we have not back-tested them as a signal.
implied_probability carries the margin. It is a plain 100 / price, so
across a full market it sums to more than 100. If you want a fair comparison
against share, de-vig the prices yourself first.
Coverage follows BSD football coverage. A fixture is stored only when it
resolves to a BSD event with confidence. Reserve teams, parts of the women's
game and smaller federations sit outside our football coverage and therefore
outside this dataset. GET /wom/api/coverage/ reports what is actually there.
Captures are periodic. In-play fixtures are sampled every 15 seconds and
prematch fixtures every 60 seconds, which is as fast as the numbers change at
source. The long tail of markets — corners, cards, correct score — is refreshed
hourly. A row is written only when volume or price actually moved, so gaps in a
series mean "nothing changed", not "data missing", and captured_at always
tells you how old the state you are reading is.
This is a polling API, not a push stream. If you need to react within a second,
poll /wom/api/events/?status=live on your own clock; for tick-level odds use
the WebSocket.
Volumes are in EUR and reflect matched money on exchange-style markets, not total turnover across the whole betting market.