# Create app api key
Source: https://developers.betdex.com/api-reference/api-key-management/create-app-api-key
https://docs.api.monacoprotocol.xyz/api/v1/exchange-openapi-general.json post /api-keys
# Deactivate app api key
Source: https://developers.betdex.com/api-reference/api-key-management/deactivate-app-api-key
https://docs.api.monacoprotocol.xyz/api/v1/exchange-openapi-general.json post /api-keys/deactivate
# Rename app api key
Source: https://developers.betdex.com/api-reference/api-key-management/rename-app-api-key
https://docs.api.monacoprotocol.xyz/api/v1/exchange-openapi-general.json post /api-keys/rename
# Create a new app commission rate
Source: https://developers.betdex.com/api-reference/app-management/create-a-new-app-commission-rate
https://docs.api.monacoprotocol.xyz/api/v1/exchange-openapi-general.json post /apps/{id}/commission-rates
Create a new commission rate to be applied to profit on risk created via the given app. Commission rates can have a value between 0.0 and 1.0, where e.g., 0.01 would be 1% of the profit.
The most recently created rate will be used as the default rate. The default rate is used when no commission rate ID is passed during order creation.
NOTE maximum effective commission rate for an app will be
1.0 - protocol commission(fee) rate
# Fetch commission rates
Source: https://developers.betdex.com/api-reference/app-management/fetch-commission-rates
https://docs.api.monacoprotocol.xyz/api/v1/exchange-openapi-general.json get /apps/{id}/commission-rates
# Create, update, or refresh a heartbeat
Source: https://developers.betdex.com/api-reference/heartbeat-management/create-update-or-refresh-a-heartbeat
https://docs.api.monacoprotocol.xyz/api/v1/exchange-openapi-general.json post /heartbeats
Client heartbeat - to be used to ensure a client connection to the platform is active for the authenticated app. If a heartbeat for an application is configured, but no heatbeats are received for the provided timeoutMs then the platform will
- block further order creation until such time as the heartbeats resume, or until the heartbeat is deleted
- attempt to cancel *all* unmatched liquidity provided by that app
A request body is only required on this endpoint if a new heartbeat is being configured, or if the timeoutMs needs to be modified. Basic heartbeats after initial configuration require no request body.
# Delete a heartbeat
Source: https://developers.betdex.com/api-reference/heartbeat-management/delete-a-heartbeat
https://docs.api.monacoprotocol.xyz/api/v1/exchange-openapi-general.json delete /heartbeats
Deletes a configured heartbeat for an app. The existing heartbeat can be active or inactive, if it was inactive then the block on order creation will be removed.
# Fetch heartbeat
Source: https://developers.betdex.com/api-reference/heartbeat-management/fetch-heartbeat
https://docs.api.monacoprotocol.xyz/api/v1/exchange-openapi-general.json get /heartbeats
Returns information about the heartbeat configured for the authenticated app.
# Exchange status
Source: https://developers.betdex.com/api-reference/status/exchange-status
https://docs.api.monacoprotocol.xyz/api/v1/exchange-openapi-general.json get /status
# Create wallet api key
Source: https://developers.betdex.com/api-reference/wallet-api-key-management/create-wallet-api-key
https://docs.api.monacoprotocol.xyz/api/v1/exchange-openapi-general.json post /wallets/{id}/api-keys
# Deactivate wallet api key
Source: https://developers.betdex.com/api-reference/wallet-api-key-management/deactivate-wallet-api-key
https://docs.api.monacoprotocol.xyz/api/v1/exchange-openapi-general.json post /wallets/{id}/api-keys/{name}/deactivate
# Fetch api key for wallets
Source: https://developers.betdex.com/api-reference/wallet-api-key-management/fetch-api-key-for-wallets
https://docs.api.monacoprotocol.xyz/api/v1/exchange-openapi-general.json get /wallets/{id}/api-keys
# Create a deposit request
Source: https://developers.betdex.com/api-reference/wallets/create-a-deposit-request
https://docs.api.monacoprotocol.xyz/api/v1/exchange-openapi-general.json post /wallets/{id}/deposits/{currencyId}
Create and process a deposit request for given external address, wallet id and currency id
# Create a wallet
Source: https://developers.betdex.com/api-reference/wallets/create-a-wallet
https://docs.api.monacoprotocol.xyz/api/v1/exchange-openapi-general.json post /wallets
Creates a wallet that can be used to create orders and receive winnings
# Fetch a list of wallets
Source: https://developers.betdex.com/api-reference/wallets/fetch-a-list-of-wallets
https://docs.api.monacoprotocol.xyz/api/v1/exchange-openapi-general.json get /wallets
Fetch a page of wallets for the current application
# Fetch / create a deposit address
Source: https://developers.betdex.com/api-reference/wallets/fetch-create-a-deposit-address
https://docs.api.monacoprotocol.xyz/api/v1/exchange-openapi-general.json get /wallets/{id}/deposits/addresses
Fetch / create a deposit address for given wallet id and currency-id
# Request to withdraw Funds
Source: https://developers.betdex.com/api-reference/wallets/request-to-withdraw-funds
https://docs.api.monacoprotocol.xyz/api/v1/exchange-openapi-general.json post /wallets/{id}/withdrawals
Request to withdraw funds to an external address from the current wallet
# Building an App
Source: https://developers.betdex.com/building-an-app
Building your own exchange UI? Or creating a trading software product? Here's how you can use BetDEX's liquidity for your own product.
This guide walks you through a few key steps you might need to follow for connecting your app to BetDEX, getting market prices & placing bets.
# Before You Start
Make sure you've got your:
* App ID
* API Key
If you don't have these yet, contact us at [api@betdex.com](mailto:api@betdex.com) and we'll help you out.
Also remember the **base URLs** as follows:
```text Sandbox theme={null}
https://sandbox.api.btdx.io/
```
```text Production theme={null}
https://prod.api.btdx.io/
```
# Fee Structure
BetDEX charge app builders a **standard 1% fee** on net profits per market - **this is the same fee as we charge users who bet on our website.** This would be paid by your end users (if they make a profit on a market), **to us**.
App builders then have the ability to add **product commission** on top of that. This would be paid by your end users (if they make a profit on a market), **to you**. See below on how to set this.
For example, if you set product commission to 2%:
* User 123 bets \$1k @ 2.20, backing Knicks -2.5
* Knicks win 140-130, therefore user wins. Gross profit = \$1000 x 1.20 = \$1200
* Payouts are as follows
* BetDEX collects 1% platform fee of \$1200 x 1% = \$12
* You collect 2% product commission of \$1200 x 2% = \$24
* User receives back \$2166 (=\$1200 x 97% + original \$1k stake)
# 1. Authenticate
All requests require an `Authorization` header containing a session `accessToken`:
```http theme={null}
Authorization: Bearer
```
To obtain a token, use POST/sessions. See [https://developers.betdex.com/api-reference/session-management/create-a-session](https://developers.betdex.com/api-reference/session-management/create-a-session) for more info.
```text theme={null}
curl --request POST \
--url https://sandbox.api.btdx.io/sessions \
--header 'Content-Type: application/json' \
--data '
{
"appId": "ANAPPID",
"walletId": "OPTIONAL-ONLY-IF-APIKEY-IS-TYPE-WALLET",
"apiKey": "password1234"
}
'
```
# 2. Get Open Markets
Depending on which sports, leagues, market types you are looking for, there are plenty of ways to get the information you need. At a basic level:
* [GET/categories](https://developers.betdex.com/api-reference/event-categories/fetch-all-event-categories) - will return you the list of categories we offer. Currently this is just SPORTS, but we will expand in future
* [GET/subcategories](https://developers.betdex.com/api-reference/event-subcategories/fetch-subcategories-by-categories) - will return you the list of sports we offer
* [GET/event-groups](https://developers.betdex.com/api-reference/event-groups/fetch-all-event-groups) (optionally filtered by subcategoryIds) - will return you the list of tournaments/leagues we offer
* [GET/events](https://developers.betdex.com/api-reference/events/fetch-events) - will return you the list of events. There are plenty of filtering options here, most commonly used ones being:
* eventGroupIds - to filter by tournament / league
* Active = True - to get active events only
* Starting - to get events that are live, starting today (UTC), starting later than today (after 00:00 UTC tomorrow), or by a range
* [GET/markets](https://developers.betdex.com/api-reference/markets/fetch-markets) - to get the list of markets. Again plenty of filtering options here, most commonly used ones being:
* eventIds - to filter by specific event(s)
* Statuses = Open - to get markets that are open for trading
* marketTypeIds - to a specific market type like Handicaps, Moneyline, Totals, etc
# 3. Get Market Prices
To get the prices & liquidity available on a given market, you can use:
1. [GET/market-prices?marketIds=\[....\]](https://developers.betdex.com/api-reference/market-prices/fetch-market-prices) - to get prices for multiple markets
2. [GET/markets/\{id}/prices](https://developers.betdex.com/api-reference/markets/fetch-market-prices) - to get prices for a single market
3. Our Stream API (websocket) to subscribe to receive [MarketPriceUpdates](https://developers.betdex.com/stream-api#marketpriceupdatemessage)
An example response:
```text expandable theme={null}
{
"prices": [
{
"side": "Against",
"outcomeId": "1081970",
"price": 2.00,
"amount": 37.770000000000,
"matched": 0
},
{
"side": "Against",
"outcomeId": "1081971",
"price": 1.36,
"amount": 5.340000000000,
"matched": 0
},
{
"side": "Against",
"outcomeId": "1081971",
"price": 1.49,
"amount": 33.170000000000,
"matched": 0
},
{
"side": "For",
"outcomeId": "1081970",
"price": 3.45,
"amount": 24.840000000000,
"matched": 0
},
{
"side": "For",
"outcomeId": "1081970",
"price": 3.65,
"amount": 22.070000000000,
"matched": 0
},
{
"side": "For",
"outcomeId": "1081971",
"price": 2.88,
"amount": 34.310000000000,
"matched": 0
},
{
"side": "For",
"outcomeId": "1081972",
"price": 2.78,
"amount": 7.150000000000,
"matched": 0
}
],
"_meta": {
"_primary_document": "prices",
"_count": 7,
"_page": null
}
}
```
It's important to note here - the **SIDE** denotes what is **already on the market.**
So **side = against** with **price = 2.00** and **amount = 37.77** means there is \$37.77 **available to back** at price of 2.0 (odds)
**side = for** with **price = 3.45** and **amount = 24.84** means there is \$24.84 **available to lay** at price of 3.45 (odds)
Both of these can be visualised below.
# 4. Creating Wallets
When a new user signs up on your app / platform, you would create them a wallet using [POST/wallets](https://developers.betdex.com/api-reference/wallets/create-a-wallet).
The reference used here can be anything, but should be unique & tie to some sort of user id on your platform side (for example just incrementing numbers 1,2,3,..etc; or some random string like abcde-12345-fghij-678910)
Description can be anything, such as a username your users have set.
So if you had 100 users sign up, then you would have 100 unique wallets.
# 5. Placing Bets
Using the accessToken from the session generated (see Authentication above), then when a user comes to placing a trade via your platform, your platform should call [POST/orders](https://developers.betdex.com/api-reference/orders/create-order) with
* accessToken in the request header as authorization
* the user's unique walletId in the body
* all other required bet details in the body
# 6. Charging Commission
As outlined above, 'commission' is something App builders have the ability to add on top of platform fees (which we collect).
This would be paid by your end users (if they make a profit on a market), **to you**.
## 6.1 Setting a default commission rate
Simply call [POST/apps/\{id}/commission-rates](https://developers.betdex.com/api-reference/app-management/create-a-new-app-commission-rate) with the desired % in the request body. Commission rates can have a value between 0.0 and 1.0, where e.g., 0.01 would be 1% of the profit.
The default rate can be changed at any time, and the latest one set will be the latest.
You can always check the latest using [GET/apps/\{id}/commission-rates](https://developers.betdex.com/api-reference/app-management/fetch-commission-rates).
## 6.2 Overriding the default rate per bet
If you wish to charge a different commission rate without changing the default, simply include `commissionRateId` in the order request.
```text expandable highlight={16} focus={16} theme={null}
curl --request POST \
--url https://sandbox.api.btdx.io/orders \
--header 'Content-Type: application/json' \
--header 'authorization: ' \
--data '
{
"walletId": "string",
"marketId": "string",
"side": "For",
"outcomeId": "string",
"price": 0,
"stake": 0,
"keepWhenInPlay": true,
"matchBehavior": "RetainUnmatched",
"reference": "string",
"commissionRateId": "string"
}'
```
By omitting `commissionRateId` in the order request, the app's default
commission rate will be used (or 0% if not set).
This might be useful if you wish to drive promotions and charge less commission than the default.
# FAQs
Source: https://developers.betdex.com/faqs
## General
#### Is the API open to the public?
Yes! BetDEX launched their API to the public at the end of May 2026.
#### Is there a sandbox?
Yes, you can obtain an API key to our sandbox environment by creating an account on [https://sandbox.betdex.com](https://sandbox.betdex.com).
API requests can then be made using:
* REST API base URL: [https://sandbox.api.btdx.io/](https://sandbox.api.btdx.io/)
* Websocket host: wss\://[sandbox.stream.btdx.io](http://sandbox.stream.btdx.io)
#### Are there any fees to use the API?
Other than the usual 1% commission that we charge on net profits per market, there are no additional charges to trading via our API.
## Order Creation
#### How quickly are orders confirmed?
Latency on BetDEX Exchange is currently \~0.08s. Naturally this will vary during peak periods, however we are committed to continuously improving our infrastructure and are pushing to get this down to \~0.01s consistently.
Please note, this does not include any 'in-play bet delay'. If you are placing an order on an in-play market with a bet delay of 5 seconds for example, then you should expect confirmation within 5.08s.
#### What are the limits on order placement?
Using POST/orders, you can only place 1 order at a time. Using POST/orders/batch, you can place up to 50 orders per request.
Overall, standard usage rate limits will apply.
#### Can I see orders from other apps, for example to create an order book UI?
Yes, GET/orders and GET/market-prices will return you all orders / prices on BetDEX.
Likewise if you subscribe to OrderUpdate and/or MarketPriceUpdate in the Stream API.
However to maintain security, the specific app & wallet names and any identifiers of other customers will be omitted.
#### Can we see available liquidity at each price level?
Yes, you will get the outcome, side, price, available (backer) stake for each price point where liquidity is available on the market.
#### Are partial fills supported?
Yes, if \$1k exists at 2.0, and you place an order for \$1.5k, we will match that \$1k. The remaining \$500 depends on what you specify as the "matchBehaviour" in the order request:
* **RetainUnmatched** = the remaining \$500 would be kept open as an offer for someone else to later match against (or for you to later cancel yourselves)
* **CancelUnmatched** = the remaining \$500 would be cancelled and returned to your wallet
#### Is there maker/taker fee differentiation?
Currently, no - we charge a flat 1% commission on net profits per market, regardless of whether the order is a maker or a taker. This may change in the future; however, we'll of course communicate any changes well in advance.
## Order Cancellation
#### Using batch cancellation (/orders/cancel-v2), what happens if you attempt to cancel 5 order ids, of which 1 is already cancelled?
If the order has been cancelled already, it won't be amended further, and won't be returned by the request. The process is to take the request parameters, find applicable (open) orders based on the parameters, cancel them and return a list of the cancelled orders.
So if the request parameters don't match an order (ie. it's been cancelled already, or fully matched) then it will not be returned in the response.
#### What are the possible `cancellationReason`'s?
Failed pending orders:
* **A timeout in processing the order**: “TIMEOUT”
* **A data issue processing the order**: system determined, but will be whatever we currently have for batch order creation if any of them fail due to a data issue (basically a database failure causing data inconsistency - shouldn’t really happen)
Fully Cancelled orders:
* **Cancelled on creation due to no match**: “CancelUnmatched applied“
* **Cancelled by ID or by batch**: “Requested“
* **Cancelled by stale heartbeat**: “Stale heartbeat“
* **Cancelled at market transition to in-play**: “Market transition (market went in-play)“
* **Cancelled when market locks**: “Market transition (status locked)“
* **Cancelled at settlement because fully unmatched**: “Unmatched at settlement“
* **Cancelled due to excess liquidity** (unused functionality): “Removing excess liquidity“
#### When cancelling, how do I know how much was cancelled?
This is provided in the orders object as **stakeVoided**.
You can get this by making any /orders request, or if you are subscribed to OrderUpdate on the Stream API, you will receive an OrderUpdateMessage which includes the **stakeVoided**.
## Settlement
#### Are settlement/resolution statuses available programmatically?
Yes when markets are settled, if you are subscribed to:
* MarketUpdate and/or MarketStatusUpdate --> you will receive a websocket message informing you that the status has changed
* OrderUpdate --> order status would update to Won / Lost / Voided.
* EventUpdate --> event active status would update to "inactive".
## Sessions
#### After creating a session, I call other APIs but they return 400 error. What’s wrong?
Ensure that you have included accessToken pasted under authorization in the request header.
#### How long do sessions last?
The initial accessToken provided expires after 30 mins. The refresh token provided, which can be used in POST/sessions/refresh to extend the session for a further 30 mins, will expire after 1 week.
## Common Errors
#### I’m receiving a 403 error ‘the request could not be satisfied’. What should I do?
Cloudfront may be rejecting your requests, as one of the rules is that you specify a user agent. Adding CURLOPT\_USERAGENT => 'BetMonitor/1.0 (client)', into the curl options should fix the issue.
#### Why is "*\_page*" returning as NULL?
We only offer pagination on certain endpoints.
`_page` will be null on endpoints that don't offer pagination.
#### Why can't I filter /markets or /orders by a single status?
You can!
The filter status is still `statuses` even if it just for one status.
So **GET/markets?statuses=Open** or **GET/orders?statuses=Unmatched** will work all the same.
## Stream API / Websockets
#### What are the connection timeouts?
There are some limits imposed by the provider we're using: Idle Connection Timeout: 10mins Max Connection Duration: 2 hours
These are hard limits with the implementation we have at the moment.
For the idle connection (10 mins), you can send PING messages (or any message) to keep the connection which will keep this alive. For the 2 hour timeout, you'll just need to reconnect
We are planning to move to a different websocket provider which should alleviate these problems in the long term.
#### I have authenticated & subscribed, but I’m not receiving updates. What can I do?
Check to ensure your connection has not disconnected due to one of the timeout reasons above.
## Events & Markets
#### What does the API show if an event is cancelled or postponed?
If we are voiding the markets, then you will see: Market Status = Voided, Event Active = False.
You can get this either from the REST API (using GET/events & GET/markets), or from the Stream API via the EventUpdate & MarketStatusUpdate subscriptions.
If we are letting the markets run (& just updating the event start time), you will see the updated event start time (again via GET/events on the the REST API, or EventUpdate subscription via Stream API).
You can refer to our [rules](https://learn.betdex.com/betdex-exchange/betdex-offering/exchange-rules-and-regulations) for clarification on when we void.
#### Will I be able to create my own events?
Currently we are not able to support setting up of new market operators. It's on our radar for future development.
For now however, if you wish to request new leagues or markets, you can reach out to us at [requestabet@betdex.com](mailto:requestabet@betdex.com). If we can accommodate it, then we will get your request added ASAP!
#### Does the API provide full order book depth or only best bid/ask?
Yes, all market prices/liquidity will be available.
#### Are matched trades/recent fills available through the API?
Yes, all orders & trades are available through the API with various filtering options. You will of course not receive the app/wallet id of who specifically created the trade (unless it's your own), but can get the market, side, price, stake details.
#### Is there websocket support for real-time updates?
Yes, see our developer docs [here](/stream-api).
#### What is the expected update latency for odds / orderbook changes?
Our REST API & Websocket updates are currently averaging \~between 30-50ms for market price updates, with a max \~100ms during peak periods.
#### Are event, market & participant IDs & names consistent?
Once created, market & event IDs will not change unless a game is cancelled and then rearranged as another event.
Team/participants are generally re-used across events - ie. so for NBA playoffs, for Knicks v Cavaliers series, all games use these participants:
* CLECAV - Cleveland Cavaliers - 10569
* NEYOKN - New York Knicks - 10571
Note, the ids may differ between Sandbox & Prod, but are consistent in the same env.
#### What in-play delays are applied?
* Tennis - 3s
* Basketball - 0s
* MLB - 0s
* All else - 5s
#### Is there a price ladder?
Yes, you can get this from `GET /market-prices/ladder`
The summary of the ladder is:
* Minimum odds are 1.01
* Maximum depth is 2 decimal points (ie. cannot accept 1.011)
* Odds between 1.01 and 2 are in increments of 0.01
* Odds between 2 and 3 are in increments of 0.02
* Odds between 3 and 4 are in increments of 0.05
* Odds between 4 and 6 are in increments of 0.1
* Odds between 6 and 10 are in increments of 0.2
* Odds between 10 and 20 are in increments of 0.5
* Odds between 20 and 30 are in increments of 1
* Odds between 30 and 50 are in increments of 2
* Odds between 50 and 100 are in increments of 5
* Odds between 100 and 1000 are in increments of 10
* Maximum odds are 1000
### What happens when a market is suspended?
If a market is suspended, we will reject **new** orders to prevent them being created.
Existing liquidity will remain, but as above cannot be matched with (until the market is unsuspended), given new orders can't be created.
Unmatched liquidity / orders on a suspended market can be cancelled.
For Stream API users, a MarketStatusUpdate & MarketUpdate message will be sent whenever a market is suspended or unsuspended.
## Rate Limits
#### What are the API rate limits?
BetDEX strives to provide a strong experience for all API & website customers alike - we simply want to allow you to trade with us however you wish.
In order to maintain a robust Exchange product for all, **general rate limits of 5000 requests per minute are enforced**.
This currently is across both GET & POST calls, but will be split out in the near-future.
#### Are websocket subscriptions limited?
Yes. In order to maintain a robust Exchange product for all, the following general rate limits are enforced:
* 10 **active** connections at any one time
* 500 **active** individual subscriptions at any one time - these can be spread across the number of active connections as needed.
**Recommendation:** for Stream API connections & subscriptions, if you are currently subscribing to markets by single market ids, then look at utilising the wildcard `"*"` instead.
For example, if you are subscribing just for a few markets, then
```text wrap theme={null}
"subscriptionIds": ["400001, 400002, 400003, 400004, 400005, 400006, 400007, 400008, 400009, 400010"]
```
makes sense.
However if you are subscribing to 501+ markets, you'll need to use
```text theme={null}
"subscriptionIds": ["*"]
```
## Any other questions?
If you have another question which we've not answered here, please get in touch either on our Discord channel, or via Live Chat on our website.
# How to get an API key?
Source: https://developers.betdex.com/how-to-get
In order to start building Apps as an App Builder on the BetDEX API you need an App and API Key created.
Please contact us on [api@betdex.com](mailto:api@betdex.com) and we'll get an App set up.
# How to get my API key?
Source: https://developers.betdex.com/how-to-get-my-api-key
In order to use our APIs, you require:
* An ‘App ID’
* An ‘API Key’
* A 'Wallet ID'
The App ID & API Key are needed to create a 'session' to get an 'accessToken'. The Wallet ID is required to make various requests such as placing/cancelling/viewing orders.
### Step-by-step process
1. From anywhere on BetDEX, click on your balance
2. Click on "API"
3. Click "Generate API Key"
4. Read the T\&Cs. If you agree, then select the checkbox and click "Continue"
5. After accepting the T\&Cs, click "Generate API Key"
6. That's it! You will now be given your unique API key. **Please ensure you securely save & store the API key, as it cannot be retrieved again**.
### What if I lose my key, or if it's compromised?
If you lose the key, or believe it compromised, you can deactivate it and generate a new one from scratch.
1. Go to [https://betdex.com/account?tab=api-key](https://betdex.com/account?tab=api-key)
2. Click on 'Deactivate API key' & 'Confirm'
3. Then click 'Generate new key' to get the new key
### Why is it asking me to verify account?
You must have completed "Basic Verification" to sign up to trade via our API. You can do this by going to [https://betdex.com/account?tab=personal](https://betdex.com/account?tab=personal) and submitting your personal details.
Once that is completed, head back to [https://betdex.com/account?tab=api-key](https://betdex.com/account?tab=api-key) (or simply click the "API" tab again) and you will be all set to generate the API key following the steps above.
### Any other issues?
As always, if you are facing any issues with generating your API key, or have any questions / feedback for us, please don't hesitate to reach out via Live Chat and one of our team will assist you as quickly as possible.
# Welcome
Source: https://developers.betdex.com/index
BetDEX Exchange API documentation overview
## Introduction
BetDEX provides both a RESTful API and a Stream API (websocket) to ensure seamless, easy integration between the BetDEX Exchange and client applications. The RESTful API is an HTTP-based API which lets you read/GET event, market & price data, and write/POST orders. The Websocket API (wss) provides you instant notifications when there are changes to Markets, Prices, Events & your Orders.
This documentation covers the BetDEX Exchange API for real-time market data, trade execution, and account management. Use the REST APIs to manage events, markets, sessions, wallets, and orders, or connect to the Stream API for real-time updates.
Endpoints for managing events, markets, sessions, wallets, and orders.
Subscribe to real-time event, market, order, and wallet updates over WebSocket.
## Environments
REST: `https://sandbox.api.btdx.io/`
Stream: `wss://sandbox.stream.btdx.io`
REST: `https://prod.api.btdx.io/`
Stream: `wss://prod.stream.btdx.io`
## Getting Started
In order to use our APIs, you require:
* Your ‘App ID’
* Your 'Wallet ID'
* An ‘API Key’
To get these, follow the steps outlined on [https://learn.betdex.com/betdex-exchange/using-the-betdex-exchange/betdex-api/how-to-get-my-api-key](https://learn.betdex.com/betdex-exchange/using-the-betdex-exchange/betdex-api/how-to-get-my-api-key)
The server / base URL for any calls on production is [**https://prod.api.btdx.io/**](https://prod.api.btdx.io/)
## Authentication
Using your ‘App ID’, 'Wallet ID' & ‘API Key’, initiate a ‘session’ by calling: `POST https://prod.api.btdx.io/sessions` using the App id & API key provided in the request body:
```text theme={null}
{
"appId": "01abcde12345",
"walletId": "01abcde12345-User-123",
"apiKey": "123abc456def789ghi"
}
```
The response provides an `accessToken`:
```text theme={null}
{
"sessions": [
{
"accessToken": "....",
"refreshToken": "...",
"accessExpiresAt": "2026-03-24T14:45:01.000Z",
"refreshExpiresAt": "2026-03-31T14:15:01.000Z"
}
]
}
```
Include the session `accessToken` as the authorization value in the request header for all subsequent API calls.
| Token | Lifetime | Notes |
| :- | :- | :- |
| `accessToken` | 30 minutes | Used to authenticate all API requests |
| `refreshToken` | 1 week | Use with `POST /sessions/refresh` to extend the session for another 30 minutes |
## Placing Your First Bet
Using your session `accessToken` as the authorization value in the request header, place an ‘order’ by calling: `POST https://prod.api.btdx.io/orders`
```text theme={null}
{
"walletId": "string",
"marketId": "string",
"side": "For",
"outcomeId": "string",
"price": 0,
"stake": 0,
"keepWhenInPlay": true,
"matchBehavior": "RetainUnmatched",
"reference": "string"
}
```
Where:
* **walletId** is what you were given when you generated your API key, and can always be found at [https://betdex.com/account?tab=api-key](https://betdex.com/account?tab=api-key)
* **marketId** is the unique market id number that you wish to bet on. You can find these using GET/markets
* **side** is **For** (Back) or **Against** (Lay)
* **outcomeId** is the outcome (team, participant, selection) you wish to back or lay, again obtained in GET/markets
* **price** is your desired odds/price
Note, we have a fixed odds ladder - you can get this from GET/market-prices/ladder.
Tip - we have the same ladder as Betfair.
* **stake** is your desired **backer stake**
* Note, this is always from the backer's perspective
* So if you are laying, this is your desired profit, not your risk
* Meaning if you wish to Lay/Against Team A risking \$1000 @ 1.5, your stake input (profit) must be \$2000
* **keepWhenInPlay** is **true** if you wish for any unmatched stake to remain open when the event starts & market moves to in play; or **false** if you wish it to be cancelled
* **matchBehavior** is **RetainUnmatched** if you wish for any unmatched stake (after the order is processed) to remain unmatched as open liquidity at the price given; or **CancelUnmatched** if you wish us to cancel anything that does not get immediately matched
* **reference** is an optional string field you can provide if you wish
## Error Messages
The BetDEX API uses standard HTTP status codes to indicate successful or error responses.
| Status Code | Description |
| :- | :- |
| `200` | OK — Request succeeded |
| `400` | Bad Request — The request was invalid or malformed |
| `401` | Unauthorized — Authentication failed or token expired |
| `404` | Not Found — The requested resource does not exist |
| `500` | Internal Server Error — Something went wrong on the server |
## Rate Limits
BetDEX strives to provide a strong experience for all API & website customers alike - we simply want to allow you to trade with us however you wish.
In order to maintain a robust Exchange product for all, the following general rate limits are enforced:
### REST API
* 5000 requests per minute
This currently is across both GET & POST calls, but will be split out in the near-future.
### Stream API
* 10 **active** connections at any one time.
* 500 **active** individual subscriptions at any one time - these can be spread across the number of active connections as needed.
* 500 subscriptions per minute.
**Recommendation:** for Stream API connections & subscriptions, if you are currently subscribing to markets by single market ids, then look at utilising the wildcard `"*"` instead.
For example, if you are subscribing just for a few markets, then
```text wrap theme={null}
"subscriptionIds": ["400001, 400002, 400003, 400004, 400005, 400006, 400007, 400008, 400009, 400010"]
```
makes sense.
However if you are subscribing to 501+ markets, you'll need to use the wildcard, as you can only subscribe to 500 markets at a time.
```text theme={null}
"subscriptionIds": ["*"]
```
## Terms of Use
You can view the latest Terms of Use for using the BetDEX API here - [https://terms.btdx.io/betdexApi/1.0.0.pdf](https://terms.btdx.io/betdexApi/1.0.0.pdf)
*Terms last updated 25th May 2026*
© 2026 BetDEX Labs, Inc. All rights reserved. BetDEX™ word marks and logos are trademarks owned by BetDEX Labs, Inc.
BetDEX API is NOT a licensed gambling product/service offering.
# REST API Overview (premium)
Source: https://developers.betdex.com/rest-api/apps/overview
Introduction to the BetDEX REST API for managing events, markets, sessions, wallets, and orders.
The BetDEX REST API lets you interact with the BetDEX Exchange programmatically. Use it to manage events, markets, sessions, wallets, and orders.
## Base URLs
```text Sandbox theme={null}
https://sandbox.api.btdx.io/
```
```text Production theme={null}
https://prod.api.btdx.io/
```
## Authentication
All requests require an `Authorization` header containing a session `accessToken`:
```http theme={null}
Authorization: Bearer
```
To obtain a token, create a session using your **App ID** and **API key** via the Session Management endpoints.
## What you can do
Authenticate your application and obtain access tokens.
Browse categories, event groups, events, and markets.
Place, cancel, and inspect orders against open markets.
Manage balances and review wallet activity.
## Real-time updates
For real-time event, market, order, and wallet updates, use the [Stream API](/stream-api) over WebSocket.
# REST API Overview (basic)
Source: https://developers.betdex.com/rest-api/overview
Introduction to the BetDEX REST API for managing events, markets, sessions, wallets, and orders.
The BetDEX REST API lets you interact with the BetDEX Exchange programmatically. Use it to manage events, markets, sessions, wallets, and orders.
## Base URLs
```text Sandbox theme={null}
https://sandbox.api.btdx.io/
```
```text Production theme={null}
https://prod.api.btdx.io/
```
## Authentication
All requests require an `Authorization` header containing a session `accessToken`:
```http theme={null}
Authorization: Bearer
```
To obtain a token, create a session using your **App ID** and **API key** via the Session Management endpoints.
## What you can do
Authenticate your application and obtain access tokens.
Browse categories, event groups, events, and markets.
Place, cancel, and inspect orders against open markets.
Manage balances and review wallet activity.
## Real-time updates
For real-time event, market, order, and wallet updates, use the [Stream API](/stream-api) over WebSocket.
# Stream API
Source: https://developers.betdex.com/stream-api
Real-time WebSocket subscriptions for events, markets, orders, and wallets.
The BetDEX Stream API provides methods for clients to subscribe to exchange events. Clients connect to the Stream API, authenticate their connection, and then subscribe to the updates they wish to receive.
## Connection details
**WebSocket host:**
```text Sandbox theme={null}
wss://sandbox.stream.btdx.io
```
```text Production theme={null}
wss://prod.stream.btdx.io
```
## Message types
There are two types of messages that can be sent to the Stream API:
* [Authentication](#authentication)
* [Subscription](#subscription)
## Authentication
To authenticate, clients must send an `AuthenticationRequest` after connecting. The `accessToken` is available from the REST API when creating a session.
### AuthenticationRequest
```json theme={null}
{
"action": "authenticate",
"accessToken": "string"
}
```
## Subscription
To subscribe to updates, clients must send a `SubscriptionRequest` describing the type of update and the IDs they are interested in.
* The `subscriptionType` field defines the type for which subscription is made. Allowed values are:
* `EventUpdate`
* `MarketUpdate`
* `MarketPriceUpdate`- all price changes in a market, both direct and derived prices (cross matching liquidity)
* `MarketDirectPriceUpdate` - direct only price changes in a market (excluding derived prices/cross matching liquidity triggered updates)
* `MarketStatusUpdate`
* `OrderUpdate`
* `WalletUpdate`
* The `subscriptionIds` field determines the entities to subscribe to. Allowed values are:
* For `EventUpdate`, it can be set to
* `*` - all events
* `event-id` - specific events
* For`MarketUpdate`, `MarketPriceUpdate`, `MarketDirectPriceUpdate`and `MarketStatusUpdate` it can be set to:
* `*` - all markets
* `market-id` - specific markets
Please note for Market subscriptions:
**If you are subscribing to get a large set of markets, then you should subscribe using the wildcard** `* `**as otherwise you will hit subscription limits (max 500).**
**COMING SOON:** We will also be soon introducing ability to subscribe at a sport and league level too (ie. to subscribe to all Football or all NFL price updates in one subscription, rather than having to do the wildcard for all markets and then filtering down to what you need on your side)
* For `OrderUpdate` it can be set to:
* `*`
* `order-id` — subscribe to updates for an order identified by its id.
* `app-id:*` — subscribe to all orders owned by `app-id` (only the connection's `app-id` is allowed).
* `app-id:wallet-id` — subscribe to all orders owned by `app-id` and `wallet-id` (only the connection's `app-id` is allowed, and the wallet must be owned by that `app-id`).
* For `WalletUpdate` it can be set to
* `*` - all wallets owned by your app
* `wallet-id` - specific wallet owned by your app
Setting `subscriptionId` to `*` means the client will be subscribed to all updates of the given type.
### SubscriptionRequest
```json theme={null}
{
"action": "subscribe",
"subscriptionType": "EventUpdate | MarketUpdate | MarketPriceUpdate | MarketDirectPriceUpdate | MarketStatusUpdate | OrderUpdate | WalletUpdate",
"subscriptionIds": ["string"]
}
```
### Subscription Limits
To keep the system performance strong for every client, we're introducing the following limits:
* 10 **active** connections at any one time
* 500 **active** individual subscriptions at any one time - these can be spread across the number of active connections as needed.
* 500 subscriptions per minute.
**Recommendation:** for Stream API connections & subscriptions, if you are currently subscribing to markets by single market ids, then look at utilising the wildcard `"*"` instead.
For example, if you are subscribing just for a few markets, then
```text wrap theme={null}
"subscriptionIds": ["400001, 400002, 400003, 400004, 400005, 400006, 400007, 400008, 400009, 400010"]
```
makes sense.
However if you are subscribing to 501+ markets, you'll need to use
```text theme={null}
"subscriptionIds": ["*"]
```
### UnsubscriptionRequest
```json theme={null}
{
"action": "unsubscribe",
"subscriptionType": "EventUpdate | MarketUpdate | MarketPriceUpdate | MarketStatusUpdate | OrderUpdate | WalletUpdate",
"subscriptionIds": ["string"]
}
```
## Update messages
After authentication and subscription, clients will receive update messages when markets, orders, or wallets are updated on the platform. The supported update message formats are listed below.
### EventUpdateMessage
```json theme={null}
{
"eventId": "string",
"eventGroupId": "string",
"eventGroupName": "string",
"subcategoryId": "string",
"subcategoryName": "string",
"categoryId": "string",
"categoryName": "string",
"appId": "string",
"name": "string",
"code": "string",
"active": "boolean",
"expectedStartTime": "string (date-time)",
"createdAt": "string (date-time)",
"modifiedAt": "string (date-time)"
}
```
Example:
```text expandable wrap theme={null}
{"type":"EventUpdate","eventId":"28282","eventGroupId":"NHL","eventGroupName":"NHL","subcategoryId":"ICEHKY","subcategoryName":"Ice Hockey","categoryId":"SPORTS","categoryName":"Sports","appId":"APPID","name":"Washington Capitals v Pittsburgh Penguins","code":"NHL-washington-capitals-v-pittsburgh-penguins-1791415800","active":true,"expectedStartTime":"2026-10-07T23:30:00.000Z","createdAt":"2026-10-05T23:33:42.126Z","modifiedAt":"2026-10-07T12:22:19.351Z"}
```
### MarketUpdateMessage
```json theme={null}
{
"marketId": "string",
"appId": "string",
"eventId": "string",
"eventGroupId": "string",
"categoryId": "string",
"subCategoryId": "string",
"name": "string",
"marketTypeId": "string",
"marketValue": "string",
"marketDiscriminator": "string | null",
"escrowWalletId": "string",
"currencyId": "string",
"marketOutcomes": [
{
"outcomeId": "string",
"title": "string",
"ordering": 0,
"winner": "string | null",
"participantId": "string | null",
"createdAt": "string (date-time)",
"modifiedAt": "string (date-time)"
}
],
"crossMatchingEnabled": "boolean",
"lockAt": "string (date-time)",
"settledAt": "string (date-time) | null",
"marketLockAction": "None | CancelUnmatchedLiquidity",
"eventStartAction": "None | CancelUnmatchedLiquidity",
"status": "Initializing | Open | Locked | Settling | Settled | Voiding | Voided | Closed",
"published": "boolean",
"suspended": "boolean",
"inPlayStatus": "NotApplicable | PrePlay | InPlay",
"inPlayDelay": 0,
"createdAt": "string (date-time)",
"modifiedAt": "string (date-time)"
}
```
### MarketStatusUpdateMessage
```json theme={null}
{
"marketId": "string",
"eventId": "string",
"eventGroupId": "string",
"categoryId": "string",
"subCategoryId": "string",
"status": "Initializing | Open | Locked | Settling | Settled | Voiding | Voided | Closed",
"published": "boolean",
"suspended": "boolean",
"inPlayStatus": "NotApplicable | PrePlay | InPlay"
}
```
Example:
```text expandable wrap theme={null}
{"type":"MarketStatusUpdate","marketId":"898792","eventId":"28282","eventGroupId":"NHL","categoryId":"SPORTS","subCategoryId":"ICEHKY","status":"Open","published":true,"suspended":false,"inPlayStatus":"PrePlay"}
```
### MarketPriceUpdateMessage & MarketDirectPriceUpdateMessage
```json theme={null}
{
"updateType": "Snapshot | Incremental",
"marketId": "string",
"eventId": "string",
"eventGroupId": "string",
"categoryId": "string",
"subCategoryId": "string",
"prices": [
{
"side": "For | Against",
"outcomeId": "string",
"price": "number",
"liquidity": "number",
"change": "number",
"validAt": "string (date-time)"
},
{
"side": "For | Against",
"outcomeId": "string",
"price": "number",
"liquidity": "number",
"change": "number",
"validAt": "string (date-time)"
}
]
}
```
Snapshot Example:
```text expandable wrap theme={null}
{"type":"MarketPriceUpdate","updateType":"Snapshot","marketId":"898792","eventId":"28282","eventGroupId":"NHL","categoryId":"SPORTS","subCategoryId":"ICEHKY","prices":[{"side":"Against","outcomeId":"2067060","price":1.97,"liquidity":40.61,"change":40.61,"validAt":"2026-10-07T12:26:25.468Z"},{"side":"Against","outcomeId":"2067060","price":2,"liquidity":50,"change":50,"validAt":"2026-10-07T12:26:25.446Z"},{"side":"Against","outcomeId":"2067060","price":2.06,"liquidity":38.76560013,"change":38.76560013,"validAt":"2026-10-07T12:26:25.543Z"},{"side":"Against","outcomeId":"2067060","price":2.08,"liquidity":47.6476002,"change":47.6476002,"validAt":"2026-10-07T12:26:25.520Z"},{"side":"Against","outcomeId":"2067061","price":1.8,"liquidity":44.44,"change":44.44,"validAt":"2026-10-07T12:26:25.576Z"},{"side":"Against","outcomeId":"2067061","price":1.81,"liquidity":43.96880002,"change":43.96880002,"validAt":"2026-10-07T12:26:25.442Z"},{"side":"Against","outcomeId":"2067061","price":1.83,"liquidity":54.64,"change":54.64,"validAt":"2026-10-07T12:26:25.546Z"},{"side":"Against","outcomeId":"2067061","price":1.86,"liquidity":53.70800015,"change":53.70800015,"validAt":"2026-10-07T12:26:25.425Z"},{"side":"For","outcomeId":"2067060","price":2.16,"liquidity":46.3,"change":46.3,"validAt":"2026-10-07T12:26:25.397Z"},{"side":"For","outcomeId":"2067060","price":2.22,"liquidity":81.39119994,"change":81.39119994,"validAt":"2026-10-07T12:26:25.422Z"},{"side":"For","outcomeId":"2067060","price":2.26,"liquidity":35.552,"change":35.552,"validAt":"2026-10-07T12:26:25.601Z"},{"side":"For","outcomeId":"2067061","price":1.91,"liquidity":52.36,"change":52.36,"validAt":"2026-10-07T12:26:25.497Z"},{"side":"For","outcomeId":"2067061","price":1.94,"liquidity":41.24,"change":41.24,"validAt":"2026-10-07T12:26:25.522Z"},{"side":"For","outcomeId":"2067061","price":2,"liquidity":50,"change":50,"validAt":"2026-10-07T12:26:25.465Z"},{"side":"For","outcomeId":"2067061","price":2.04,"liquidity":39.3916999,"change":39.3916999,"validAt":"2026-10-07T12:26:25.491Z"}]}
```
Incremental Example:
```text expandable wrap theme={null}
{"type": "MarketPriceUpdate","updateType": "Incremental","marketId": "867386","eventId": "412093","eventGroupId": "EPL","categoryId": "FOOTBALL","subCategoryId": "SOCCER",
"prices": [
{"side":"For", "outcomeId":"2001","price":2.5,"liquidity":6, "change":6, "validAt":"2026-10-01T10:15:00.061Z"},
{"side":"For", "outcomeId":"2001","price":3.0,"liquidity":5, "change":5, "validAt":"2026-10-01T10:15:00.061Z"},
{"side":"Against", "outcomeId":"2002","price":1.6,"liquidity":12,"change":12,"validAt":"2026-10-01T10:15:00.062Z"}
]
}
```
### OrderUpdateMessage
```json theme={null}
{
"orderId": "string",
"eventId": "string",
"eventGroupId": "string",
"categoryId": "string",
"subCategoryId": "string",
"marketId": "string",
"walletId": "string",
"status": "Cancelled | Matched | PartiallyMatched | Unmatched | Won | Lost | Voided",
"side": "For | Against",
"outcomeIndexId": "string",
"price": 0.0,
"priceAvg": 0.0,
"totalExposure": 0.0,
"stake": 0.0,
"stakeUnmatched": 0.0,
"stakeVoided": 0.0,
"reference": "string",
"createdAt": "string (date-time)",
"modifiedAt": "string (date-time)",
"trades": [
{
"tradeId": "string",
"side": "For | Against",
"price": 0.0,
"stake": 0.0,
"createdAt": "string (date-time)"
}
],
"cancellationReason": "string"
}
```
Order Created Example:
```text expandable wrap theme={null}
{"type":"OrderUpdate","orderId":"305137051","eventId":"56653","eventGroupId":"SULG","categoryId":"SPORTS","subCategoryId":"FOOTBALL","marketId":"1569503","walletId":"ANAPPID-User","status":"Unmatched","side":"For","outcomeIndexId":"3517754","price":14.0,"priceAvg":null,"totalExposure":0,"stake":7.0,"stakeUnmatched":7.0,"stakeVoided":0,"reference":"YourReference001","createdAt":"2026-09-11T07:22:38.552Z","modifiedAt":"2026-09-11T07:22:38.552Z","trades":[],"cancellationReason":null}
```
Order Cancelled Example:
```text expandable wrap theme={null}
{"type":"OrderUpdate","orderId":"305137051","eventId":"56653","eventGroupId":"SULG","categoryId":"SPORTS","subCategoryId":"FOOTBALL","marketId":"1569503","walletId":"ANAPPID-User","status":"Cancelled","side":"For","outcomeIndexId":"3517754","price":14.00,"priceAvg":null,"totalExposure":0,"stake":7.000000000000,"stakeUnmatched":0,"stakeVoided":7.000000000000,"reference":"YourReference002","createdAt":"2026-09-11T07:22:38.552Z","modifiedAt":" ,"trades":[],"cancellationReason":"Requested"}
```
### WalletUpdateMessage
```json theme={null}
{
"walletId": "string",
"currencyId": "string",
"balance": 0.0,
"change": 0.0
}
```
## Next steps
For session creation and access tokens, refer to the [REST API (BetDEX)](/rest-api/overview) documentation.