See the promos your accounts hold right now - Mega Boosts, odds boosts, savers, bonus bets, SGM savers - and place the ones that are a single bet with one call, by id.
Base URL: https://api.b337.ai
This is the same list the Manual Betting page shows, with the same wording. Everything here can also be placed from the Manual Betting page.
When a session logs in, it reads the offers the bookie is showing that
account and stores them. Each one becomes an account promo with a stable
promo_id. Most promos are tokens you spend on a selection you choose
(a saver on any runner in a race, a bonus bet on any market). A few are a
whole bet the bookie has already built and priced - a bet365 Mega Boost
("Horse A or Horse B (Either to Win) 1.50 → 2.00") is one. Those have exactly
one possible bet, so you can place them by id alone.
GET /v3/account_promos {#list}Synchronous read. Only active promos are returned; no bookie is contacted.
| Field | Type | Required | Description |
|---|---|---|---|
session_id | string | No | Only this session's account. Must be your session (403 otherwise). Omit to list every account on your key. |
bookie | string | No | Filter by bookie, e.g. bet365. |
curl "https://api.b337.ai/v3/account_promos?session_id=900" \ -H "X-API-Key: YOUR_API_KEY"
{ "success": true, "count": 2, "promos": [ { "promo_id": 363983, "session_id": "900", "session_ids": ["900"], "account_id": "uuid", "bookie": "bet365", "promo": "bet_boost", "label": "Mega Boost", "summary": { "label": "Mega Boost", "headline": "Rosehill R8 (R)", "detail": "Horse A or Horse B (Either to Win) · 1.50 -> 2.00" }, "kind": "racing", "epk": "rosehill/8/(R)/au/2026-09-26", "track": "Rosehill", "race_num": 8, "race_type": "(R)", "date": "2026-09-26", "card_id": "3209180", "voucher_type": "pba", "selection": "Horse A or Horse B (Either to Win)", "base_odds": 1.5, "boosted_odds": 2.0, "placeable_by_id": true, "how_to_bet": { "endpoint": "/v3/place_promo" } }, { "promo_id": 363990, "session_id": "900", "session_ids": ["900"], "account_id": "uuid", "bookie": "bet365", "promo": "2/3", "label": "2nd or 3rd", "summary": { "label": "2nd or 3rd", "headline": "Rosehill R7 (R)", "detail": "Run 2nd or 3rd, get your stake back" }, "kind": "racing", "track": "Rosehill", "race_num": 7, "race_type": "(R)", "date": "2026-09-26", "placeable_by_id": false, "how_to_bet": { "endpoint": "/v3/place_bet", "promo_name": "2/3", "note": "This promo has more than one possible bet: choose the selection and place it with POST /v3/place_bet and promo_name." } } ] }
| Field | Type | Description |
|---|---|---|
promo_id | int | The promo's id. Pass this to /v3/place_promo. |
session_id | string | null | The session you asked about; otherwise your first active session on that account (null when none is running). |
session_ids | string[] | All your active sessions on that account. |
bookie | string | Bookie the account belongs to. |
promo | string | Promo code (bet_boost, boost, 2/3, bonus, …). |
label / summary | string / object | Our customer-facing wording, as on the website. |
kind | string | racing, sports or deposit. Racing rows carry track, race_num, race_type, date; sports rows sport, competition, event, date. |
card_id, voucher_type, selection, base_odds, boosted_odds | Bet Boost cards only. boosted_odds is the card's own price and is absent until it has been captured. | |
placeable_by_id | bool | true only when the promo has exactly one possible bet (see below). |
how_to_bet | object | Where to place it - see below. |
placeable_by_idtrue only for a bet365 Bet Boost card (e.g. a Mega Boost) of a single
fixture - a single racing selection, or a single sports game - whose boosted
price has been captured. Multi / outright cards and cards with no captured
price are false.
how_to_bet| Value | Meaning |
|---|---|
{"endpoint": "/v3/place_promo"} | Place it by id (section 2). |
{"endpoint": "/v3/place_bet", "promo_name": "…", "note": "…"} | A token: pick your selection and place via /v3/place_bet with this promo_name. |
{"endpoint": null, "note": "…"} | Not placeable through the API yet (the note says why), or not a bet (deposit offers). |
POST /v3/place_promo {#place}Places a promo whose placeable_by_id is true. Send no price,
selection or market - the card already names them. At bet time the session
re-reads the card from the bookie and refuses if it has gone or is no longer
boosted.
| Field | Type | Required | Description |
|---|---|---|---|
session_id | string | Yes | Active session on the account that holds the promo |
promo_id | int | Yes | promo_id from GET /v3/account_promos |
stake | float | Yes | Stake in dollars |
callback_url | string | No | Receive the signed result POST here instead of polling |
idempotency_key | string | No | 1-200 chars. A retry with the same key returns the same correlation_id and places nothing new - even after the promo has been used. |
trace_id | string | No | Your own id, echoed back everywhere |
{ "session_id": "900", "promo_id": 363983, "stake": 10, "idempotency_key": "mega-boost-rosehill-r8" }
The same async envelope as /v3/place_bet,
plus the promo_id:
{ "status": "pending", "correlation_id": "9b3c…uuid…", "session_id": "900", "bookie": "bet365", "username": "user@example.com", "submitted_at": "2026-09-26T04:00:00.000Z", "timeout_at": "2026-09-26T04:05:00.000Z", "callback_url": null, "trace_id": null, "promo_id": 363983 }
Poll POST /api/bet_status with {"correlation_ids": ["…"]} every 1-2s
until status is completed or timeout, or pass callback_url to
receive a signed webhook. Details in the
Async API (v3) guide. The bet is recorded as a
promo bet (bet_type: "promo", promo_name: "bet_boost").
import requests, time BASE_URL = "https://api.b337.ai" HEADERS = {"X-API-Key": "YOUR_API_KEY", "Content-Type": "application/json"} promos = requests.get(f"{BASE_URL}/v3/account_promos", headers=HEADERS, params={"bookie": "bet365"}).json()["promos"] card = next(p for p in promos if p["placeable_by_id"] and p["session_id"]) sub = requests.post(f"{BASE_URL}/v3/place_promo", headers=HEADERS, json={ "session_id": card["session_id"], "promo_id": card["promo_id"], "stake": 10, "idempotency_key": f"promo-{card['promo_id']}", }).json() while True: st = requests.post(f"{BASE_URL}/api/bet_status", headers=HEADERS, json={"correlation_ids": [sub["correlation_id"]]}).json() entry = st["statuses"][0] if entry["status"] != "pending": print(entry) break time.sleep(2)
| Status | When |
|---|---|
400 | session_id, promo_id or stake missing |
403 | Betting not enabled on your key; the session is not yours; the session is not active |
404 | The promo is not held by this session's account |
422 | The promo is no longer active (used, expired, gone or dismissed), or it is not placeable by id - the message says why and, for tokens, points you to /v3/place_bet with promo_name |
Promos with more than one choice (racing boosts, savers, bonus bets) are still placed via
/v3/place_betwithpromo_name. See Bonus Bets & Promos for the token flags.