Place a Bet
End-to-end walkthrough for placing a bet against Vertex: discover a sport, find a scheduled event, pick a selection, resolve your account references, and POST /bets. Every example references $VERTEX_TOKEN from the Quickstart setup.
1. List sportsβ
Confirm the sport you want to bet on is available.
- curl
- JavaScript
- Python
curl -s -H "Authorization: Bearer $VERTEX_TOKEN" \
"https://trading.vertex-api.com/api/v2/sports"
const BASE = 'https://trading.vertex-api.com/api/v2';
const headers = { Authorization: `Bearer ${process.env.VERTEX_TOKEN}` };
const sports = await fetch(`${BASE}/sports`, { headers }).then(r => r.json());
console.log(sports.data);
import os, requests
BASE = "https://trading.vertex-api.com/api/v2"
headers = {"Authorization": f"Bearer {os.environ['VERTEX_TOKEN']}"}
sports = requests.get(f"{BASE}/sports", headers=headers).json()
print(sports["data"])
Response:
{
"data": [
{ "id": 1, "name": "Soccer", "code": "soc" },
{ "id": 6, "name": "Ice Hockey", "code": "ih" },
{ "id": 5, "name": "American Football", "code": "usf" }
]
}
We'll use 6 (Ice Hockey) for the rest of this walkthrough because it's reliably populated.
2. Find a scheduled eventβ
List upcoming events for your chosen sport. Use expand=participants to get team names. Without it the response only contains IDs.
- curl
- JavaScript
- Python
curl -s -H "Authorization: Bearer $VERTEX_TOKEN" \
"https://trading.vertex-api.com/api/v2/events?sport_id=6&status=scheduled&expand=participants"
const events = await fetch(
`${BASE}/events?sport_id=6&status=scheduled&expand=participants`,
{ headers }
).then(r => r.json());
const event = events.data[0];
console.log(event.id, event.participants.map(p => `${p.role}: ${p.name}`));
events = requests.get(
f"{BASE}/events",
params={"sport_id": 6, "status": "scheduled", "expand": "participants"},
headers=headers,
).json()
event = events["data"][0]
print(event["id"], [(p["role"], p["name"]) for p in event["participants"]])
Response (trimmed to one event, pagination object omitted):
{
"data": [
{
"id": 6717,
"sport_id": 6,
"competition_id": 11,
"start_time": "2026-05-23T00:00:00Z",
"status": "scheduled",
"participants": [
{ "id": 19, "name": "Colorado Avalanche", "role": "away", "type": "team" },
{ "id": 11, "name": "Vegas Golden Knights", "role": "home", "type": "team" }
]
}
]
}
Capture the id of the event you want to bet on.
Event status values (scheduled, live, finished, cancelled) are returned in lowercase.
3. Load markets and pick a selectionβ
Fetch the markets for your event with selections and stake limits expanded. Prices live inside each selection, so expand=selections is required, and stake_limits tells you how much you can stake before you build the bet.
- curl
- JavaScript
- Python
EVENT_ID=6717
curl -s -H "Authorization: Bearer $VERTEX_TOKEN" \
"https://trading.vertex-api.com/api/v2/events/$EVENT_ID/markets?expand=selections,stake_limits"
const eventId = 6717;
const markets = await fetch(
`${BASE}/events/${eventId}/markets?expand=selections,stake_limits`,
{ headers }
).then(r => r.json());
// Find the moneyline market with an active Vertex price (pricing_provider_id: 1).
// Key off market_type.code: several market types (including player props) share segment.code 'result'.
const moneyline = markets.data.find(m => m.market_type.code === 'moneyline');
const selection = moneyline.selections.find(s =>
s.prices.some(p => p.pricing_provider_id === 1 && p.status === 'active')
);
const price = selection.prices.find(p => p.pricing_provider_id === 1);
console.log({ selection_id: selection.id, price: price.price });
event_id = 6717
markets = requests.get(
f"{BASE}/events/{event_id}/markets",
params={"expand": "selections,stake_limits"},
headers=headers,
).json()
# Key off market_type.code: several market types (including player props) share segment.code "result"
moneyline = next(m for m in markets["data"] if m["market_type"]["code"] == "moneyline")
selection = next(
s for s in moneyline["selections"]
if any(p["pricing_provider_id"] == 1 and p["status"] == "active" for p in s["prices"])
)
price = next(p for p in selection["prices"] if p["pricing_provider_id"] == 1)
print({"selection_id": selection["id"], "price": price["price"]})
Response (trimmed to one selection):
{
"data": [
{
"id": 63959,
"event_id": 6717,
"segment": { "id": 2, "code": "result", "name": "result", "scope": "event" },
"market_type": { "id": 1, "code": "moneyline", "name": "Moneyline", "category": "result" },
"status": "open",
"live": false,
"selections": [
{
"id": 35928,
"market_id": 63959,
"name": "Away",
"participant_id": 19,
"prices": [
{ "pricing_provider_id": 1, "price": 2.6747, "status": "active" }
]
}
],
"stake_limits": [
{ "pricing_provider_id": 1, "participant_id": null, "min_stake": 10, "max_stake": 1000 }
]
}
]
}
Prices that are not tradable are filtered out before they reach you, so status is always active here and the status === 'active' test above never fails. It is kept in the examples as a cheap guard, not because the value varies.
What changes instead is whether a selection appears at all. On this endpoint a selection with no tradable price is omitted from the response, so if the side you want is missing, it is not currently quoted β pick another market or event and check again later. (GET /selections/{selectionId} behaves differently: the selection is always returned and its prices array is empty instead.)
A price can still be withdrawn between your read and your bet. There is no way to pre-empt that, so handle the Selection price is not active rejection covered in Errors and re-read the market.
Capture three things from the selection:
selection.idβ goes into the bet body asselection_id- The matching
price.pricevalue β goes in asprice - The matching
price.pricing_provider_idβ goes in aspricing_provider_id
Check the stake limitsβ
The market's stake_limits entry tells you the stake range the bet must fall in. min_stake applies directly. max_stake caps your stake at prices of 2.00 or higher and your potential profit below 2.00, so the cap on your stake depends on the price:
If price >= 2.00: max_accepted_stake = max_stake
If price < 2.00: max_accepted_stake = max_stake / (price - 1)
In this example (max_stake: 1000, price 2.6747), you can stake up to 1000. At a price of 1.50 on the same market you could stake up to 2000, because the limit shifts to your potential profit below 2.00. See Stake Limits for the full explanation.
4. Know your account identifiersβ
Your client identity comes from your API key. The Bearer token on the request identifies your firm, so there's no client-ID field in the bet body. The body carries one account-related field that you control:
| Field | Type | Where it comes from |
|---|---|---|
external_account_ref_tokens | string | Your bettor's account reference: a single string, despite the plural name. You choose this, typically your internal user ID or account handle. The first time we see a new value we persist it; subsequent bets with the same value attach to that bettor record. |
If you only have one bettor (e.g. a single trading account), you can use a fixed string. For consumer-facing operators, pass your end-user's identifier so we can track bets per bettor.
5. Place the betβ
You now have everything you need:
{
"selection_id": 35928,
"pricing_provider_id": 1,
"stake": 10,
"price": 2.6747,
"external_account_ref_tokens": "bettor-001",
"bet_reference": "order-12345"
}
- curl
- JavaScript
- Python
curl -s -X POST \
-H "Authorization: Bearer $VERTEX_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"selection_id": 35928,
"pricing_provider_id": 1,
"stake": 10,
"price": 2.6747,
"external_account_ref_tokens": "bettor-001",
"bet_reference": "order-12345"
}' \
"https://trading.vertex-api.com/api/v2/bets"
const bet = await fetch(`${BASE}/bets`, {
method: 'POST',
headers: { ...headers, 'Content-Type': 'application/json' },
body: JSON.stringify({
selection_id: 35928,
pricing_provider_id: 1,
stake: 10,
price: 2.6747,
external_account_ref_tokens: 'bettor-001',
bet_reference: 'order-12345',
}),
}).then(r => r.json());
console.log('Placed:', bet.bet_id);
bet = requests.post(
f"{BASE}/bets",
headers={**headers, "Content-Type": "application/json"},
json={
"selection_id": 35928,
"pricing_provider_id": 1,
"stake": 10,
"price": 2.6747,
"external_account_ref_tokens": "bettor-001",
"bet_reference": "order-12345",
},
).json()
print("Placed:", bet["bet_id"])
On success the server responds with HTTP 201 and:
{
"bet_id": 299,
"message": "Bet placed"
}
You've placed a bet. Confirm it in your bet history:
curl -s -H "Authorization: Bearer $VERTEX_TOKEN" \
"https://trading.vertex-api.com/api/v2/bets?page_size=5"
To fetch the bet you just placed directly, use the returned bet_id against GET /bets/{id}, which returns the bet object on its own (no data wrapper).
Use bet_reference for safe retriesβ
bet_reference is your own idempotency key: pick a value that is unique per bet on your side (for example, your internal order ID). It makes retries safe:
- Re-sending
POST /betswith the samebet_referenceand the same stake and price returns the existing bet (201, with the messageBet already placed). No duplicate is created. - Re-sending the same
bet_referencewith a different stake or price is rejected with409 Conflict. The original bet is left untouched.
Retrieve a bet at any time with GET /bets/by-reference/{reference}, or by its numeric id with GET /bets/{id}:
curl -s -H "Authorization: Bearer $VERTEX_TOKEN" \
"https://trading.vertex-api.com/api/v2/bets/by-reference/order-12345"
If a POST /bets call returns a network error, timeout, or 5xx, the request may have succeeded server-side. If you sent a bet_reference, you can safely retry with the same reference, stake, and price: you will get the original bet back rather than a duplicate. Without a bet_reference, do not retry blindly; query GET /bets to confirm state first.
What you just learnedβ
- List endpoints are wrapped in
{ data: [...] }; single-resource lookups (likeGET /bets/{id}) return the object directly. - Expansion is opt-in:
expand=participantsfor team names on events,expand=selectionsfor prices on markets,expand=stake_limitsfor the stake range. - A bet is identified by selection Γ price against the Vertex pricing provider (
pricing_provider_id: 1). stakemust fall within the market's stake limits (min_stakeβ€ stake β€ the price-adjusted maximum) andpricemust be at least1.0; otherwise the bet returns400.
Next stepsβ
- REST API reference: every endpoint with request/response schemas and a built-in try-it-now.
- Concepts: pricing, data hierarchy, stake limits.
- WebSocket Feed: live price streaming.