CLAUDE.mdepks — self-contained for any AI assistant. Base URL: https://api.b337.ai.<paste-your-api-key-here> placeholder./v3/place_bet is async.202 in ~100 ms with a correlation_id.POST /api/bet_status with that correlation_id until status is success/failure (deadline ~60s) — or provide callback_url and the API will POST the signed result to you./v3/place_bet is the new recommended way to place bets. It returns immediately
(~100 ms) with a correlation_id and decouples your script from the actual
bookie wait, so you no longer get 30s timeouts when a bookie is slow.
The legacy /api/place_bet and /v2/place_bet endpoints hold the HTTP
connection open while the server places the bet on the bookie. Slow bookies
cause:
/v3/place_bet fixes all of this by returning a correlation_id
in ~100 ms. You then either poll for the result or have it POSTed back to
your URL when ready.
POST /v3/place_bet
Headers:
X-API-Key: YOUR_API_KEYContent-Type: application/jsonIdentical to /v2/place_bet. Any payload that works on v2 works on v3
unchanged. Plus one optional field:
| Field | Type | Required | Description |
|---|---|---|---|
callback_url | string | No | If provided, the result will be POSTed to this URL when the bet completes. Must be http:// or https://. If absent, you poll for the result instead. |
Supported categories: racing, sports, multi (single, SRM, SGM, parlay),
and betfair (direct Exchange BACK/LAY, incl. BSP — see
Place Bet → Betfair).
Stacked SRMs now have a dedicated v3 endpoint -
POST /v3/stacked_srm - with the
same async contract as this one.
{ "status": "pending", "correlation_id": "9b3c…uuid…", "session_id": "uuid", "bookie": "tab", "username": "user@example.com", "submitted_at": "2026-05-07T10:00:00.000Z", "timeout_at": "2026-05-07T10:05:00.000Z", "callback_url": null }
status is always "pending" at this point. Use the correlation_id to
fetch the actual bet result via either delivery option below.
POST /api/bet_status with the correlation_id until status flips from
pending to completed (or timeout).
curl -X POST https://api.b337.ai/api/bet_status \ -H "X-API-Key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"correlation_ids": ["9b3c…uuid…"]}'
Response:
{ "statuses": [ { "correlation_id": "9b3c…uuid…", "status": "completed", "session_id": "...", "bookie": "tab", "username": "...", "submitted_at": "...", "timeout_at": "...", "result": { "success": true, "bet_result": { "success": true, "bet_id": "TAB-12345", "odds": 3.5, "stake": 10.0, "error": null } } } ] }
When status === "completed", read the result field. result.success
is whether the bet placed; the bookie receipt (bet_id, odds, stake,
error) is nested under result.bet_result.
status values:
pending - bet is in flight, keep polling.completed - bet finished, read result.timeout - server gave up waiting for the client. Treat as a transient failure.unknown - correlation_id expired (over 30 minutes since submission).Provide callback_url in the original /v3/place_bet request and the result
will be POSTed to your URL when ready. Use this if you already run an HTTP server.
Requirements: your URL must be reachable from api.b337.ai and
respond 2xx within 10 seconds.
Inbound POST shape:
POST {your callback_url}
Headers:
Content-Type: application/json
Bet-Correlation-Id: {correlation_id}
Bet-Signature: sha256=<HMAC-SHA256(api_key, body)>
Header rename: as of 2026-05-14 these headers are
Bet-SignatureandBet-Correlation-Id- previouslyX-Bet337-SignatureandX-Bet337-Correlation-Id. The "X-" prefix was dropped per RFC 6648 and the "Bet337" namespace removed for whitelabel-friendliness. Header values are unchanged. If you have existing handlers reading the old names, update the lookup - verification logic itself doesn't change.
{ "correlation_id": "9b3c…uuid…", "result": { "success": true, "bet_id": "TAB-12345", "odds": 3.5, "stake": 10.0, "potential_return": 35.0 }, "metadata": { "bookie": "tab", "username": "user@example.com", "session_id": "..." }, "delivered_at": "2026-05-07T10:00:02.500Z" }
Verifying the signature (Python):
import hmac, hashlib def verify_callback(api_key: str, raw_body: bytes, header_sig: str) -> bool: expected = "sha256=" + hmac.new( api_key.encode("utf-8"), raw_body, hashlib.sha256 ).hexdigest() return hmac.compare_digest(expected, header_sig)
If the receiver fails, we retry 3 times with exponential backoff
(1s, 4s, 16s). After that the delivery is dead-lettered server-side; you
can still recover the result by polling /api/bet_status with the
correlation_id for up to 30 minutes.
import requests import time API_KEY = "YOUR_API_KEY" BASE_URL = "https://api.b337.ai" HEADERS = {"X-API-Key": API_KEY, "Content-Type": "application/json"} def place_bet_v3(payload: dict, poll_budget: float = 60.0) -> dict: """Place a bet via /v3/place_bet and poll until it completes. Returns the same dict shape that /v2/place_bet used to return: {"success": bool, "bet_id": ..., "odds": ..., "stake": ..., ...} """ # Step 1: submit (returns in ~100ms) submit = requests.post( f"{BASE_URL}/v3/place_bet", headers=HEADERS, json=payload, timeout=15, ) submit.raise_for_status() cid = submit.json()["correlation_id"] # Step 2: poll /api/bet_status until completed/timeout deadline = time.time() + poll_budget while time.time() < deadline: status = requests.post( f"{BASE_URL}/api/bet_status", headers=HEADERS, json={"correlation_ids": [cid]}, timeout=10, ) status.raise_for_status() entry = status.json()["statuses"][0] if entry["status"] == "completed": return entry["result"] if entry["status"] in ("timeout", "unknown"): return {"success": False, "error": entry["status"], "transient": True} time.sleep(1) return {"success": False, "error": "Poll budget exhausted", "transient": True} # Usage - payload is identical to /v2/place_bet result = place_bet_v3({ "session_id": "your-session-uuid", "category": "racing", "stake": 10.0, "track": "Flemington", "race_num": 5, "race_type": "(R)", "date": "2026-05-08", "runner": "Horse Name", "market": "win", "target_odds": 3.5, }) if result["success"]: print(f"✓ Placed at {result['odds']} for ${result['stake']}") else: print(f"✗ {result.get('error')}")
Drop-in replacement: change the URL and read result from the poll response
instead of the direct response.
| Aspect | v2 sync (/v2/place_bet) | v3 async (/v3/place_bet) |
|---|---|---|
| Returns | Bet result directly, after a 30s+ wait | {correlation_id, …} in ~100ms |
| Timeouts | Frequent 504s on slow bookies | Decoupled; HTTP request never times out on the bet itself |
| Retrieving result | Synchronous response body | Poll /api/bet_status or webhook |
| Payload shape | Same | Same (plus optional callback_url) |
stacked_srm | Supported | Moved to /v3/stacked_srm (same async contract) |
betfair | Supported | Supported (BACK/LAY + BSP) |
Once you read statuses[0].result from /api/bet_status: result.success
is placed/not, and the bookie receipt (bet_id, odds, stake) is nested
under result.bet_result.
while loop, so this should be very rare.