Skip to main content

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 -s -H "Authorization: Bearer $VERTEX_TOKEN" \
"https://trading.vertex-api.com/api/v2/sports"

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 -s -H "Authorization: Bearer $VERTEX_TOKEN" \
"https://trading.vertex-api.com/api/v2/events?sport_id=6&status=scheduled&expand=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.

Status is lowercase

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.

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"

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 }
]
}
]
}
Every price you are served is bettable

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 as selection_id
  • The matching price.price value β†’ goes in as price
  • The matching price.pricing_provider_id β†’ goes in as pricing_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:

FieldTypeWhere it comes from
external_account_ref_tokensstringYour 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 -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"

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 /bets with the same bet_reference and the same stake and price returns the existing bet (201, with the message Bet already placed). No duplicate is created.
  • Re-sending the same bet_reference with a different stake or price is rejected with 409 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"
Retrying a bet placement

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 (like GET /bets/{id}) return the object directly.
  • Expansion is opt-in: expand=participants for team names on events, expand=selections for prices on markets, expand=stake_limits for the stake range.
  • A bet is identified by selection Γ— price against the Vertex pricing provider (pricing_provider_id: 1).
  • stake must fall within the market's stake limits (min_stake ≀ stake ≀ the price-adjusted maximum) and price must be at least 1.0; otherwise the bet returns 400.

Next steps​