CTBAPI Betting API Reference
Published by CTBAPI
Last updated
Overview
The betting API turns a full bet-placement run into a short sequence of HTTP calls. It mirrors exactly what the platform does by hand: pick a country and pool, drill into a meeting and race, choose one or more runners, then place win and/or place bets with a stake, a ticket price and an accepted limit range. The API confirms each bet and returns a receipt you can poll for its status.
Base URL
https://api.ctbapi.com/v1All endpoints are relative to this base URL. Access is provisioned per client, and your issued key is scoped to a single funded account.
Conventions
- All requests and responses are JSON. Send
Content-Type: application/json. - Money fields are decimal strings in the account's settled currency.
- Identifiers (
meeting_id,race_id,runner_id) are opaque strings; do not construct them yourself. - Timestamps are ISO 8601 in UTC.
Authentication
Every request carries your API key as a bearer token. The key maps to a single funded account on the backend; the underlying platform credentials never leave the server and are never exposed through the API.
curl https://api.ctbapi.com/v1/meetings \
-H "Authorization: Bearer $CTBAPI_KEY"Keep the key server-side. A missing or invalid key returns 401 unauthorized.
The placement flow
- List meetings for a country, discipline and pool.
- List races within the chosen meeting.
- List runners for the chosen race.
- Place bets: win and/or place, with stake, ticket price and limits.
- Read the receipt to confirm the bet was matched.
Endpoints at a glance
| Method | Path | Purpose |
|---|---|---|
GET | /v1/meetings | List meetings by country, discipline, pool |
GET | /v1/meetings/{id}/races | List races in a meeting |
GET | /v1/races/{id}/runners | List runners in a race |
POST | /v1/bets | Place win and/or place bets |
GET | /v1/bets/{id} | Read a bet's status and receipt |
List meetings
GET /v1/meetings. Filter by country, discipline (horse, harness or greyhound) and pool (for example S-TAB or NSW).
curl "https://api.ctbapi.com/v1/meetings?country=AU&discipline=horse&pool=NSW" \
-H "Authorization: Bearer $CTBAPI_KEY"{
"data": [
{
"meeting_id": "mtg_au_randwick_20260920",
"country": "AU",
"discipline": "horse",
"pool": "NSW",
"venue": "Randwick",
"date": "2026-09-20",
"races": 9
}
]
}List races
GET /v1/meetings/{meeting_id}/races. Returns the races in the meeting with their scheduled start times and status.
curl "https://api.ctbapi.com/v1/meetings/mtg_au_randwick_20260920/races" \
-H "Authorization: Bearer $CTBAPI_KEY"{
"data": [
{ "race_id": "rce_randwick_r5", "number": 5, "start": "2026-09-20T06:40:00Z", "status": "open" },
{ "race_id": "rce_randwick_r6", "number": 6, "start": "2026-09-20T07:15:00Z", "status": "open" }
]
}List runners
GET /v1/races/{race_id}/runners. Returns the runners for the race, each with its number and current best price.
curl "https://api.ctbapi.com/v1/races/rce_randwick_r5/runners" \
-H "Authorization: Bearer $CTBAPI_KEY"{
"data": [
{ "runner_id": "run_r5_07", "number": 7, "name": "Northern Light", "best_price": "4.50" },
{ "runner_id": "run_r5_03", "number": 3, "name": "Coastal Runner", "best_price": "6.00" }
]
}Place bets
POST /v1/bets is the core call. One request can carry both a win and a place bet on the same runner. The fields map directly to a manual placement:
runner_id: the selection, from the runners list.bets: one entry pertype(win,place) with itsstake.ticket_price: the price you are willing to take the bet at.limit: the accepted range for matching, asminandmax.
The example below places a 10 win / 10 place bet at a ticket price of 4.50, accepting a limit range of 30 to 120.
curl -X POST https://api.ctbapi.com/v1/bets \
-H "Authorization: Bearer $CTBAPI_KEY" \
-H "Content-Type: application/json" \
-d '{
"race_id": "rce_randwick_r5",
"runner_id": "run_r5_07",
"bets": [
{ "type": "win", "stake": "10.00" },
{ "type": "place", "stake": "10.00" }
],
"ticket_price": "4.50",
"limit": { "min": "30.00", "max": "120.00" }
}'{
"bet_id": "bet_8a1f6c2d",
"status": "submitted",
"race_id": "rce_randwick_r5",
"runner_id": "run_r5_07",
"legs": [
{ "type": "win", "stake": "10.00", "matched": "0.00" },
{ "type": "place", "stake": "10.00", "matched": "0.00" }
],
"ticket_price": "4.50",
"submitted_at": "2026-09-20T06:31:12Z"
}Read the receipt
GET /v1/bets/{bet_id}. Poll this to watch the bet move from submitted to matched (fully or partly) or voided, and to read the matched amounts.
curl "https://api.ctbapi.com/v1/bets/bet_8a1f6c2d" \
-H "Authorization: Bearer $CTBAPI_KEY"{
"bet_id": "bet_8a1f6c2d",
"status": "matched",
"legs": [
{ "type": "win", "stake": "10.00", "matched": "10.00", "price": "4.50" },
{ "type": "place", "stake": "10.00", "matched": "10.00", "price": "1.80" }
],
"matched_at": "2026-09-20T06:31:14Z"
}Bet status values
submitted: received and working, not yet matched.matched: matched in full at or better than the ticket price.partial: part of the stake matched within the limit; the rest is still working or lapsed.voided: not matched within the limit, or the race closed first.
Errors
Errors use standard HTTP status codes with a JSON body carrying a stable code and a human-readable message.
{
"error": {
"code": "insufficient_funds",
"message": "Account balance is below the requested stake."
}
}| Status | Code | Meaning |
|---|---|---|
401 | unauthorized | Missing or invalid API key |
404 | not_found | Unknown meeting, race, runner or bet |
409 | race_closed | The race is no longer accepting bets |
422 | insufficient_funds | Account balance below the stake |
429 | rate_limited | Too many requests; retry after the header delay |
Getting access
Live keys are issued once an account is opened and funded, so the API has a balance to bet from. To be provisioned, start with the request form, or reach us on Telegram. For how accounts are opened and funded, see about CTBAPI and the USDT deposits guide.
CTBAPI is not affiliated with or endorsed by any betting platform. Betting involves risk and you can lose money. See our responsible gambling page.