This guide covers placing a sports bet end to end: the payload shape, how preset sports and leagues are named, browsing today's events in the Sports Tree, and the optional event key you can attach for exact event targeting.
A sports bet is a POST /v3/place_bet call with "category": "sports".
The Manual Betting page fills these exact same fields when you place a
bet from the dashboard — this is the raw payload underneath that form.
curl -X POST https://api.b337.ai/v3/place_bet \ -H "X-API-Key: <your key>" \ -H "Content-Type: application/json" \ -d '{ "session_id": "11142", "category": "sports", "competition": "afl", "event": "Carlton v Collingwood", "market": "head_to_head", "selection": "home", "stake": 50.00, "target_odds": 1.85 }'
Response (202 Accepted) — the standard async envelope every /v3
endpoint returns:
{ "status": "pending", "correlation_id": "9b3c…uuid…", "submitted_at": "2026-07-13T03:11:00.000Z", "timeout_at": "2026-07-13T03:12:00.000Z" }
Poll /api/bet_status with the correlation_id to get the actual bet
result (fill price, success/failure) — see
Async API for the full polling contract.
| Field | Type | Required | Notes |
|---|---|---|---|
session_id | string | ✅ | Must belong to caller's API key and be active. |
category | "sports" | ✅ | Constant. |
competition | string | ✅ | League string, e.g. "afl", "nba", "epl". |
event | string | ✅ | Match name as the bookie shows it, e.g. "Carlton v Collingwood". |
market | string | ✅ | "head_to_head", "line", "total_points", a player-prop market, etc. — see the full catalog on the Place Bet page, and Market naming & line conventions below for the few cases where the canonical Sports Tree name isn't the id you send. |
selection | string | for single bets | "home" / "away" / "over" / "under" / team / player, depending on the market. |
sport | string | optional | Some integrations also send the preset sport/league key (e.g. "afl") alongside competition. The server resolves primarily off competition + event. |
player | string | for player props | Player name as the bookie shows it. |
line | number | for O/U + handicaps | Threshold, e.g. 25.5. |
start_time_iso | string | optional | ISO-8601 kickoff time, when known — helps disambiguate two teams that play each other twice in a day. |
stake | number | ✅ | Dollars. |
target_odds | number | ✅ | Floor — bet rejected below this. |
is_same_event_multi | bool | for SGM | Same Game Multi — supply legs[] instead of selection/player. |
epk | string | optional, recommended | Event key from the Sports Tree — the server resolves it to the bookie's own event name before the bet reaches the bookie session (id-based resolution is rolling out separately). See Event keys below. |
Important. This is the same field set the Manual Betting form on the dashboard sends — if you've placed a sports bet through the UI, you've already produced one of these payloads. For the full market catalog and an interactive payload builder per sport, see Place Bet — Sports.
The dashboard's sport picker offers a fixed list of preset sports/leagues.
These are the values that go in competition (and sometimes sport):
AFL, NRL, NBA, NBL, NFL, Soccer, Tennis, MLB, NHL,
Cricket, Golf, MMA, Boxing, Rugby Union, Rugby League,
WNBA, Darts, NPB (Baseball), KBO (Baseball), CPBL (Baseball),
MLS (Soccer), Table Tennis, Snooker, NCAA Basketball,
NCAA Football, NCAA Hockey, CFL, UFL, Basketball (Other),
Baseball (Other), Football (Other).
Under the hood these league-level values fold into 16 broader sport groups for browsing (this folding is display-only — it never changes how a bet is matched):
| Sport group | Leagues folded in |
|---|---|
| Basketball | NBA, WNBA, NBL, NCAAB, Basketball (Other) |
| American Football | NFL, NCAAF, CFL, UFL, Football (Other) |
| Baseball | MLB, KBO, NPB, CPBL, Baseball (Other) |
| Ice Hockey | NHL, NCAA Hockey |
| Aussie Rules | AFL, Aussie Rules (Other) |
| Rugby League | NRL |
| Rugby Union | Rugby Union |
| Soccer | Soccer, MLS |
| MMA | MMA, UFC |
| Boxing | Boxing |
| Tennis | Tennis |
| Table Tennis | Table Tennis |
| Darts | Darts |
| Snooker | Snooker |
| Golf | Golf |
| Cricket | Cricket |
In short: competition (and sport, when sent) identify the specific
league (e.g. "nba"), while the sport group (e.g. "Basketball") is
the umbrella category leagues sit under when you're browsing rather than
placing a bet by exact fields.
The dashboard's Sports Tree view (/terminal/sports/tree —
Sport → Country → League → Event) lets you browse today's sports events
instead of typing fields blind. Every event is identified by an event
key of the form sport/country/league/event/date, for example:
basketball/us/nba/cavaliers_v_knicks/2026-07-13
Underneath each event the Tree also carries canonical market names
(head_to_head, total_points, player_disposals, …) and structured
selections — a market's real shape (player / stat / line / direction /
period) rather than a single free-text string. Browsing the Tree is the
fastest way to confirm the exact competition/event/market strings for
a match before placing a bet, and the market catalog you see per sport in
the interactive builders on this page and on
Place Bet is drawn from that same
canonical set — see Market naming & line conventions below for the
handful of cases where the canonical name and the id you actually send
differ.
Drill sport group → country → league → event → market/selection — the payload, event key (epk), and curl below update live.
basketball/us/nba/lakers_v_celtics/2026-10-01{
"session_id": "11142",
"category": "sports",
"competition": "nba",
"event": "Lakers v Celtics",
"market": "head_to_head",
"selection": "home",
"stake": 50,
"target_odds": 1.85,
"epk": "basketball/us/nba/lakers_v_celtics/2026-10-01"
}curl -X POST https://api.b337.ai \
-H "X-API-Key: demo" \
-H "Content-Type: application/json" \
-d '{
"session_id": "11142",
"category": "sports",
"competition": "nba",
"event": "Lakers v Celtics",
"market": "head_to_head",
"selection": "home",
"stake": 50,
"target_odds": 1.85,
"epk": "basketball/us/nba/lakers_v_celtics/2026-10-01"
}'Every sports event has an event key: a stable identity string of the form:
{sport_group}/{country}/{league}/{home}_v_{away}/{date}
Examples:
basketball/us/nba/cavaliers_v_knicks/2026-07-13
aussie_rules/au/afl/richmond_v_carlton/2026-07-18
soccer/intl/soccer/melbourne_victory_v_sydney_fc/2026-07-15
Outrights / futures (tournament winner, etc.) use outright_{subkey} in
the event slot instead of {home}_v_{away}.
You can attach the event key to any sports bet payload as an optional
"epk" field for exact event targeting:
{ "category": "sports", "stake": 20, "sport": "aussie_rules", "event": "Richmond v Carlton", "competition": "AFL", "market": "head_to_head", "selection": "Richmond", "target_odds": 1.85 }
becomes:
{ "category": "sports", "stake": 20, "sport": "aussie_rules", "event": "Richmond v Carlton", "competition": "AFL", "market": "head_to_head", "selection": "Richmond", "target_odds": 1.85, "epk": "aussie_rules/au/afl/richmond_v_carlton/2026-07-18" }
epk is fully optional and backwards compatible — it works exactly
like racing's optional pk field. Omit it and the server matches the event
from your other fields (sport/event/competition) as it always has.
Live today: name-based event identity. Server-side
epkresolution is live — when you send it, the server maps it against the Sports Tree and threads the bookie's own event name (its exact spelling, e.g. TAB's abbreviated"Sydney v Wst Bulldogs") into the bet before it reaches the bookie session, instead of the client guessing name variants. We recommend always sendingepk— grab it from the Sports Tree browser or the interactive builder above. Not live yet: forwarding the Sports Tree's market/selection ids straight into bookie placement — that's a separate, progressively-rolling-out path (gated per bookie), so don't rely on id-based placement happening today just because you sentepk. See Resolution & fallback below.
A sports bet resolves in a few steps, each one only kicking in if the step before it couldn't fully pin the bet down. None of them can make a bet fail outright — a gap in the Sports Tree's mapping only ever degrades match quality, it never blocks placement:
epk (live today). If you sent an epk, the
server maps it through the Sports Tree and threads the bookie's own event
name into the bet. This is name-based, not id-based — it gets the
client session looking at the exact right event under the bookie's own
spelling, rather than resolving straight to a market/selection id.market /
selection / player / line fields the same way it always has. This
is the primary matching path for every bet today, epk or not.epk didn't resolve an event at all (not
sent, or the Tree has no mapping for it yet), the client falls all the way
back to matching purely on competition + event + market +
selection text — exactly how sports bets were matched before the
Sports Tree existed.Rolling out, not live: the server forwarding the Sports Tree's own
market/selection ids straight into bookie placement (skipping name search
entirely) is a separate, per-bookie canary-gated path, still being rolled
out. Don't assume a bet placed today skipped name matching just because you
sent epk — assume name search ran, and treat id-based placement as a
future speed/precision improvement layered on top of the same fallback
chain, not a present guarantee.
"N+" == Over N - 0.5 (for the folded stat families only)Bookies commonly display certain player-stat thresholds as "10+ Disposals", "15+ Points", etc. For the plain stat families —
disposals/marks/tackles/handballs/clearances/hitouts (AFL), points/
rebounds/assists/threes/steals/blocks/pra (basketball), tackles/runs (NRL) —
the platform folds the bookie's tiered *_threshold market
(player_disposals_threshold, player_pts_threshold, …) into the plain
over/under form at the same canonical id (player_disposals,
player_points, …), one line below the threshold: "10+" is
line: 9.5, selection: "over". You can send either shape and get the same
market — there's no separate threshold-specific market id to learn for these:
{ "player": "Marcus Bontempelli", "market": "player_disposals", "line": 28.5, "selection": "over" }
is equivalent to asking for "29+ Disposals".
This does NOT extend to goal/try scorer thresholds — "Charlie Cameron to kick 2+ Goals", "to score 2+ tries", etc. These stay their own
canonical buckets (goalscorer_threshold_afl for AFL, goalscorer_threshold
for soccer, try_scorer_threshold for NRL/rugby) and are not folded into
the plain over/under shape above. For these:
selection is the player name, not "over"/"under".line is the literal threshold N — most bookies expect a plain whole
number ("1", "2", "3"…, not N - 0.5), though a few accept
half-lines for the same market, so it's not standardised cross-bookie. Read
the bookie's offered lines from a 400 rejection, or check
Price Check first.{ "category": "sports", "stake": 15.0, "sport": "AFL", "competition": "AFL", "event": "Brisbane v Essendon", "market": "goalscorer_threshold_afl", "selection": "Charlie Cameron", "player": "Charlie Cameron", "line": "2", "target_odds": 1.91 }
The Sports Tree's canonical market catalog is the source of truth for market
naming (head_to_head, total_points, player_disposals, …), and for
almost every market the string you send is identical to that canonical name
— that's exactly what the builders above generate. A small number of
markets have a canonical Sports Tree name that differs from (or has no
equivalent in) what the client bookie sessions resolve today; for those,
send the id in the "Send this id" column, not the raw canonical name:
| Sport | Canonical name (Sports Tree) | Send this id |
|---|---|---|
| Soccer | head_to_head (the Sports Tree's own label for soccer's match-result market) | match_result_3way — soccer's home/draw/away result normalizes to the dedicated 3-way bucket client-side; head_to_head is reserved for genuine 2-way winner markets and won't match a soccer bookie's "Match Result"/"1X2" listing. |
| American Football | player_passing_yards | passing_yards |
| American Football | player_rushing_yards | rushing_yards |
| American Football | player_receiving_yards | receiving_yards |
| American Football | anytime_touchdown_scorer / first_touchdown_scorer / last_touchdown_scorer | touchdown_scorer (one anytime-TD bucket — first/last aren't separately resolvable yet) |
A further handful of canonical Sports Tree markets — AFL's
combined_player_goals/combined_player_disposals/player_goals_duel/the
most_*_group ranking markets/disposal_win_double; American Football's
player_passing_touchdowns/player_rushing_touchdowns/
player_receiving_touchdowns/player_receptions/player_sacks;
Baseball's player_stolen_bases; Cricket's player_of_the_match/
player_sixes/player_fours; and Basketball's bookie-specific
player_first_half_*/player_first_quarter_* combo props — don't have a
client-resolvable id at all yet. They're intentionally left out of the
interactive builders above; if you need one of them today, contact support
rather than guessing a string.
Everything else — every market id shown in the builders on this page and on Place Bet, across every sport — is verified to resolve as-is.
The market list and interactive bet builder on this page are a curated
subset, not an exhaustive catalog: our docs list the markets we normalize
and compare across bookies, filtering out ones we can't yet compare cleanly
— same-game combos, period-scoped markets whose exact period (1st quarter,
1st half, an innings, a set, …) can't be determined with certainty rather
than guessed, and finished events. A market not being listed here does not
mean your bookie doesn't carry it. The betting
API has no such filter — /v3/place_bet accepts any market/selection
string your bookie's own catalogue actually offers, whether or not it's one
of the markets these docs currently normalize.
Recommended flow before staking on something you don't see listed here:
market/selection/player/line fields you'd use
for /v3/place_bet to
POST /v3/price_check instead
(drop stake/target_odds). This is read-only — it asks the bookie
session to resolve the selection against its live catalogue and return the
current price; nothing is staked./api/bet_status for the result, same as any /v3 call./v3/place_bet using the same fields (plus
target_odds as your floor).price_check validates the
selection against the bookie's live catalogue exactly as placement does —
a market that price-checks clean resolves the same way when you place it
for real. (TAB's id-based price check goes further: it runs the exact
same two-step pricing enquiry placement uses, just with the enquiry's
stake set to $0.00 so nothing is staked — the price returned is the
price a real bet would get.){ "success": false, "soft_miss": true, "error": "market_not_carried: 'player_receptions' not offered for Chiefs v Bills. Available: [\"h2h\", \"total_points\", \"spread\", \"player_passing_yards\", ...]" }
soft_miss and the Available list (every market key that bookie
actually parsed for the event) are bookie-specific — TAB's response
includes both; some bookies return only the bare market_not_carried
message. Read Available off the rejection when it's there rather than
guessing a different string.