GET /v3/markets
Every market one bookie prices on one event, with the market_ref that
places each selection.
This is the middle question between Events (EPK) ("which events can I bet") and Place Bet ("place this one"): what is actually on this book's card right now?
Why you need it. Until now you could only bet a market whose name you already knew, which in practice meant our curated list — head to head, line, totals, the usual player props. Everything else a bookie sells was invisible through the API even though we hold the prices. Across 400 live events there are 194,830 selections outside that curated list against 130,805 inside it; in soccer the unnamed tail alone is about nine times the curated grid. This endpoint returns all of it, and every row comes back bet-ready.
If you can't find a market, call this before concluding we don't support it. The canonical market names aren't published as a list anywhere — so "it's not in the docs" isn't evidence the market is missing. This endpoint answers it definitively for a given event and bookie, and hands you a
market_reffor whatever it finds. Guessing amarketkey doesn't error loudly; it silently fails to match, which looks the same as the market not existing.
Base URL: https://api.b337.ai · Header: X-API-Key: YOUR_API_KEY
| Param | Type | Default | Description |
|---|---|---|---|
epk | string | required | Event key from GET /v3/events. |
bookie | string | required | The book whose card you want, e.g. sportsbet. Must be one of the event's books. |
tier | string | — | Only one tier: popular, other or unmapped. |
market | string | — | Only one market, by its key, e.g. total_points. |
include_unbettable | bool | false | Also list selections we can see but can't place. See below. |
site_event_id | string | — | The bookie's own event id. Lets you read a card before we have linked the book to the event — you are responsible for it being the right match. See "Fast-listing books" below. |
curl -s -G "https://api.b337.ai/v3/markets" \ --data-urlencode "epk=aussie_rules/au/afl/st_kilda_saints_v_gold_coast_suns/2026-08-20" \ --data-urlencode "bookie=sportsbet" \ -H "X-API-Key: YOUR_API_KEY"
Every market row carries a tier. It tells you how comparable the row is
across books — it does not tell you whether you can bet it. All three tiers
are equally bettable via market_ref, and betting the third one is the
point of this endpoint. (Whether you can also bet it by our name depends on
the tier — see "Two ways to bet" below.)
tier | What it means | What market is |
|---|---|---|
popular | On our curated grid for this sport | Our name — lines up across every book |
other | We've named it, it's just not on the grid | Our name — lines up across every book |
unmapped | We haven't named it yet | A slug of this one book's wording |
Careful with
unmapped. Itsmarketkey is derived from this bookie's own title, so it will not match another book's key for the same bet. Don't use it to compare prices across books. Useraw_market_name— the bookie's own words — when you're showing it to a human.
There are two ways to tell Place Bet what you want, and this endpoint gives you both.
market_ref | market + selection (our name) | |
|---|---|---|
| Works for | every row in this response | popular and other — the markets we've named |
| Scope | this one book, this one event | any book you have a session on |
| How it resolves | an exact id — no matching | matches our name at the bookie |
| Needs | market_ref_betting_enabled | nothing extra |
For popular and other, either works. Those are our canonical names, so
the normal sports payload you're already using is unchanged — this endpoint just
lets you discover them.
For unmapped, you must use market_ref. Its market key is a slug of
that one bookie's own wording, not a name we've agreed across books — so there's
nothing for us to match on at the bookie. That is exactly why market_ref
exists.
If you want one rule: market_ref works for everything here, so if you're
building against this endpoint, just use it throughout.
{ "epk": "aussie_rules/au/afl/st_kilda_saints_v_gold_coast_suns/2026-08-20", "bookie": "sportsbet", "site_event_id": "12345678", "bound": true, "market_ref_betting_enabled": true, "n_markets": 87, "n_selections": 1204, "n_bettable_selections": 1204, "include_unbettable": false, "filters": { "tier": null, "market": null, "include_unbettable": false }, "markets": [ { "market": "head_to_head", "label": "Head To Head", "raw_market_name": "Head To Head", "tier": "popular", "n_bettable": 2, "selections": [ { "label": "St Kilda Saints", "odds": 2.4, "line": null, "player": null, "slug": "st_kilda_saints", "market_ref": "st_kilda_saints", "proposition_id": "9876543", "bettable": true } ] }, { "market": "third_quarter_first_goalscorer", "label": "Third Quarter First Goalscorer", "raw_market_name": "3rd Quarter First Goalscorer", "tier": "unmapped", "n_bettable": 44, "selections": [ { "label": "Nasiah Wanganeen-Milera", "odds": 9.0, "line": null, "player": "Nasiah Wanganeen-Milera", "slug": "nasiah_wanganeen_milera", "market_ref": "nasiah_wanganeen_milera", "proposition_id": "9876599", "bettable": true } ] } ] }
| Field | Meaning |
|---|---|
bound | Whether this bookie is attached to this event at all. false ⇒ no bet can be routed to it. |
site_event_id | The bookie's own id for the event. |
market_ref_betting_enabled | Whether market_ref betting is switched on for this book. Read it before you loop — see below. |
n_selections / n_bettable_selections | Totals for the response after your filters. |
markets[].market | Our key for the market — comparable across books unless tier is unmapped. |
markets[].label | Human-readable version of market. |
markets[].raw_market_name | The bookie's own title. Show this one to people. |
markets[].n_bettable | How many of this market's selections you can actually place. |
selections[].market_ref | The string you send to Place Bet. Present only on bettable rows. |
selections[].odds | Price at the last scrape. Confirm with Price Check before staking. |
selections[].line / player | The handicap/total and the player, where the market has them. |
Markets come back popular → other → unmapped, and within a tier the ones
with the most bettable selections first.
That's the whole point. Take the market_ref and send it straight to
Place Bet — no market name, no selection
name, no matching on your side:
{ "session_id": "YOUR_SESSION_ID", "sport": "aussie_rules", "epk": "aussie_rules/au/afl/st_kilda_saints_v_gold_coast_suns/2026-08-20", "market_ref": "nasiah_wanganeen_milera", "stake": 10 }
A market_ref is computed from one bookie's card for one event, so it's
only valid for the bookie you asked for. Don't send a market_ref from one
book to a session on another.
site_event_idSome books publish an event only minutes before it starts (Sportsbet's fast
table tennis leagues, for example, list roughly 15–30 minutes out). We scrape
the new card within about a minute — but linking it to the event's epk
takes a few minutes more, and until that happens this endpoint answers
bound: false with no markets, even though the book's own site is already
taking bets.
If you already know the book's own event id, pass it as site_event_id
and we serve the card from that id directly, without waiting for us to link
it:
curl -s -G "https://api.b337.ai/v3/markets" \ --data-urlencode "epk=table_tennis/pl/tt_elite_series/frantisek_krcil_v_michal_minda/2026-09-29" \ --data-urlencode "bookie=sportsbet" \ --data-urlencode "site_event_id=10994944" \ -H "X-API-Key: YOUR_API_KEY"
The bookie's own id for the match, from the bookie's site. For Sportsbet it is the number at the end of the match page's address:
https://www.sportsbet.com.au/betting/table-tennis/…/frantisek-krcil-v-michal-minda-10994944
^^^^^^^^
site_event_id = 10994944
It is best suited to books like Sportsbet whose event id is a plain number on
their own site. epk is still required — the slower books usually list the
match hours ahead, so you will have it early.
Until we have linked the book to the event ourselves, we have not checked
that your site_event_id is the match your epk names — you are vouching
for it. If the id you send is wrong, or belongs to a different match from
the one you intended, you get that other match's card, its market_refs, and
— if you place — a bet on that other match. You are solely responsible for
the site_event_id you supply and for any bet placed with it. We accept no
liability for a bet placed on the wrong event through this parameter, since
by using it you are choosing to bet before our own linking has confirmed the
event. Take the id from the bookie's page for the exact match you want.
When you send site_event_id, the response carries
site_event_id_source: "caller", spine_site_event_id (the id we have
linked — null until we link it) and site_event_id_check:
site_event_id_check | Meaning | Markets |
|---|---|---|
unbound | We have not linked this book to the event yet. Served on your say-so — bound stays false. | Your id's card |
confirmed | We have linked it, and your id is one we linked to this event. | The card |
conflict | We have linked this event to a different match at the book than the id you sent. | None — drop site_event_id and use epk alone |
no_card | We have linked it elsewhere, and we hold no card for your id. | None |
Once we have linked the event, our link takes over: an id for a different match is refused rather than served.
Price Check and
Place Bet accept the same
site_event_id alongside market_ref and apply the same check — a
conflict there is a 409 site_event_id_conflict and no bet is sent. The
bet status and result webhook show dispatched.site_event_id_source
(caller_unbound when the bet went down on your id before we had linked the
event).
If you send an id we hold no card for yet (unbound, empty markets),
brand-new listings appear within a scrape cycle (~30–60s), so retry shortly.
By default we only return selections we can place safely. A selection is left out when either:
So if a market isn't in the response, the correct reading is "we can't place this by id", not "the book doesn't offer it". Don't fall back to some other route on a miss.
include_unbettable=true widens the answer from "what can I place" to "what's
on the card at all". The dropped selections come back with bettable: false
and a reason, and markets where nothing is placeable appear too:
{ "market": "weird_bookie_special", "raw_market_name": "Milestone Markets", "tier": "unmapped", "n_bettable": 0, "selections": [ { "label": "Yes", "odds": 3.0, "slug": "yes_weird_bookie_special", "proposition_id": null, "bettable": false, "reason": "no_proposition_id" } ] }
These rows are informational only — there's no market_ref and nothing to
send. reason is no_proposition_id or ambiguous_proposition_id.
market_ref_betting_enabled firstWhether a selection is identifiable and whether market_ref betting is
switched on for that book are two different things. The second is a per-book
setting on our side. If market_ref_betting_enabled is false, everything in
the response is readable but Place Bet will reject it with a 422 — so check
it once rather than finding out one bet at a time.
120 requests/minute per API key. Over it you get a 429 with a
Retry-After header.
A bookie's card only changes when we re-scrape it — minutes apart — so cache
the response rather than polling it. If you're walking many events, narrow each
call with tier= or market=.
GET /v3/events → pick an epk, and note
which books it's bound to.GET /v3/markets?epk=...&bookie=sportsbet → find the market you want.POST /v3/price_check → confirm the
current price.POST /v3/place_bet with the
market_ref and a stake.Before we have linked the bookie to the event (a fast-listing book such as
Sportsbet's fast table tennis, in its first few minutes — bound: false in
step 2):
GET /v3/events → the epk (the slower
books list these matches hours ahead, so it exists already).GET /v3/markets?epk=...&bookie=sportsbet&site_event_id=... → check
site_event_id_check is unbound or confirmed, and find the
market_ref.POST /v3/price_check and
POST /v3/place_bet with
market_ref + site_event_id + epk. Always send the epk too:
it is what lets us refuse the bet if the id turns out to be a different
match. Until we have linked the event, the id is your responsibility
(see "You are responsible for the id" above).404 — unknown epk. List valid ones with GET /v3/events.422 — unknown tier (the response carries valid_tiers).401 — unauthorized.429 — rate limited; see Retry-After.503 — markets temporarily unavailable.Two non-errors worth handling separately, because they mean different things:
200 with "bound": false — this bookie isn't attached to this event.
Check the event's books list — or, if the book's own site already lists
it, pass site_event_id (see "Fast-listing books" above).200 with "bound": true and an empty markets — the bookie is attached,
but nothing on its card is placeable by id right now.market_ref you just foundepk this endpoint needs