Introduction
Be your own bookmaker or fill orders programmatically with the SX.bet API!
Technical questions or need support? Send us an e-mail.
We support betting in USDC, WETH, and WSX
DEPRECATED: If you wish to use our wrapper for javascript you can do so here
Token | SX Network Address |
---|---|
USDC | 0xe2aa35C2039Bd0Ff196A6Ef99523CC0D3972ae3e |
WETH | 0xA173954Cc4b1810C0dBdb007522ADbC182DaB380 |
WSX | 0xaa99bE3356a11eE92c3f099BD7a038399633566f |
Explorer available here
API Key
In order to use the SX.Bet API, you do NOT need an API Key. The standard API user can perform all the requests provided in this document. There is a baseline rate limiter applied to all requests with or without an API Key. The API Key will give you elevated privilages on certain functions.
Generating API Key
- Visit sx.bet and register/login to your account. You can connect your MetaMask wallet, or login using your Fortmatic email address.
- If using MetaMask,
sign
the Signature Request. - Click the
Account
tab on the top navigation bar. - Click the
Overview
tab on the account navigation bar. - You will see an
API Credentials
card. Complete theEnhanced Verification
if you have not yet done so by clicking the link in the card. - If you've signed in and completed the enhanced verification, you can now click the button:
GENERATE API KEY NOW
. An API Key will be displayed. - The API Key generated will not be displayed again, so please copy and save this key for future use.
Usage
curl --location --request GET 'https://api.sx.bet/user/token' \
--header 'X-Api-Key: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX'
You must fill your API Key in the header
X-Api-Key
. The above is a sample command to create an Ably Token Request.
Once your API Key is generated (see above), you must add it as a HTTP Header with the name: X-Api-Key
.
Markets
Get active markets
curl --location --request GET 'https://api.sx.bet/markets/active?onlyMainLine=true'
The above command returns JSON structured like this:
{
"status": "success",
"data": {
"markets": [
{
"status": "ACTIVE",
"marketHash": "0x0d64c52e8781acdada86920a2d1e5acd6f29dcfe285cf9cae367b671dff05f7d",
"outcomeOneName": "Nikoloz Basilashvili",
"outcomeTwoName": "Carlos Alcaraz",
"outcomeVoidName": "NO_CONTEST",
"teamOneName": "Nikoloz Basilashvili",
"teamTwoName": "Carlos Alcaraz",
"type": 226,
"gameTime": 1622735700,
"sportXeventId": "L7032829",
"liveEnabled": true,
"sportLabel": "Tennis",
"sportId": 6,
"leagueId": 1070,
"homeTeamFirst": true,
"leagueLabel": "ATP French Open",
"group1": "ATP French Open"
},
{
"status": "ACTIVE",
"marketHash": "0xe609a49d083cd41214a0db276c1ba323c4a947eefd2e4260386fec7b5d258188",
"outcomeOneName": "Over 36.5",
"outcomeTwoName": "Under 36.5",
"outcomeVoidName": "NO_GAME_OR_EVEN",
"teamOneName": "Nikoloz Basilashvili",
"teamTwoName": "Carlos Alcaraz",
"type": 2,
"gameTime": 1622735700,
"line": 36.5,
"sportXeventId": "L7032829",
"liveEnabled": true,
"sportLabel": "Tennis",
"sportId": 6,
"leagueId": 1070,
"homeTeamFirst": true,
"leagueLabel": "ATP French Open",
"mainLine": true,
"group1": "ATP French Open"
},
{
"status": "ACTIVE",
"marketHash": "0x85e588d72b4a2ec6386846a6f4706dba2124410e38bd8e8f7f37dee9728e0d84",
"outcomeOneName": "Nikoloz Basilashvili +1.5",
"outcomeTwoName": "Carlos Alcaraz -1.5",
"outcomeVoidName": "NO_GAME_OR_EVEN",
"teamOneName": "Nikoloz Basilashvili",
"teamTwoName": "Carlos Alcaraz",
"type": 3,
"gameTime": 1622735700,
"line": 1.5,
"sportXeventId": "L7032829",
"liveEnabled": true,
"sportLabel": "Tennis",
"sportId": 6,
"leagueId": 1070,
"homeTeamFirst": true,
"leagueLabel": "ATP French Open",
"mainLine": true,
"group1": "ATP French Open"
}
],
"nextKey": "60c7b8f54da0ad001aa3261c"
}
}
This endpoint retrieves active markets on the exchange. It does not return markets that have been settled or reported. Note that to retrieve odds for a particular market, you must query the orders endpoint the orders endpoint separately.
HTTP Request
GET https://api.sx.bet/markets/active
Query parameters
Name | Required | Type | Description |
---|---|---|---|
onlyMainLine | false | boolean | If set to true, the result will only include main lines on spread and over under markets |
eventId | false | string | If set, it will only include markets for a particular sportXeventId |
leagueId | false | number | If set, it will only include markets for a particular league ID |
sportIds | false | number[] | If set, it will only include markets for particular sport IDs (comma separated) |
liveOnly | false | boolean | If set, it will only include markets that are currently available for in-play betting |
betGroup | false | string | If set, it will only include markets for a particular bet group |
type | false | number | If set, it will only include markets for a particular market type. See below for the options |
paginationKey | false | string | Used for pagination. Pass the nextKey returned from the previous request to retrieve the next set of records. |
pageSize | false | number | Used for pagination. Requested page size. Each call will only return up to this amount of records. Maximum of 50 |
Response format
Name | Type | Description |
---|---|---|
status | string | success or failure if the request succeeded or not |
data | object | The response data |
> markets | Market[] | The active markets |
> nextKey | string | Use this key as the paginationKey to retrieve the next set of records, if any |
A market
object looks like this
Name | Type | Description |
---|---|---|
status | string | ACTIVE or INACTIVE |
marketHash | string | The unique identifier for the market |
outcomeOneName | string | Outcome one for this market |
outcomeTwoName | string | Outcome two for this market |
outcomeVoidName | string | Outcome void for this market |
teamOneName | string | The name of the first team/player participating |
teamTwoName | string | The name of the second team/player participating |
type | MarketType | The type of the market |
gameTime | number | The UNIX timestamp of the game |
line | number? | The line of the market. Only applicable to markets with a line |
sportXeventId | string | The unique event ID for this market |
liveEnabled | boolean | Whether or not this match is available for live betting |
sportLabel | string | The name of the sport for this market |
sportId | number | The ID of the sport for this market |
leagueId | number | The league ID for this market |
homeTeamFirst | boolean | Indicator to the client of whether to display the home team first or not |
leagueLabel | string | The name of the league for this market |
mainLine | boolean? | If this market is currently the main line or not. If the market is not a market with multiple lines, this field will not be present |
group1 | string | Indicator to the client of how to display this market |
group2 | string? | Indicator to the client of how to display this market |
teamOneMeta | string? | Extra metadata for team one |
teamTwoMeta | string? | Extra metadata for team two |
marketMeta | string? | Extra metadata for the market overall |
legs | Market[]? | If this is a Parlay Market, this field will contain an array of the underlying Legs as a Market object |
A MarketType
can currently be one of the following
ID | Name | Has Lines | Description | Bet Group |
---|---|---|---|---|
1 | 1X2 | false | Who will win the game (1X2) | 1X2 |
52 | 12 | false | Who will win the game | game-lines |
88 | To Qualify | false | Which team will qualify | game-lines |
226 | 12 Including Overtime | false | Who will win the game including overtime (no draw) | game-lines |
3 | Asian Handicap | true | Who will win the game with handicap (no draw) | game-lines |
201 | Asian Handicap Games | true | Who will win more games with handicap (no draw) | game-lines |
342 | Asian Handicap Including Overtime | true | Who will win the game with handicap (no draw) including Overtime | game-lines |
2 | Under/Over | true | Will the score be under/over a specific line | game-lines |
835 | Asian Under/Over | true | Will the score be under/over specific asian line | game-lines |
28 | Under/Over Including Overtime | true | Will the score including overtime be over/under a specific line | game-lines |
29 | Under/Over Rounds | true | Will the number of rounds in the match will be under/over a specific line | game-lines |
166 | Under/Over Games | true | Number of games will be under/over a specific line | game-lines |
1536 | Under/Over Maps | true | Will the number of maps be under/over a specific line | game-lines |
274 | Outright Winner | false | Winner of a tournament, not a single match | outright-winner |
202 | First Period Winner | false | Who will win the 1st Period Home/Away | first-period-lines |
203 | Second Period Winner | false | Who will win the 2nd Period Home/Away | second-period-lines |
204 | Third Period Winner | false | Who will win the 3rd Period Home/Away | third-period-lines |
205 | Fourth Period Winner | false | Who will win the 4th Period Home/Away | fourth-period-lines |
866 | Set Spread | true | Which team/player will win more sets with handicap | set-betting |
165 | Set Total | true | Number of sets will be under/over a specific line | set-betting |
53 | Asian Handicap Halftime | true | Who will win the 1st half with handicap (no draw) | first-half-lines |
64 | Asian Handicap First Period | true | Who will win the 1st period with handicap (no draw) | first-period-lines |
65 | Asian Handicap Second Period | true | Who will win the 2nd period with handicap (no draw) | second-period-lines |
66 | Asian Handicap Third Period | true | Who will win the 3rd period with handicap (no draw) | third-period-lines |
63 | 12 Halftime | false | Who will win the 1st half (no draw) | first-half-lines |
77 | Under/Over Halftime | true | Will the score in the 1st half be under/over a specific line | first-half-lines |
21 | Under/Over First Period | true | Will the score in the 1st period be under/over a specific line | first-period-lines |
45 | Under/Over Second Period | true | Will the score in the 2nd period be under/over a specific line | second-period-lines |
46 | Under/Over Third Period | true | Will the score in the 3rd period be under/over a specific line | third-period-lines |
281 | 1st Five Innings Asian handicap | true | Who will win the 1st five innings with handicap (no draw) | first-five-innings |
1618 | 1st 5 Innings Winner-12 | false | Who will win in the 1st five innings | first-five-innings |
236 | 1st 5 Innings Under/Over | true | Will the score in the 1st five innings be under/over a specific line | first-five-innings |
More types will be added continuously.
Get specific markets
curl --location --request GET 'https://api.sx.bet/markets/find'
The above command returns JSON structured like this:
{
"status": "success",
"data": [
{
"status": "ACTIVE",
"marketHash": "0x3cba25f2253035b015b9bb555c1bf900f6737704d57425dd2a5b60e929c33b81",
"outcomeOneName": "Over 2.5",
"outcomeTwoName": "Under 2.5",
"outcomeVoidName": "NO_GAME_OR_EVEN",
"teamOneName": "Aston Villa",
"teamTwoName": "Burnley",
"type": 2,
"gameTime": 1608228000,
"line": 2.5,
"reportedDate": 1608234719,
"outcome": 2,
"teamOneScore": 0,
"teamTwoScore": 0,
"sportXeventId": "L6247212",
"liveEnabled": false,
"sportLabel": "Soccer",
"sportId": 5,
"leagueId": 29,
"homeTeamFirst": true,
"leagueLabel": "English Premier League",
"group1": "English Premier League"
}
]
}
This endpoint retrieves specific markets
HTTP Request
GET https://api.sx.bet/markets/find
Query parameters
Name | Required | Type | Description |
---|---|---|---|
marketHashes | true | string[] | The market hashes of the markets to retrieve. Maximum 30. Comma separated. |
Response format
Name | Type | Description |
---|---|---|
status | string | success or failure if the request succeeded or not |
data | Market[] | The response data |
See active markets section for how the Market
object is formatted. Note that there are a few additional fields if you are querying a market that has been settled/reported:
Name | Type | Description |
---|---|---|
reportedDate | number | Time in unix seconds of when the market was reported |
outcome | number | The outcome of the market. Can be one of 0 1 or 2. 0 means the market was voided and stakes were returned to bettors. 1 means the outcome labeled outcomeOneName was the outcome. 2 means the outcome labeled outcomeTwoName was the outcome. |
teamOneScore | number | Final score of team one |
teamTwoScore | number | Final score of team two |
Popular markets
curl --location --request GET 'https://api.sx.bet/markets/popular'
The above command returns JSON structured like this:
{
"status": "success",
"data": [
{
"status": "ACTIVE",
"marketHash": "0x66fc26c008c724d5dec2fb0bf8fb8797ecc49a4302a785cc8e5e7faf96249d8a",
"outcomeOneName": "Ismagulov D.",
"outcomeTwoName": "Rafael da Silva Alves",
"outcomeVoidName": "NO_GAME",
"teamOneName": "Ismagulov D.",
"teamTwoName": "Rafael da Silva Alves",
"type": 226,
"gameTime": 1621724400,
"sportXeventId": "L6896568",
"liveEnabled": false,
"sportLabel": "Mixed Martial Arts",
"sportId": 7,
"leagueId": 34,
"homeTeamFirst": true,
"leagueLabel": "UFC",
"group1": "UFC"
},
{
"status": "ACTIVE",
"marketHash": "0xad3494f1bf10e826cd8e0faecd42e0f578f2bc9de748946fbe3d833a11764b89",
"outcomeOneName": "West Ham United -0.5",
"outcomeTwoName": "Southampton +0.5",
"outcomeVoidName": "NO_GAME_OR_EVEN",
"teamOneName": "West Ham United",
"teamTwoName": "Southampton",
"type": 3,
"gameTime": 1621782000,
"line": -0.5,
"sportXeventId": "L6973172",
"liveEnabled": false,
"sportLabel": "Soccer",
"sportId": 5,
"leagueId": 29,
"homeTeamFirst": true,
"leagueLabel": "English Premier League",
"mainLine": true,
"group1": "English Premier League"
}
]
}
This endpoint retrieves the top 10 popular markets by volume.
HTTP Request
GET https://api.sx.bet/markets/popular
Response format
Name | Type | Description |
---|---|---|
status | string | success or failure if the request succeeded or not |
data | Market[] | The response data |
See active markets section for how the Market
object is formatted
Parlay Markets
Bettors can request a custom parlay on SX.Bet by selecting multiple markets. When they submit a parlay request, a message is sent via Websocket (See this link for more details on the request). Once this message is sent, the web client will wait one second to allow market makers to submit orders for the parlay market.
Market makers can use the payload data from the Parlay Request to submit an order. Market makers have a three second window to post orders. After this point, bettors will be shown all available orders at the same time and no other orders will be viewable by the bettor.
Bettors can choose which order to take and will be able to fill orders like any other non-parlay order.
Market makers can cancel orders like any other non-parlay order.
Leagues
Get leagues
curl --location --request GET 'https://api.sx.bet/leagues'
The above command returns JSON structured like this:
{
"status": "success",
"data": [
{
"leagueId": 20,
"label": "The Memorial Tournament_Round 1",
"sportId": 4,
"active": false,
"homeTeamFirst": true
},
{
"leagueId": 5,
"label": "The Masters_Round 1",
"sportId": 4,
"active": false,
"homeTeamFirst": true
},
{
"leagueId": 1,
"label": "NBA",
"sportId": 1,
"active": true,
"homeTeamFirst": false
},
{
"leagueId": 28,
"label": "US Open_Round 4",
"sportId": 4,
"active": false,
"homeTeamFirst": true
},
{
"leagueId": 10,
"label": "Wells Fargo Championship_Round 1",
"sportId": 4,
"active": false,
"homeTeamFirst": true
},
{
"leagueId": 37,
"label": "RBC Heritage_Round 4",
"sportId": 4,
"active": false,
"homeTeamFirst": true
},
{
"leagueId": 3,
"label": "NHL",
"sportId": 2,
"active": true,
"homeTeamFirst": false
},
{
"leagueId": 34,
"label": "UFC",
"sportId": 7,
"active": true,
"homeTeamFirst": true
}
]
}
This endpoint returns all the leagues supported by SX.bet
HTTP Request
GET https://api.sx.bet/leagues
Query parameters
Name | Required | Type | Description |
---|---|---|---|
sportId | false | number | Only return leagues for this particular sport ID. |
Response format
Name | Type | Description |
---|---|---|
status | string | success or failure if the request succeeded or not |
data | League[] | An array of league objects |
A League
object looks like this
Name | Type | Description |
---|---|---|
leagueID | number | The ID for this league |
label | string | The name of this league |
sportID | number | The ID of the sport this league corresponds to |
active | boolean | Whether or not the league is active on SX.bet currently |
homeTeamFirst | boolean | Instructions to the client of how to show the team names for markets in this league. true if the home team is teamOneName , false if the home team is teamTwoName |
Get active leagues
curl --location --request GET 'https://api.sx.bet/leagues/active'
The above command returns JSON structured like this:
{
"status": "success",
"data": [
{
"leagueId": 34,
"label": "UFC",
"sportId": 7,
"homeTeamFirst": true,
"eventsByType": {
"game-lines": 36
}
},
{
"leagueId": 30,
"label": "Champions League_UEFA",
"sportId": 5,
"homeTeamFirst": true,
"eventsByType": {
"outright-winner": 32,
"game-lines": 10
}
},
{
"leagueId": 29,
"label": "English Premier League",
"sportId": 5,
"homeTeamFirst": true,
"eventsByType": {
"outright-winner": 23,
"game-lines": 10
}
},
{
"leagueId": 1197,
"label": "UEFA Nations League",
"sportId": 5,
"homeTeamFirst": true,
"eventsByType": {
"outright-winner": 19,
"game-lines": 2
}
},
{
"leagueId": 244,
"label": "Bundesliga",
"sportId": 5,
"homeTeamFirst": true,
"eventsByType": {
"game-lines": 9,
"outright-winner": 18
}
},
{
"leagueId": 1114,
"label": "La Liga",
"sportId": 5,
"homeTeamFirst": true,
"eventsByType": {
"game-lines": 10,
"outright-winner": 37
}
},
{
"leagueId": 1112,
"label": "Ligue 1",
"sportId": 5,
"homeTeamFirst": true,
"eventsByType": {
"game-lines": 20,
"outright-winner": 20
}
},
{
"leagueId": 1113,
"label": "Serie A",
"sportId": 5,
"homeTeamFirst": true,
"eventsByType": {
"outright-winner": 20,
"game-lines": 10
}
},
{
"leagueId": 1236,
"label": "Portugal Primeira Liga",
"sportId": 5,
"homeTeamFirst": true,
"eventsByType": {
"outright-winner": 17,
"game-lines": 9
}
}
]
}
This endpoint returns all the currently active leagues with markets in them.
HTTP Request
GET https://api.sx.bet/leagues/active
Query parameters
Name | Required | Type | Description |
---|---|---|---|
sportId | true | number | Only return active leagues under this sport |
Response format
See get leagues section for how the response object is formatted. There is an additional field eventsByType
which maps the number of unique events within a particular bet group (for example, game-lines
or outright-winner
).
Get league teams
curl --location --request GET 'https://api.sx.bet/leagues/teams/1'
The above command returns JSON structured like this:
{
"status": "success",
"data": {
"teams": [
{
"id": 725,
"name": "Juventus"
},
{
"id": 724,
"name": "Inter"
},
{
"id": 1058,
"name": "Napoli"
},
{
"id": 719,
"name": "Atalanta"
},
{
"id": 726,
"name": "Lazio"
},
{
"id": 728,
"name": "AC Milan"
},
{
"id": 732,
"name": "AS Roma"
},
{
"id": 723,
"name": "Fiorentina"
},
{
"id": 134312,
"name": "Sassuolo"
},
{
"id": 720,
"name": "Bologna"
},
{
"id": 733,
"name": "Torino"
},
{
"id": 1049,
"name": "Cagliari"
},
{
"id": 1062,
"name": "Sampdoria"
},
{
"id": 736,
"name": "Hellas Verona"
},
{
"id": 729,
"name": "Parma"
},
{
"id": 1057,
"name": "Genoa"
},
{
"id": 734,
"name": "Udinese"
},
{
"id": 1052,
"name": "Crotone"
},
{
"id": 115743,
"name": "Spezia"
},
{
"id": 134313,
"name": "Benevento"
}
]
}
}
This endpoint returns all the teams under a particular league
HTTP Request
GET https://api.sx.bet/leagues/teams/:id
Request parameters
Name | Required | Type | Description |
---|---|---|---|
id | true | number | Return the teams for this league ID |
Response format
Name | Type | Description |
---|---|---|
status | string | success or failure if the request succeeded or not |
data | object | The response data |
teams | Team[] | An array of teams |
A Team
object looks like this
Name | Type | Description |
---|---|---|
id | number | The ID for this team |
name | string | The name of this team |
Sports
Get sports
curl --location --request GET 'https://api.sx.bet/sports'
The above command returns JSON structured like this:
{
"status": "success",
"data": [
{
"sportId": 1,
"label": "Basketball"
},
{
"sportId": 2,
"label": "Hockey"
},
{
"sportId": 3,
"label": "Baseball"
},
{
"sportId": 4,
"label": "Golf"
},
{
"sportId": 5,
"label": "Soccer"
},
{
"sportId": 6,
"label": "Tennis"
},
{
"sportId": 7,
"label": "Mixed Martial Arts"
},
{
"sportId": 8,
"label": "Football"
},
{
"sportId": 9,
"label": "E Sports"
},
{
"sportId": 10,
"label": "Custom"
},
{
"sportId": 11,
"label": "Rugby Union"
},
{
"sportId": 12,
"label": "Racing"
},
{
"sportId": 13,
"label": "Boxing"
},
{
"sportId": 14,
"label": "Crypto"
},
{
"sportId": 15,
"label": "Cricket"
},
{
"sportId": 16,
"label": "Economics"
},
{
"sportId": 17,
"label": "Politics"
},
{
"sportId": 18,
"label": "Entertainment"
},
{
"sportId": 19,
"label": "Medicinal"
},
{
"sportId": 20,
"label": "Rugby League"
}
]
}
This endpoint retrieves all sports available on the exchange
HTTP Request
GET https://api.sx.bet/sports
Response format
Name | Type | Description |
---|---|---|
status | string | success or failure if the request succeeded or not |
data | Sport[] | Sports available on the exchange |
A Sport
object looks like
Name | Type | Description |
---|---|---|
sportId | number | The ID of the sport |
label | string | The sport name |
Fixtures
Get fixtures
curl --location --request GET 'https://api.sx.bet/fixture/active?leagueId=2'
The above command returns JSON structured like this:
{
"status": "success",
"data": [
{
"participantOneName": "Nevada Wolf Pack",
"participantTwoName": "North Dakota State",
"startDate": "2020-11-25T20:00:00.000Z",
"status": 1,
"leagueId": 2,
"leagueLabel": "NCAA",
"sportId": 1,
"eventId": "L6206070"
},
{
"participantOneName": "UMass Lowell River Hawks",
"participantTwoName": "San Francisco Dons",
"startDate": "2020-11-25T20:00:00.000Z",
"status": 1,
"leagueId": 2,
"leagueLabel": "NCAA",
"sportId": 1,
"eventId": "L6208648"
},
{
"participantOneName": "William Jewell",
"participantTwoName": "Indianapolis",
"startDate": "2020-11-28T03:45:00.000Z",
"status": 1,
"leagueId": 2,
"leagueLabel": "NCAA",
"sportId": 1,
"eventId": "L6217784"
}
]
}
This endpoint returns current active fixtures for a particular league. A fixture can also be thought of as an event and multiple markets are under a particular event. Note that this endpoint only returns fixtures that have a status of either 1
, 2
, 6
, 7
, 8
, or 9
. See the status table in this section for more details.
HTTP Request
GET https://api.sx.bet/fixture/active
Query parameters
Name | Required | Type | Description |
---|---|---|---|
leagueId | true | number | The ID of the league |
Response format
Name | Type | Description |
---|---|---|
status | string | success or failure if the request succeeded or not |
data | Fixture[] | The active fixtures for this particular league |
A Fixture
object looks like this
Name | Type | Description |
---|---|---|
participantOneName | string? | The first participant in the fixture. Present if it's a two-participant event. |
participantTwoName | string? | The second participant in the fixture. Present if it's a two-participant event. |
participants | string[]? | All the participants in the fixture. Present if it's an n-participant event. |
startDate | string | The start date of the event in UTC time |
status | number | The status of the fixture. See the status table in this section for more details. |
leagueId | number | The ID of the league this fixture belongs to |
leagueLabel | string | The name of the league this fixture belongs to |
sportId | number | The ID of the sport this fixture belongs to |
eventId | string | The ID of this fixture |
Get fixture status
curl --location --request GET 'https://api.sx.bet/fixture/status'
The above command returns JSON structured like this:
{
"status": "success",
"data": {
"L6217784": 1
}
}
This endpoint returns the status of the passed event IDs.
HTTP Request
GET https://api.sx.bet/fixture/status
Query parameters
Name | Required | Type | Description |
---|---|---|---|
sportXeventIds | true | string[] | An array of event IDs (comma separated) |
Response format
Name | Type | Description |
---|---|---|
status | string | success or failure if the request succeeded or not |
data | obj | Mapping from event Id to a status flag for each event's status. |
The possible statuses for a fixture are the following
ID | Name | Description |
---|---|---|
1 | Not started yet | The event has not started yet |
2 | In progress | The event is live |
3 | Finished | The event is finished |
4 | Cancelled | The event has been cancelled |
5 | Postponed | The event has been postponed. Postponed is sent for events which are postponed and will be played at a later time. In case no new start date/time is available within 48 hours, the event will be cancelled. In case a new start time is available within 48 hours, we will update it to "Not started yet" with the new start time. |
6 | Interrupted | The event has been interrupted. Interrupted is sent for interrupted events (for example - rain delay). We will continue the coverage once the event is renewed, under the same event ID. |
7 | Abandoned | The event has been abandoned. Abandoned is a final status sent for abandoned events (player injury in Tennis for example), these matches will not resume. |
8 | Coverage lost | The coverage for this event has been lost |
9 | About to start | The event has not started but is about to. Note: this status will be shown up to 30 minutes before the event has started. |
Trades
Get active trades
curl --location --request GET 'https://api.sx.bet/trades'
The above command returns JSON structured like this:
{
"status": "success",
"data": {
"trades": [
{
"baseToken": "0x8f3Cf7ad23Cd3CaDbD9735AFf958023239c6A063",
"bettor": "0x63a4491dC73245E181c47BAe0ae9d6627E56dE55",
"stake": "10236134166947574099",
"odds": "50583446830801460000",
"orderHash": "0xb47329db2a3f612748094f415f9bf478cbed2f196548ec68154bd1fc543a6f09",
"marketHash": "0x3fd03af14bf11264f5274ed4c8cc283e4479d29d33e17409e8e7d9b26ca9f030",
"maker": true,
"betTime": 1607708054,
"settled": true,
"settleValue": 1,
"bettingOutcomeOne": false,
"fillHash": "0xbe846c92bec584c4d2215df76ac7d53ebab25f81a30cca5811fb93f35e8b5321",
"tradeStatus": "SUCCESS",
"valid": true,
"outcome": 1,
"settleDate": "2020-12-11T20:17:45.990Z"
},
{
"baseToken": "0x8f3Cf7ad23Cd3CaDbD9735AFf958023239c6A063",
"bettor": "0x683bcf3ecc5A6e2E99ff83f3300515a584108391",
"stake": "9999999999999999999",
"odds": "49416553169198540000",
"orderHash": "0xb47329db2a3f612748094f415f9bf478cbed2f196548ec68154bd1fc543a6f09",
"marketHash": "0x3fd03af14bf11264f5274ed4c8cc283e4479d29d33e17409e8e7d9b26ca9f030",
"maker": false,
"betTime": 1607708054,
"settled": true,
"settleValue": 1,
"bettingOutcomeOne": true,
"fillHash": "0xbe846c92bec584c4d2215df76ac7d53ebab25f81a30cca5811fb93f35e8b5321",
"tradeStatus": "SUCCESS",
"valid": true,
"outcome": 1,
"settleDate": "2020-12-11T20:17:45.990Z"
},
{
"baseToken": "0x8f3Cf7ad23Cd3CaDbD9735AFf958023239c6A063",
"bettor": "0xE965292B97CD666e85FaB99e2732b1A71046cf3F",
"stake": "17592800276256254757",
"odds": "69184587813620070000",
"orderHash": "0x1534133364d8b18d803a9419914bb89a651de5e9fa1845868d6844a6670c4762",
"marketHash": "0xcb8285aeef17d824b76cf4a00ba5f2bf256048114937c85609897e7b8967e9ca",
"maker": true,
"betTime": 1607719092,
"settled": true,
"settleValue": 1,
"bettingOutcomeOne": true,
"fillHash": "0xc0240cf27c111d843bc4cf2de0521ab097223de933125857343e7a6fba469172",
"tradeStatus": "SUCCESS",
"valid": true,
"outcome": 1,
"settleDate": "2020-12-11T22:02:19.484Z"
},
{
"baseToken": "0x8f3Cf7ad23Cd3CaDbD9735AFf958023239c6A063",
"bettor": "0x5aC843EecBf67669d4003aa49aE5e0136dc73365",
"stake": "7835984995472770999",
"odds": "30815412186379930000",
"orderHash": "0x1534133364d8b18d803a9419914bb89a651de5e9fa1845868d6844a6670c4762",
"marketHash": "0xcb8285aeef17d824b76cf4a00ba5f2bf256048114937c85609897e7b8967e9ca",
"maker": false,
"betTime": 1607719092,
"settled": true,
"settleValue": 1,
"bettingOutcomeOne": false,
"fillHash": "0xc0240cf27c111d843bc4cf2de0521ab097223de933125857343e7a6fba469172",
"tradeStatus": "SUCCESS",
"valid": true,
"outcome": 1,
"settleDate": "2020-12-11T22:02:19.484Z"
}
],
"nextKey": "60e4b70dc476a37a5b1b15ae",
"pageSize": 4
}
}
This endpoint retrieves past trades on the exchange split up by order. This is a paginated endpoint. For example, if a trade fills more than one order at once, it will show up as two entries for the bettor.
HTTP Request
GET https://api.sx.bet/trades
Query parameters
Name | Required | Type | Description |
---|---|---|---|
startDate | false | number | Only get trades placed after this time in UNIX seconds |
endDate | false | number | Only get trades placed before this time in UNIX seconds |
bettor | false | string | Only get trades placed by this bettor (regardless if maker or taker) |
settled | false | boolean | If true , only get settled trades |
marketHashes | false | string[] | Only get trades for particular markets. Comma separated |
baseToken | false | string | Only get trades placed for a particular token |
maker | false | boolean | If true , only get trades where the bettor is the maker |
affiliate | false | string | Only get trades under this affiliate |
pageSize | false | number | Requested page size. Each call will only return up to this amount of records. Default is 100. |
paginationKey | false | string | Used for pagination. Pass the nextKey returned from the previous request to retrieve the next set of records. |
tradeStatus | false | string | Filter trades to see only those with SUCCESS or FAILED status' |
Response format
Name | Type | Description |
---|---|---|
status | string | success or failure if the request succeeded or not |
data | object | The response data |
> trades | Trade[] | The trades for the request |
> nextKey | string | Use this key as the paginationKey to retrieve the next set of records, if any |
> pageSize | number | Maximum amount of records on this page. Will be equal to the pageSize passed in |
A Trade
object has the following format
Name | Type | Description |
---|---|---|
baseToken | string | The token in which this trade was placed |
bettor | string | The address of the bettor who placed the trade |
stake | string | Exact token amount that was staked for the bet. To convert into a readable token amount, see the token conversion section |
odds | string | Implied odds that the bettor received for this bet. Divide by 10^20 to get the odds in decimal format. |
orderHash | string | The unique identifier of the order that was filled for this trade |
marketHash | string | The unique identifier of the market for which this trade was placed |
maker | boolean | true if the bettor is market maker in this trade |
betTime | number | The time in UNIX seconds when the trade was placed |
settled | boolean | true if this bet is settled (this refers to if the bet was won lost or voided, not if the trade succeeded or not) |
bettingOutcomeOne | boolean | true if the bettor is betting outcome one in the market |
fillHash | string | The unique identifier for this trade |
tradeStatus | string | SUCCESS or FAILED depending on if this trade succeeded or not |
valid | boolean | true if the trade counts toward competitions or tournaments |
outcome | number | with settled=true , this will be 0, 1, or 2 depending on the final outcome of the market |
settleDate | string | ISO formatted date string of when the trade was settled |
Get consolidated trades
curl --location --request GET 'https://api.sx.bet/trades/consolidated'
The above command returns JSON structured like this:
{
"status": "success",
"data": {
"trades": [
{
"baseToken": "0x8f3Cf7ad23Cd3CaDbD9735AFf958023239c6A063",
"tradeStatus": "SUCCESS",
"bettor": "0x025b9FD9F1ed818d688e19c5F43c500bf44cEA47",
"totalStake": "4.999999999999999999",
"weightedAverageOdds": "67564738292011016000",
"marketHash": "0x7b27d766b6e36b87e2b7924f792c4f84d9b459318735d69cbb7d695b0857f8d5",
"maker": false,
"settled": false,
"fillHash": "0xc44e72f94e70ede9742c6b8aeb77e743e0cbf753ff3eae8bd4f1ab98b9260f0e",
"gameLabel": "Australia vs New Zealand",
"bettingOutcomeLabel": "Australia",
"sportXeventId": "L6350002",
"bettingOutcome": 1,
"gameTime": "2021-01-06T23:30:00.000Z",
"leagueLabel": "International Test",
"outcome": 0,
"netReturn": "4.999999999999999999"
},
{
"baseToken": "0x8f3Cf7ad23Cd3CaDbD9735AFf958023239c6A063",
"tradeStatus": "SUCCESS",
"bettor": "0x025b9FD9F1ed818d688e19c5F43c500bf44cEA47",
"totalStake": "4.999999999999999999",
"weightedAverageOdds": "67564738292011016000",
"marketHash": "0x7b27d766b6e36b87e2b7924f792c4f84d9b459318735d69cbb7d695b0857f8d5",
"maker": false,
"settled": false,
"fillHash": "0xc44e72f94e70ede9742c6b8aeb77e743e0cbf753ff3eae8bd4f1ab98b9260f0e",
"gameLabel": "Australia vs New Zealand",
"bettingOutcomeLabel": "Australia",
"sportXeventId": "L6350002",
"bettingOutcome": 1,
"gameTime": "2021-01-06T23:30:00.000Z",
"leagueLabel": "International Test",
"outcome": 0,
"netReturn": "4.999999999999999999"
}
],
"count": 100
}
}
This endpoint retrieves past consolidated trades on the exchange via pagination. If a trade fills multiple orders, it will show up as one entry here per bettor.
HTTP Request
GET https://api.sx.bet/trades/consolidated
Query parameters
Name | Required | Type | Description |
---|---|---|---|
bettor | false | string | Only get trades placed by this bettor |
settled | true | boolean | If true only get settled trades |
page | true | number | Page number for pagination |
perPage | true | number | Amount of records to fetch per page |
sortBy | true | string | Which field to sort by (see response for field names) |
sortAsc | true | boolean | If true , sorts in ascending order |
maker | false | boolean | If true , only gets trades where the bettor was a market maker |
sportXeventId | false | string | Only gets trades for this event ID |
tradeStatus | false | string | Filter trades to see only those with SUCCESS or FAILED status' |
Response format
Name | Type | Description |
---|---|---|
status | string | success or failure if the request succeeded or not |
data | any | The response data |
> trades | ConsolidatedTrade[] | The consolidated trades for this request |
> count | number | Total count of trades for this query |
A ConsolidatedTrade
object has the following format
Name | Type | Description |
---|---|---|
baseToken | string | The token in which this trade was placed |
tradeStatus | string | SUCCESS or FAILED depending on if this trade succeeded or not |
bettor | string | The address of the bettor who placed the trade |
totalStake | string | Total nominal stake of the trade |
weightedAverageOdds | string | Weighted average odds (based on stake) of the trade if the trade filled multiple orders. Divide by 10^20 to get the odds in decimal format. |
marketHash | string | The unique identifier of the market for which this trade was placed |
maker | boolean | true if the bettor is market maker in this trade |
settled | boolean | true if this bet is settled (this refers to if the bet was won lost or voided, not if the trade succeeded or not) |
fillHash | string | The unique identifier for this trade |
gameLabel | string | A general label for the market |
bettingOutcomeLabel | string | Which team/side the bettor bet |
sportXeventId | string | The unique fixture ID for this trade |
bettingOutcome | number | 1 if the bettor is betting outcome one, 2 otherwise |
gameTime | string | ISO formatted date string of when the game is suppossed to occur |
leagueLabel | string | The name of this league |
outcome | number | With settled=true , this will be 0, 1, or 2 depending on the final outcome of the market |
Live scores
Get live scores
curl --location --request GET 'https://api.sx.bet/live-scores'
The above command returns JSON structured like this:
{
"status": "success",
"data": [
{
"createdAt": "2021-07-08T17:57:38.057Z",
"currentPeriod": "8th Inning",
"extra": "[{\"Name\":\"Strikes\",\"Value\":\"0\"},{\"Name\":\"Turn\",\"Value\":\"2\"},{\"Name\":\"Balls\",\"Value\":\"1\"},{\"Name\":\"Outs\",\"Value\":\"2\"},{\"Name\":\"Bases\",\"Value\":\"0/0/0\"}]",
"leagueId": 171,
"periodTime": "-1",
"periods": [
{
"label": "1st Inning",
"isFinished": true,
"teamOneScore": "0",
"teamTwoScore": "2"
},
{
"label": "2nd Inning",
"isFinished": true,
"teamOneScore": "0",
"teamTwoScore": "0"
},
{
"label": "3rd Inning",
"isFinished": true,
"teamOneScore": "0",
"teamTwoScore": "0"
},
{
"label": "4th Inning",
"isFinished": true,
"teamOneScore": "0",
"teamTwoScore": "0"
},
{
"label": "5th Inning",
"isFinished": true,
"teamOneScore": "0",
"teamTwoScore": "0"
},
{
"label": "6th Inning",
"isFinished": true,
"teamOneScore": "0",
"teamTwoScore": "0"
},
{
"label": "7th Inning",
"isFinished": true,
"teamOneScore": "1",
"teamTwoScore": "0"
},
{
"label": "8th Inning",
"isFinished": false,
"teamOneScore": "0",
"teamTwoScore": "0"
}
],
"sportId": 3,
"teamOneScore": 1,
"teamTwoScore": 2,
"updatedAt": "2021-07-08T20:35:21.607Z",
"sportXeventId": "L7187811"
}
]
}
This endpoint retrieves live scores for a particular event ID.
HTTP Request
GET https://api.sx.bet/live-scores
Query parameters
Name | Required | Type | Description |
---|---|---|---|
sportXEventIds | true | string[] | An array of event IDs |
Response format
Name | Type | Description |
---|---|---|
status | string | success or failure if the request succeeded or not |
data | Score[] | The resulting scores for the fixtures passed in |
A Score
object has the following format
Name | Type | Description |
---|---|---|
currentPeriod | string | The current period label |
extra | string | Extra data for this match |
leagueId | number | The league ID for this match |
periodTime | number | The time in the period. If it's -1, it is not available or the game is finished |
periods | Period[] | All of the periods in the match |
sportId | number | The ID of the sport for this match |
teamOneScore | number | The current score for team one |
teamTwoScore | number | The current score for team two |
sportXeventId | string | The event ID for this match |
A Period
object has the following format
Name | Type | Description |
---|---|---|
label | string | The current period name |
isFinished | boolean | true if the period is over |
teamOneScore | string | The score of team one in this period |
teamTwoScore | string | The score of team two in this period |
Metadata
Get metadata
curl --location --request GET 'https://api.sx.bet/metadata'
The above command returns JSON structured like this
{
"status": "success",
"data": {
"executorAddress": "0x52adf738AAD93c31f798a30b2C74D658e1E9a562",
"oracleFees": {
"0xA173954Cc4b1810C0dBdb007522ADbC182DaB380": "2600000000000000000",
"0xe2aa35C2039Bd0Ff196A6Ef99523CC0D3972ae3e": "2600000000000000000",
"0xaa99bE3356a11eE92c3f099BD7a038399633566f": "2600000000000000000"
},
"sportXAffiliate": {
"address": "0xa21ac1436f7fcD43008c9473A78433339E222FcA",
"amount": "1400000000000000000"
},
"makerOrderMinimums": {
"0xA173954Cc4b1810C0dBdb007522ADbC182DaB380": "5000000000000000",
"0xe2aa35C2039Bd0Ff196A6Ef99523CC0D3972ae3e": "10000000",
"0xaa99bE3356a11eE92c3f099BD7a038399633566f": "30000000000000000000"
},
"takerMinimums": {
"0xA173954Cc4b1810C0dBdb007522ADbC182DaB380": "2500000000000000",
"0xe2aa35C2039Bd0Ff196A6Ef99523CC0D3972ae3e": "5000000",
"0xaa99bE3356a11eE92c3f099BD7a038399633566f": "30000000000000000000"
},
"addresses": {
"416": {
"WETH": "0xA173954Cc4b1810C0dBdb007522ADbC182DaB380",
"USDC": "0xe2aa35C2039Bd0Ff196A6Ef99523CC0D3972ae3e",
"WSX": "0xaa99bE3356a11eE92c3f099BD7a038399633566f"
}
},
"totalVolume": 214120305.80276245,
"domainVersion": "4.0",
"EIP712FillHasher": "0x3E96B0a25d51e3Cc89C557f152797c33B839968f",
"TokenTransferProxy": "0xCc4fBba7D0E0F2A03113F42f5D3aE80d9B2aD55d",
"bridgeFee": 1,
"oddsLadderStepSize": 25
}
}
This endpoint retrieves metadata on the exchange itself and useful parameters to interact with the exchange.
HTTP Request
GET https://api.sx.bet/metadata
Response format
Name | Type | Description |
---|---|---|
status | string | success or failure if the request succeeded or not |
data | any | The metadata for the exchange |
executorAddress | string | The executor address for the sx.bet exchange. Use this address for setting the executor when posting new orders as a market maker |
oracleFees | any | A mapping from token address to a percentage indicating the fees paid on profit for each token. To convert to a readable number, divide by 10^20 |
sportXAffiliate | any | The default sx.bet affiliate. Currently set to 0 and unused |
makerOrderMinimums | any | A mapping from token address to a real token amount indicating the minimum maker order size. To convert into a readable amount, see the token conversion section |
takerMinimums | any | A mapping from token address to a real token amount indicating the minimum taker order size. To convert into a readable amount, see the token conversion section |
addresses | any | A mapping from network id -> canonical token name -> token address of assets supported by the exchange |
totalVolume | number | All time total volume on the exchange |
domainVersion | string | Used in EIP712 signing |
EIP712FillHasher | string | Address used in EIP712 signing for filling orders |
TokenTransferProxy | string | Address used in EIP712 signing for enabling betting |
bridgeFee | number | USD fee for bridge transaction from Polygon to SX Network |
oddsLadderStepSize | number | Odds ladder step size. See the post a new order section |
Orders
Get active orders
curl --location --request GET 'https://api.sx.bet/orders'
The above command returns JSON structured like this
{
"status": "success",
"data": [
{
"fillAmount": "0",
"orderHash": "0xb46e5fff6498f061e93c4f5ed501ee72d924180d6aa78cdfd4d188d3383c91d4",
"marketHash": "0x0eeace4a9bbf6235bc59695258a419ed3a05a2c8e3b6a58fb71a0d9e6b031c2b",
"maker": "0x63a4491dC73245E181c47BAe0ae9d6627E56dE55",
"totalBetSize": "10000000000000000000",
"percentageOdds": "70455284072443640000",
"expiry": 2209006800,
"apiExpiry": 1631233201
"baseToken": "0x8f3Cf7ad23Cd3CaDbD9735AFf958023239c6A063",
"executor": "0x3E91041b9e60C7275f8296b8B0a97141e6442d49",
"salt": "69415402816762328320330277846098411244657139277332120954321492419616371539163",
"isMakerBettingOutcomeOne": true,
"signature": "0x2aaea5b7c86166c0fbf2745c66aff794a23d21ac71ee143d08706700adbb59aa4c9b862286cf736acae5a74b10847ced73b628f4396eaab0af13b0c637fe4d021b",
"createdAt": "2021-06-04T17:42:07.257Z"
},
{
"fillAmount": "0",
"orderHash": "0xd055d177477bd19faa9bad5cb4f907d8ebe069b614bb708713de068293cb809d",
"marketHash": "0x0eeace4a9bbf6235bc59695258a419ed3a05a2c8e3b6a58fb71a0d9e6b031c2b",
"maker": "0x63a4491dC73245E181c47BAe0ae9d6627E56dE55",
"totalBetSize": "10000000000000000000",
"percentageOdds": "29542732332840140000",
"expiry": 2209006800,
"apiExpiry": 1631233201,
"baseToken": "0x8f3Cf7ad23Cd3CaDbD9735AFf958023239c6A063",
"executor": "0x3E91041b9e60C7275f8296b8B0a97141e6442d49",
"salt": "37069036490382455296196784649228360571791475783443923366499720348790829992442",
"isMakerBettingOutcomeOne": false,
"signature": "0x68fb16ff440c65e0306cb16a9842da362480208a9a75597d45ca722769d93e6a13c9196a2654f764bfd5c0d4c83165c0a8ffa955ae9af8afd17544b0db29eaf71c",
"createdAt": "2021-06-04T17:42:07.303Z"
},
{
"fillAmount": "0",
"orderHash": "0xa5a30ca2251ac1431adf3d88f5734a53b78a13b2c707211eb83996bf099e3973",
"marketHash": "0x15c5cceb3d27518241355e9f148ef96b0a178f1bcdb366dea2d0e621a9cef1fb",
"maker": "0x63a4491dC73245E181c47BAe0ae9d6627E56dE55",
"totalBetSize": "10000000000000000000",
"percentageOdds": "50000000000000000000",
"expiry": 2209006800,
"apiExpiry": 1631233201,
"baseToken": "0x8f3Cf7ad23Cd3CaDbD9735AFf958023239c6A063",
"executor": "0x3E91041b9e60C7275f8296b8B0a97141e6442d49",
"salt": "90344661128498016788545482097709376028896473001963632493180076229973632520043",
"isMakerBettingOutcomeOne": true,
"signature": "0x9b6c1e1d0cae584a3078f20d7bafd2031467a1cbce905027c6c970c13edef4357eeac625afe869417ffbecfb67b6672b765177d42fbda777f6b1a00962da2cc61c",
"createdAt": "2021-06-04T17:42:07.270Z"
}
]
}
This endpoint returns active orders on the exchange based on a few parameters
HTTP Request
GET https://api.sx.bet/orders
Query parameters
Name | Required | Type | Description |
---|---|---|---|
marketHashes | false | string[] | Only get orders for these market hashes. Comma separated. |
baseToken | false | string | Only get orders denominated in this base token |
maker | false | string | Only get orders for this market maker |
sportXeventId | false | string | Only get orders for this event ID |
Note that one of marketHashes
or maker
is required.
Response format
Name | Type | Description |
---|---|---|
fillAmount | string | How much this order has been filled in Ethereum units up to a max of totalBetSize . See the token section of how to convert this into nominal amounts |
orderHash | string | A unique identifier for this order |
marketHash | string | The market corresponding to this order |
maker | string | The market maker for this order |
totalBetSize | string | The total size of this order in Ethereum units. See the the token section section for how to convert this into nominal amounts. |
percentageOdds | string | The odds that the maker receives in the sx.bet protocol format. To convert to an implied odds divide by 10^20. To convert to the odds that the taker would receive if this order would be filled in implied format, use the formula takerOdds=1-percentageOdds/10^20 . See the unit conversion section for more details. |
expiry | number | Deprecated: the time in unix seconds after which this order is no longer valid. After deprecation, this field is always 2209006800 (2040) |
apiExpiry | number | The time in unix seconds after which this order is no longer valid. |
baseToken | string | The base token this order is denominated in |
executor | string | The address permitted to execute on this order. This is set to the sx.bet exchange |
salt | string | A random number to differentiate identical orders |
isMakerBettingOutcomeOne | boolean | true if the maker is betting outcome one (and hence taker is betting outcome two if filled) |
signature | string | Signature of the maker on this order |
Enabling betting
See the javascript section.
import { MaxUint256 } from "ethers/constants";
import { Contract } from "ethers";
import { JsonRpcProvider } from "ethers/providers";
const walletAddress = process.env.WALLET_ADDRESS;
const tokenAddress = process.env.TOKEN_ADDRESS;
const tokenTransferProxyAddress = process.env.TOKEN_TRANSFER_PROXY_ADDRESS;
const provider = new providers.JsonRpcProvider(`https://rpc.sx.technology`);
const wallet = new Wallet(process.env.PRIVATE_KEY).connect(provider);
const tokenContract = new Contract(
tokenAddress,
[
{
constant: false,
inputs: [
{ internalType: "address", name: "usr", type: "address" },
{ internalType: "uint256", name: "wad", type: "uint256" },
],
name: "approve",
outputs: [{ internalType: "bool", name: "", type: "bool" }],
payable: false,
stateMutability: "nonpayable",
type: "function",
},
],
wallet
);
await tokenContract.approve(tokenTransferProxyAddress, MaxUInt256, {
gasLimit: 100000,
});
To enable betting, you need to approve the TokenTransferProxy
contract for each token for which you wish to trade. Otherwise, any endpoints that create/cancel or fill orders will fail. For example if you want to trade with both ETH and USDC, you'll need to approve the contract twice, once for each token. The address of the TokenTransferProxy
is available at https://api.sx.bet/metadata
and the address of each token is given in the tokens section
If you don't wish to do this programmatically, you can simply go to https://sx.bet
, make a test bet with the account and token you'll be using, and you will be good to go.
If you want to do it programmatically, see the code sample on the right. Note you will need a little bit of MATIC to make this transaction (~$0.01 worth).
Post a new order
curl --location --request POST 'https://api.sx.bet/orders/new' \
--header 'Content-Type: application/json' \
--data-raw '{
"orders": [
{
"marketHash": "0x0eeace4a9bbf6235bc59695258a419ed3a05a2c8e3b6a58fb71a0d9e6b031c2b",
"maker": "0x6F75bA6c90E3da79b7ACAfc0fb9cf3968aa4ee39",
"totalBetSize": "21600000000000000000",
"percentageOdds": "47846889952153115000",
"baseToken": "0x8f3Cf7ad23Cd3CaDbD9735AFf958023239c6A063",
"apiExpiry": 1631359800,
"expiry": 2209006800,
"executor": "0x3E91041b9e60C7275f8296b8B0a97141e6442d49",
"isMakerBettingOutcomeOne": true,
"signature": "0x50b00e7994b0656f78701537296444bccba2a7e4d46a84ff26c8ca48cb66774c76faa893be293412959779900232065c8236e489158070777d7a3e1a37d911811b",
"salt": "61882422358902283358380622686147595792242782952753619716150366288606659190035"
}
]
}'
import { BigNumber, utils, providers, Wallet } from "ethers";
const order = {
marketHash:
"0x0eeace4a9bbf6235bc59695258a419ed3a05a2c8e3b6a58fb71a0d9e6b031c2b",
maker: "0x6F75bA6c90E3da79b7ACAfc0fb9cf3968aa4ee39",
totalBetSize: BigNumber.from("21600000000000000000").toString(),
percentageOdds: BigNumber.from("47846889952153115000").toString(),
baseToken: "0x8f3Cf7ad23Cd3CaDbD9735AFf958023239c6A063",
apiExpiry: 1631233201,
expiry: 2209006800,
executor: "0x3E91041b9e60C7275f8296b8B0a97141e6442d49",
isMakerBettingOutcomeOne: true,
salt: BigNumber.from(utils.randomBytes(32)).toString(),
};
const orderHash = utils.arrayify(
utils.solidityKeccak256(
[
"bytes32",
"address",
"uint256",
"uint256",
"uint256",
"uint256",
"address",
"address",
"bool",
],
[
order.marketHash,
order.baseToken,
order.totalBetSize,
order.percentageOdds,
order.expiry,
order.salt,
order.maker,
order.executor,
order.isMakerBettingOutcomeOne,
]
)
);
// Example shown here with an ethers.js wallet if you're interacting with the exchange using a private key
const wallet = new Wallet(process.env.PRIVATE_KEY);
const signature = await wallet.signMessage(orderHash);
// Example shown here if you're interacting with the exchange using an injected web3 provider such as metamask
const provider = new providers.Web3Provider(window.ethereum);
const signer = provider.getSigner();
const signature = await signer.signMessage(orderHash);
const signedOrder = { ...order, signature };
const result = await fetch("https://api.sx.bet/orders/new", {
method: "POST",
body: JSON.stringify({ orders: [signedOrder] }),
headers: { "Content-Type": "application/json" },
});
// Odds ladder testing
import { BigNumber } from "bignumber.js";
import { BigNumber as EthBigNumber } from "ethers";
export const ODDS_LADDER_STEP_SIZE = 25; // (0.1% = 1, 0.5% = 5, etc)
/**
* Check if the odds are valid, i.e., in one of the allowed steps
* @param odds Odds to check
*/
export function checkOddsLadderValid(
odds: EthBigNumber,
stepSizeOverride?: number
) {
// Logic:
// 100% = 10^20
// 10% = 10^19
// 1% = 10^18
// 0.1% = 10^17
return odds
.mod(EthBigNumber.from(10).pow(16).mul(ODDS_LADDER_STEP_SIZE))
.eq(0);
}
/**
* Rounds odds to the nearest step.
* @param odds Odds to round.
*/
export function roundDownOddsToNearestStep(
odds: EthBigNumber,
stepSizeOverride?: number
) {
const step = EthBigNumber.from(10).pow(16).mul(ODDS_LADDER_STEP_SIZE);
const bnStep = new BigNumber(step.toString());
const bnOdds = new BigNumber(odds.toString());
const firstPassDivision = bnOdds.dividedBy(bnStep).toFixed(0, 3);
return EthBigNumber.from(firstPassDivision).mul(step);
}
The above command returns JSON structured like this
{
"status": "success",
"data": {
"orders": [
"0x7a9d420551c4a635849013dd908f7894766e97aee25fe656d0c5ac857e166fac"
]
}
}
This endpoint offers new orders on the exchange (market making). Offering orders does not cost any fee or require you to have any MATIC tokens in your wallet.
Note you can offer as many orders as you wish, provided your total exposure for each token (as measured by totalBetSize - fillAmount
) remains under your wallet balance. If your wallet balance dips under your total exposure, orders will be removed from the book until it reaches the minimum again.
To offer bets on sx.bet via the API, make sure you first enable betting by following the steps here.
We enforce an odds ladder to prevent diming. Your offer, in implied odds, must fall on one of the steps on the ladder. Currently, that is set to intervals of 0.25%, meaning that your offer cannot fall between the steps. An offer of 50.25% would be valid, but an offer of 50.05% would not. You can check if your odds would fall on the ladder by taking the modulus of your odds and 2.5 * 10 ^ 17 and checking if it's equal to 0. See the bottom of the JavaScript tab for a sample on how to do this, and how to round your odds to the nearest step.
You can get the current interval from GET /metadata
. It will spit out a number from 10 to 100, where 10 = 0.10%, and 25 = 0.25%
HTTP Request
POST https://api.sx.bet/orders/new
Request payload parameters
Name | Required | Type | Description |
---|---|---|---|
orders | true | SignedNewOrder[] | The new orders to post |
A SignedNewOrder
object looks like this
Name | Type | Description |
---|---|---|
marketHash | string | The market you wish to place this order under |
maker | string | The ethereum address offering the bet |
baseToken | string | The token this order is denominated in |
totalBetSize | string | The total bet size of the order in Ethereum units. |
percentageOdds | string | The odds the maker will be receiving as this order gets filled. Must be on the odds ladder or will be rejected. |
expiry | number | Deprecated. Time in UNIX seconds after which this order is no longer valid. Must always be 2209006800. |
apiExpiry | number | Time in UNIX seconds after which this order is no longer valid. |
executor | string | The sx.bet executor address. See the metadata section for where to get this address |
salt | string | A random 32 byte string to differentiate between between orders with otherwise identical parameters |
isMakerBettingOutcomeOne | boolean | true if the maker is betting outcome one (and hence taker is betting outcome two if filled) |
signature | string | The signature of the maker on this order payload |
Response format
Name | Type | Description |
---|---|---|
status | string | success or failure if the request succeeded or not |
data | object | The response data |
> orders | string[] | The order hashes corresponding to the new orders |
Cancel individual orders
curl --location --request POST 'https://api.sx.bet/orders/cancel/v2' \
--header 'Content-Type: application/json' \
--data-raw '{
"orderHashes": [
"0x335d3dbd0621f0f6da90d1a58269e71b2fb5e91193dca75a0b90396cccb63001"
],
"signature": "0x1763cb98a069657cb778fdc295eac48741b957bfe58e54f7f9ad03c6c1ca3d053d9ca2e6957af794991217752b69cb9aa4ac9330395c92e24c8c25ec19220e5a1b",
"salt": "0x6845028402f518a1c90770554a71017cd434ae9f2c09aa56c9560835c1929650",
"maker": "0xe087299AE9Acd0133d6D1544A97Bb0EEe24a2671",
"timestamp": 1643897553
}'
import ethSigUtil from "eth-sig-util";
import { randomBytes } from "@ethersproject/random";
// Example is shown using a private key
const privateKey = process.env.PRIVATE_KEY;
const bufferPrivateKey = Buffer.from(privateKey.substring(2), "hex");
const orderHashes = [
"0x4ead6ef92741cd0b6e1ea32cb1d9586a85165e8bd780ab6f897992428c357bf1",
];
const salt = `0x${Buffer.from(randomBytes(32)).toString("hex")}`;
const timestamp = Math.floor(new Date().getTime() / 1000);
const wallet = new Wallet(privateKey);
function getCancelOrderEIP712Payload(orderHashes, salt, timestamp, chainId) {
const payload = {
types: {
EIP712Domain: [
{ name: "name", type: "string" },
{ name: "version", type: "string" },
{ name: "chainId", type: "uint256" },
{ name: "salt", type: "bytes32" },
],
Details: [
{ name: "orderHashes", type: "string[]" },
{ name: "timestamp", type: "uint256" },
],
},
primaryType: "Details",
domain: {
name: "CancelOrderV2SportX",
version: "1.0",
chainId,
salt,
},
message: { orderHashes, timestamp },
};
return payload;
}
const payload = getCancelOrderEIP712Payload(orderHashes, salt, timestamp, 416);
const signature = ethSigUtil.signTypedData_v4(bufferPrivateKey, {
data: payload,
});
const apiPayload = {
signature,
orderHashes,
salt,
maker: wallet.address,
timestamp,
};
const result = await fetch("https://api.sx.bet/orders/cancel/v2", {
method: "POST",
body: JSON.stringify(apiPayload),
headers: { "Content-Type": "application/json" },
});
The above command returns json structured like this
{
"status": "success",
"data": {
"cancelledCount": 1
}
}
This endpoint cancels existing orders on the exchange that you placed as a market maker. If passed orders that do not exist, they simply fail silently while the others will succeed.
HTTP Request
POST https://api.sx.bet/orders/cancel/v2
Request payload parameters
Name | Required | Type | Description |
---|---|---|---|
orderHashes | true | string[] | The order hashes to cancel |
signature | true | string | The EIP712 signature on the cancel order payload. See the EIP712 signing section for more details on how to compute this signature. |
salt | required | string | A random 32 bytes hex string to protect against replay |
maker | required | true | The account from which you are cancelling orders |
timestamp | required | true | The current timestamp in UNIX seconds to protect against replay |
Response format
Name | Type | Description |
---|---|---|
status | string | success or failure if the request succeeded or not |
data | object | The response data |
> cancelledCount | string[] | How many orders were cancelled, of the orders passed |
Cancel event orders
curl --location --request POST 'https://api.sx.bet/orders/cancel/event' \
--header 'Content-Type: application/json' \
--data-raw '{
"sportXeventId": "L1234123",
"signature": "0x1763cb98a069657cb778fdc295eac48741b957bfe58e54f7f9ad03c6c1ca3d053d9ca2e6957af794991217752b69cb9aa4ac9330395c92e24c8c25ec19220e5a1b",
"salt": "0x6845028402f518a1c90770554a71017cd434ae9f2c09aa56c9560835c1929650",
"maker": "0xe087299AE9Acd0133d6D1544A97Bb0EEe24a2671",
"timestamp": 1643898624
}'
import ethSigUtil from "eth-sig-util";
import { randomBytes } from "@ethersproject/random";
import { Wallet } from "@ethersproject/wallet";
// Example is shown using a private key
const privateKey = process.env.PRIVATE_KEY;
const bufferPrivateKey = Buffer.from(privateKey.substring(2), "hex");
const sportXeventId = "L1231231";
const salt = `0x${Buffer.from(randomBytes(32)).toString("hex")}`;
const timestamp = Math.floor(new Date().getTime() / 1000);
const wallet = new Wallet(privateKey);
function getCancelOrderEventsEIP712Payload(
sportXeventId,
salt,
timestamp,
chainId
) {
const payload = {
types: {
EIP712Domain: [
{ name: "name", type: "string" },
{ name: "version", type: "string" },
{ name: "chainId", type: "uint256" },
{ name: "salt", type: "bytes32" },
],
Details: [
{ name: "sportXeventId", type: "string" },
{ name: "timestamp", type: "uint256" },
],
},
primaryType: "Details",
domain: {
name: "CancelOrderEventsSportX",
version: "1.0",
chainId,
salt,
},
message: { sportXeventId, timestamp },
};
return payload;
}
const payload = getCancelOrderEventsEIP712Payload(
sportXeventId,
salt,
timestamp,
416
);
const signature = ethSigUtil.signTypedData_v4(bufferPrivateKey, {
data: payload,
});
const apiPayload = {
signature,
sportXeventId,
salt,
maker: wallet.address,
timestamp,
};
const result = await fetch("https://api.sx.bet/orders/cancel/v2", {
method: "POST",
body: JSON.stringify(apiPayload),
headers: { "Content-Type": "application/json" },
});
The above command returns json structured like this
{
"status": "success",
"data": {
"cancelledCount": 1
}
}
This endpoint cancels existing orders on the exchange for a particular event that you placed as a market maker.
HTTP Request
POST https://api.sx.bet/orders/cancel/event
Request payload parameters
Name | Required | Type | Description |
---|---|---|---|
sportXeventId | true | string | The event for which orders should be cancelled |
signature | true | string | The EIP712 signature on the cancel order payload. See the EIP712 signing section for more details on how to compute this signature. |
salt | required | string | A random 32 bytes hex string to protect against replay |
maker | required | true | The account from which you are cancelling orders |
timestamp | required | true | The current timestamp in UNIX seconds to protect against replay |
Response format
Name | Type | Description |
---|---|---|
status | string | success or failure if the request succeeded or not |
data | object | The response data |
> cancelledCount | string[] | How many orders were cancelled, of the orders passed |
Cancel all orders
curl --location --request POST 'https://api.sx.bet/orders/cancel/all' \
--header 'Content-Type: application/json' \
--data-raw '{
"signature": "0x1763cb98a069657cb778fdc295eac48741b957bfe58e54f7f9ad03c6c1ca3d053d9ca2e6957af794991217752b69cb9aa4ac9330395c92e24c8c25ec19220e5a1b",
"salt": "0x6845028402f518a1c90770554a71017cd434ae9f2c09aa56c9560835c1929650",
"maker": "0xe087299AE9Acd0133d6D1544A97Bb0EEe24a2671",
"timestamp": 1643898624
}'
import ethSigUtil from "eth-sig-util";
import { randomBytes } from "@ethersproject/random";
import { Wallet } from "@ethersproject/wallet";
// Example is shown using a private key
const privateKey = process.env.PRIVATE_KEY;
const bufferPrivateKey = Buffer.from(privateKey.substring(2), "hex");
const salt = `0x${Buffer.from(randomBytes(32)).toString("hex")}`;
const timestamp = Math.floor(new Date().getTime() / 1000);
const wallet = new Wallet(privateKey);
function getCancelAllOrdersEIP712Payload(salt, timestamp, chainId) {
const payload = {
types: {
EIP712Domain: [
{ name: "name", type: "string" },
{ name: "version", type: "string" },
{ name: "chainId", type: "uint256" },
{ name: "salt", type: "bytes32" },
],
Details: [{ name: "timestamp", type: "uint256" }],
},
primaryType: "Details",
domain: {
name: "CancelAllOrdersSportX",
version: "1.0",
chainId,
salt,
},
message: { timestamp },
};
return payload;
}
const payload = getCancelOrderEventsEIP712Payload(salt, timestamp, 416);
const signature = ethSigUtil.signTypedData_v4(bufferPrivateKey, {
data: payload,
});
const apiPayload = {
signature,
sportXeventId,
salt,
maker: wallet.address,
timestamp,
};
const result = await fetch("https://api.sx.bet/orders/cancel/all", {
method: "POST",
body: JSON.stringify(apiPayload),
headers: { "Content-Type": "application/json" },
});
The above command returns json structured like this
{
"status": "success",
"data": {
"cancelledCount": 10
}
}
This endpoint cancels ALL existing orders on the exchange that you placed as a market maker.
HTTP Request
POST https://api.sx.bet/orders/cancel/all
Request payload parameters
Name | Required | Type | Description |
---|---|---|---|
signature | true | string | The EIP712 signature on the cancel order payload. See the EIP712 signing section for more details on how to compute this signature. |
salt | required | string | A random 32 bytes hex string to protect against replay |
maker | required | true | The account from which you are cancelling orders |
timestamp | required | true | The current timestamp in UNIX seconds to protect against replay. |
Response format
Name | Type | Description |
---|---|---|
status | string | success or failure if the request succeeded or not |
data | object | The response data |
> cancelledCount | string[] | How many orders were cancelled, of the orders passed |
Filling orders
curl --location --request POST 'https://api.sx.bet/orders/fill' \
--header 'Content-Type: application/json' \
--data-raw '{"orderHashes":["0x863a2288a640e1bb722da2ee7a6f323ea28caee8ac681d320534d0e7f2e849de","0x001f676d68baf85310145f85bc5aeba64108b234607b4fae25c704069ca8464a"],"takerAmounts":["10000000000000000000","10000000000000000000"],"taker":"0xa3bBFaB3645B2Dd4296cADc451d74574CD47Ba1a","takerSig":"0x09d2603a8c8646221d6972b04a5cdd8b13d6326a267329825567a25a5e63606b07b97c84640bfb3ee4a5053083ce178d9e0c9cbdf1b1dfd519fda0594fae30dc1c","fillSalt":"69231297238279245345865414293427982207908612843136003245427437324972455931243","action":"N/A","market":"N/A","betting":"N/A","stake":"N/A","odds":"N/A","returning":"N/A"}'
import ethSigUtil from "eth-sig-util";
import {
BigNumber,
constants,
Contract,
providers,
utils,
Wallet,
} from "ethers";
import { randomBytes } from "ethers/lib/utils";
async function fillOrder() {
const privateKey = process.env.PRIVATE_KEY;
const takerAddress = process.env.TAKER_ADDRESS;
const tokenAddress = process.env.TOKEN_ADDRESS;
const tokenTransferProxyAddress = process.env.TOKEN_TRANSFER_PROXY_ADDRESS; // get from https://api.sx.bet/metadata
const EIP712FillHasherAddress = process.env.EIP712_FILL_HASHER_ADDRESS; // get from https://api.sx.bet/metadata
const bufferPrivateKey = Buffer.from(privateKey!.substring(2), "hex");
const wallet = new Wallet(privateKey).connect(
new providers.JsonRpcProvider(process.env.PROVIDER_URL)
);
const takerAmounts = ["10000000000000000000", "10000000000000000000"];
const fillSalt = BigNumber.from(randomBytes(32)).toString();
const approvalAmount = constants.MaxUint256;
const tokenContract = new Contract(
tokenAddress,
[
{
constant: false,
inputs: [
{ internalType: "address", name: "usr", type: "address" },
{ internalType: "uint256", name: "wad", type: "uint256" },
],
name: "approve",
outputs: [{ internalType: "bool", name: "", type: "bool" }],
payable: false,
stateMutability: "nonpayable",
type: "function",
},
{
inputs: [
{
internalType: "address",
name: "user",
type: "address",
},
],
name: "getNonce",
outputs: [
{
internalType: "uint256",
name: "nonce",
type: "uint256",
},
],
stateMutability: "view",
type: "function",
},
{
inputs: [
{
internalType: "address",
name: "owner",
type: "address",
},
],
name: "nonces",
outputs: [
{
internalType: "uint256",
name: "",
type: "uint256",
},
],
stateMutability: "view",
type: "function",
constant: true,
},
],
wallet
);
let nonce = await tokenContract.getNonce(takerAddress);
const tokenName: string = await tokenContract.name();
const abiEncodedFunctionSig = tokenContract.interface.encodeFunctionData(
"approve",
[tokenTransferProxyAddress, approvalAmount]
);
const ordersToFill = [
{
fillAmount: "0",
orderHash:
"0x863a2288a640e1bb722da2ee7a6f323ea28caee8ac681d320534d0e7f2e849de",
marketHash:
"0x0eeace4a9bbf6235bc59695258a419ed3a05a2c8e3b6a58fb71a0d9e6b031c2b",
maker: "0x63a4491dC73245E181c47BAe0ae9d6627E56dE55",
totalBetSize: "120000000000000000000",
percentageOdds: "68860772772306080000",
expiry: 2209006800,
apiExpiry: 1631233201,
baseToken: "0x8f3Cf7ad23Cd3CaDbD9735AFf958023239c6A063",
executor: "0x3E91041b9e60C7275f8296b8B0a97141e6442d49",
salt:
"64542697517744547417546237340530363903382582642857122744382059379852833736063",
isMakerBettingOutcomeOne: true,
signature:
"0xa58255f9f7bb8a2698d66f9931a3d4ebe2e5605299a57d78b7c048f26585fdb5144b16bb930ad564f2e2819f66c8c20c2c076d8951728d8242481d58af221fc51b",
createdAt: "2021-06-24T21:44:51.355Z",
},
{
fillAmount: "0",
orderHash:
"0x001f676d68baf85310145f85bc5aeba64108b234607b4fae25c704069ca8464a",
marketHash:
"0x0eeace4a9bbf6235bc59695258a419ed3a05a2c8e3b6a58fb71a0d9e6b031c2b",
maker: "0x63a4491dC73245E181c47BAe0ae9d6627E56dE55",
totalBetSize: "120000000000000000000",
percentageOdds: "27138321995464855000",
expiry: 2209006800,
apiExpiry: 1631233201,
baseToken: "0x8f3Cf7ad23Cd3CaDbD9735AFf958023239c6A063",
executor: "0x3E91041b9e60C7275f8296b8B0a97141e6442d49",
salt:
"33394198022904122521460365691253625674733505262618329029186356280403872887283",
isMakerBettingOutcomeOne: false,
signature:
"0xb52be3279a092823bb46b3bd9a397bef2a06eda95a519718992ea88d73a4375874728cb7c5f6527b67195e44f3f0f0d29956dc05e18e775547d8f2faf3d4bd001c",
createdAt: "2021-06-24T21:44:51.374Z",
},
];
const signingPayload = {
types: {
EIP712Domain: [
{ name: "name", type: "string" },
{ name: "version", type: "string" },
{ name: "chainId", type: "uint256" },
{ name: "verifyingContract", type: "address" },
],
Details: [
{ name: "action", type: "string" },
{ name: "market", type: "string" },
{ name: "betting", type: "string" },
{ name: "stake", type: "string" },
{ name: "odds", type: "string" },
{ name: "returning", type: "string" },
{ name: "fills", type: "FillObject" },
],
FillObject: [
{ name: "orders", type: "Order[]" },
{ name: "makerSigs", type: "bytes[]" },
{ name: "takerAmounts", type: "uint256[]" },
{ name: "fillSalt", type: "uint256" },
{ name: "beneficiary", type: "address" }
],
Order: [
{ name: "marketHash", type: "bytes32" },
{ name: "baseToken", type: "address" },
{ name: "totalBetSize", type: "uint256" },
{ name: "percentageOdds", type: "uint256" },
{ name: "expiry", type: "uint256" },
{ name: "salt", type: "uint256" },
{ name: "maker", type: "address" },
{ name: "executor", type: "address" },
{ name: "isMakerBettingOutcomeOne", type: "bool" },
],
},
primaryType: "Details",
domain: {
name: "SX Bet",
version: process.env.DOMAIN_VERSION, // get from https://api.sx.bet/metadata
chainId: 416 // get from https://api.sx.bet/metadata,
verifyingContract: EIP712FillHasherAddress,
},
message: {
action: "N/A",
market: "N/A",
betting: "N/A",
stake: "N/A",
odds: "N/A",
returning: "N/A",
fills: {
makerSigs: ordersToFill.map((order) => order.signature),
orders: ordersToFill.map((order) => ({
marketHash: order.marketHash,
baseToken: order.baseToken,
totalBetSize: order.totalBetSize.toString(),
percentageOdds: order.percentageOdds.toString(),
expiry: order.expiry.toString(),
salt: order.salt.toString(),
maker: order.maker,
executor: order.executor,
isMakerBettingOutcomeOne: order.isMakerBettingOutcomeOne,
})),
takerAmounts,
fillSalt,
beneficiary: constants.AddressZero
},
},
};
const approveProxySigningPayload = {
types: {
EIP712Domain: [
{ name: "name", type: "string" },
{ name: "version", type: "string" },
{ name: "verifyingContract", type: "address" },
{ name: "salt", type: "bytes32" },
],
MetaTransaction: [
{ name: "nonce", type: "uint256" },
{ name: "from", type: "address" },
{ name: "functionSignature", type: "bytes" },
],
},
domain: {
name: tokenName,
version: "1",
salt: utils.hexZeroPad(utils.hexlify(416), 32), https://api.sx.bet/metadata
verifyingContract: tokenAddress,
},
message: {
nonce,
from: takerAddress,
functionSignature: abiEncodedFunctionSig,
},
primaryType: "MetaTransaction",
};
const approveProxySignature = ethSigUtil.signTypedData_v4(bufferPrivateKey, {
data: approveProxySigningPayload,
});
const signature = ethSigUtil.signTypedData_v4(bufferPrivateKey, {
data: signingPayload,
});
const apiPayload = {
orderHashes: ordersToFill.map((order) => order.orderHash),
takerAmounts,
taker: takerAddress,
takerSig: signature,
fillSalt,
action: "N/A",
market: "N/A",
betting: "N/A",
stake: "N/A",
odds: "N/A",
returning: "N/A",
approveProxyPayload: {
owner: takerAddress,
spender: tokenTransferProxyAddress,
tokenAddress,
amount: approvalAmount.toString(),
signature: approveProxySignature,
},
};
const response = await fetch(`https://api.sx.bet/orders/fill`, {
method: "POST",
body: JSON.stringify(apiPayload),
headers: { "Content-Type": "application/json" },
});
}
The above command returns json structured like this
{
"status": "success",
"data": {
"fillHash": "0x840763ae29b7a6adfa0e315afa47be30cdebd5b793d179dc07dc8fc4f0034965"
}
}
This endpoint fills orders on the exchange. Multiple orders can be filled at once and no gas is paid as this is a meta transaction submitted by the API itself. Therefore you do not require any MATIC in your wallet to fill orders.
Note that pre-game has a built-in betting delay of 5s and in-game betting has a built-in betting delay of 8s. This is added to guard against toxic flow and high spikes in latency from the bookmaker's side. It is effectively protection for the bookmaker. If the odds change within that delay time, the order will be cancelled and an error will be thrown.
To fill orders on sx.bet via the API, make sure you first enable betting by following the steps here
HTTP Request
POST https://api.sx.bet/orders/fill
Request payload parameters
Name | Required | Type | Description |
---|---|---|---|
action | true | string | User facing string for what action the user is taking. Can simply set to "N/A" when using the API |
betting | true | string | User facing string for what bet the user is making. Can simply set to "N/A" when using the API. |
fillSalt | true | string | Random 32 byte string to identify this fill. Must be the same fillSalt used when computing the EIP712 payload |
market | true | string | User facing string for what market the user is betting on. Can simply set to "N/A" when using the API |
odds | true | string | User facing string for the odds the user is receiving. Can simply set to "N/A" when using the API |
orderHashes | true | string[] | Orders being filled. Must be the order hashes of the orders used in computing the EIP712 payload. Must be the same length as takerAmounts |
returning | true | string | User facing string for what the bet wil be returning. Can simply set to "N/A" when using the API. |
taker | true | string | Address of the taker taking the bet |
stake | true | string | User facing string for how much the user is risking. Can simly set to "N/A" when using the API. |
takerAmounts | true | string[] | How much each order is being filled, ordered by index. Must be in the same order as orderHashes , and the same length as orderHashes . It also must be the same and in the same order as the takerAmounts array used when computing the EIP712 payload. |
takerSig | true | string | The EIP712 signature of the taker on the payload. See the example of how to compute this. |
message | true | string | A user-facing message for the eip712 signing. Can be anything. |
signature | true | string | The EIP712 signature on the cancel order payload. See the EIP712 signing section for general information on how to compute this signature. See the example for the specific parameters required. |
approveProxyPayload | false | ApproveSpenderPayload |
Extra object required if you wish to atomically ERC20.approve() prior to the bet. This can be useful from a UX point of view if you don't want the user to have to wait until the approval is mined before the bet can be submitted |
affiliateAddress | false | string | Set the taker to a valid affiliate's address. |
where an ApproveSpenderPayload
looks like
Name | Required | Type | Description |
---|---|---|---|
owner | true | string | Address of the bettor/taker |
spender | true | string | Address of the contract permitted to spend tokens on behalf of the bettor/taker to bet. See the TokenTransferProxy address in the available at https://api.sx.bet/metadata for this address |
tokenAddress | true | string | Address of the token being used to bet. Must be the same as the token referenced in the orders being filled |
amount | true | string | Amount of tokens to approve. Must be greater than or equal to the total tokens bet from the bettors's perspective, i.e., how many tokens are leaving the bettor's wallet |
signature | true | string | The EIP712 signature of the this payload. |
Response format
Name | Type | Description |
---|---|---|
status | string | success or failure if the request succeeded or not |
data | object | The response data |
> fillHash | string | A unique identifier for this fill. |
Unit Conversion
Tokens
Every token in Ethereum has an associated "decimals" value. This effectively specifies how divisible the token is. For example, 100 USDC is actually stored as 100 * 10^18 USDC on Ethereum itself. Here is a table for the tokens supported by SX.bet and their associated decimals value
Token | SX Network Address | Decimals |
---|---|---|
USDC | See https://api.sx.bet/metadata for address |
6 |
WETH | See https://api.sx.bet/metadata for address |
18 |
WSX | See https://api.sx.bet/metadata for address |
18 |
To convert from nominal units (such as 100 USDC) to Ethereum units which are used in the API, you can do the following.
ethereumAmount = nominalAmount * 10^decimals
Similarly, to convert from Ethereum units to nominal units, you can do the following
nominalAmount = ethereumAmount / 10^decimals
where decimals
is specified in the above table.
Odds
Odds are specified in an implied odds format like 8391352143642350000
. To convert to a readable implied odds, divide by 10^20
. 8391352143642350000
for example is 0.0839 or 8.39%
To convert from implied odds to decimal odds, inverse the number. For example, 0.0839 in decimal format is 1/0.0839 = 11.917
.
Bookmaker odds
{
"fillAmount": "0",
"orderHash": "0xb46e5fff6498f061e93c4f5ed501ee72d924180d6aa78cdfd4d188d3383c91d4",
"marketHash": "0x0eeace4a9bbf6235bc59695258a419ed3a05a2c8e3b6a58fb71a0d9e6b031c2b",
"maker": "0x63a4491dC73245E181c47BAe0ae9d6627E56dE55",
"totalBetSize": "10000000000000000000",
"percentageOdds": "70455284072443640000",
"expiry": 2209006800,
"apiExpiry": 1631233201
"baseToken": "0x8f3Cf7ad23Cd3CaDbD9735AFf958023239c6A063",
"executor": "0x3E91041b9e60C7275f8296b8B0a97141e6442d49",
"salt": "69415402816762328320330277846098411244657139277332120954321492419616371539163",
"isMakerBettingOutcomeOne": true,
"signature": "0x2aaea5b7c86166c0fbf2745c66aff794a23d21ac71ee143d08706700adbb59aa4c9b862286cf736acae5a74b10847ced73b628f4396eaab0af13b0c637fe4d021b",
"createdAt": "2021-06-04T17:42:07.257Z"
}
It's important to note how odds are displayed on sx.bet. Recall from the order section that percentageOdds
is from the perspective of the market maker. The odds that are displayed on sx.bet in the order books are what the taker will be receiving. Let's run through an example.
Suppose an order looks like the one on the right.
Here the maker is betting outcome one (isMakerBettingOutcomeOne = true
) and receiving implied odds of 70455284072443640000 / 10^20 = 0.704552841
. Therefore the taker is betting outcome two and receiving implied odds of 1 - 0.704552841 = 0.295447159
. This would be displayed on sx.bet (what the user sees) under the second order book with odds of 29.5% in implied format, or 1 / 0.295447159 = 3.3847
in decimal format.
EIP712 Signing
For certain operations with the API, we require you to sign data using the EIP712 specification. We provide two examples to sign data in the EIP712 fashion in JavaScript if you're using an injected provider (such as MetaMask or another wallet) or a private key.
As of now we have not tested other libraries in other languages that support EIP712.
Private key signing example
See the javascript section
import ethSigUtil from "eth-sig-util";
const privateKey = process.env.PRIVATE_KEY
// Assuming process.env.PRIVATE_KEY is "0x"-prefixed
const bufferPrivateKey = Buffer.from(privateKey.substring(2), "hex");
const payload = {
types: {
EIP712Domain: [
{ name: "name", type: "string" },
{ name: "version", type: "string" },
{ name: "chainId", type: "uint256" }
],
Details: [
{ name: "message", type: "string" },
{ name: "orders", type: "string[]" }
]
},
primaryType: "Details",
domain: {
name: "CancelOrderSportX",
version: "1.0",
chainId: 416
},
message: {
message: "Are you sure you want to cancel these orders"
orders: ["0x550128e997978495eeae503c13e2e30243d747e969c65e1a0b565c609e097506"]
}
};
const signature = ethSigUtil.signTypedData_v4(
bufferPrivateKey,
{ data: payload }
);
Here we use a private key directly which is the most straightforward way to sign data and does not require access to an authenticated node. It's also the fastest.
Injected provider signing example
See the javascript section
import { providers } from "ethers";
const walletAddress = "0xa1e32a027271f7d3d5c629b1c87289ccf9611533"
const provider = new providers.Web3Provider(window.ethereum)
const payload = {
types: {
EIP712Domain: [
{ name: "name", type: "string" },
{ name: "version", type: "string" },
{ name: "chainId", type: "uint256" }
],
Details: [
{ name: "message", type: "string" },
{ name: "orders", type: "string[]" }
]
},
primaryType: "Details",
domain: {
name: "CancelOrderSportX",
version: "1.0",
chainId: 416
},
message: {
message: "Are you sure you want to cancel these orders"
orders: ["0x550128e997978495eeae503c13e2e30243d747e969c65e1a0b565c609e097506"]
}
};
const signature = await provider.send("eth_signTypedData_v4", [walletAddress, JSON.stringify(payload)])
Here we use ethers.js
and show an example with MetaMask but provided the injected provider has a send()
function and supports the eth_signTypedData
JSON-RPC method, it will work. Note that in some wallets, such as metamask, eth_signTypedData_v4
is the most up to date version of eth_signTypedData
.
Fees
Trading
Fees are capped at 4% on the profits of your bet. This fee can be reduced by staking SX. For example, if you bet 100 USDC to win 200 USDC and you win, your profit is 200 - 100 = 100 USDC
and the fees paid is 100 * 0.04 = 4 USDC
. See the fee discount page for details. Your fee is further dependent on whether you are a maker or taker in the transaction.
Websocket API
You can connect to the websocket API and listen for realtime changes in several things such as order books, markets, scores, and line updates.
Initialization
import * as ably from "ably";
import axios from "axios";
async function createTokenRequest() {
const response = await axios.get(
"https://api.sx.bet/user/token",
{
headers: {
"x-api-key": process.env.SX_BET_API_KEY,
}
}
);
return response.data;
}
async function initialize() {
const realtime = new ably.Realtime.Promise({
authCallback: async (tokenParams, callback) => {
try {
const tokenRequest = await createTokenRequest()
// Make a network request to GET /user/token passing in
// `x-api-key: [YOUR_API_KEY]` as a header
callback(null, tokenRequest)
} catch (error) {
callback(error, null)
}
}
});
await ablyClient.connection.once("connected");
}
We use the Ably SDK to allow users to connect to our API. It supports pretty much every major language but all of the examples on this page will be in JavaScript. The API is relatively identical across languages though. See this link for a basic overview of the API in other languages.
All the examples following assume you have a realtime
object in scope following the initialization code to the right.
Market updates
const channel = realtime.channels.get(`markets`);
channel.subscribe((message) => {
console.log(message.data);
});
The above command returns JSON structured like this:
[
{
"gameTime": 1625674200,
"group1": "MLB",
"homeTeamFirst": false,
"leagueId": 171,
"leagueLabel": "MLB",
"line": 7,
"liveEnabled": false,
"marketHash": "0x384c6d8e17c9b5522a17f7bb049ede7d3dd9dd1311232fe854e7f9f4708dfc4c",
"outcomeOneName": "Over 7.0",
"outcomeTwoName": "Under 7.0",
"outcomeVoidName": "NO_GAME_OR_EVEN",
"sportId": 3,
"sportLabel": "Baseball",
"sportXeventId": "L7186379",
"status": "ACTIVE",
"teamOneName": "Tampa Bay Rays",
"teamTwoName": "Cleveland Indians",
"type": 2
}
]
Subscribe to all changes in markets on sx.bet. You will get updates when
- A new market is added
- A market is removed (set to
INACTIVE
) - A market's fields have changed (for example, game time has changed or the market has settled)
Channel name format
markets
Message payload format
See the markets section for the format of the message
Parlay Market requests
const channel = realtime.channels.get(`markets:parlay`);
channel.subscribe((message) => {
console.log(message.data);
});
The above command returns JSON structured like this:
{
"marketHash": "0x38cceead7bda65c18574a34994ebd8af154725d08aa735dcbf26247a7dcc67bd",
"baseToken": "0x8f3Cf7ad23Cd3CaDbD9735AFf958023239c6A063",
"requestSize": "100000000",
"legs": [
{
"marketHash": "0x0d64c52e8781acdada86920a2d1e5acd6f29dcfe285cf9cae367b671dff05f7d",
"bettingOutcomeOne": true,
},
{
"marketHash": "0xe609a49d083cd41214a0db276c1ba323c4a947eefd2e4260386fec7b5d258188",
"bettingOutcomeOne": false,
}
]
}
When a bettor requests a Parlay Market, a message is sent via the markets:parlay
channel. In order to offer orders on Parlay Markets, you will need to subscribe to this channel. The payload will contain the marketHash
that is associated with the Parlay Market.
You can post orders to this market as you would for any other market, using this marketHash
. The payload also contains the token and size that the bettor is requesting. The legs
in the payload contain the underlying legs that make up the parlay market. You can query for market data on each leg's marketHash
to determine current orders for that individual market.
ParlayMarket
payload format
Name | Type | Description |
---|---|---|
marketHash | string | The parlay market associated with this request |
baseToken | string | The token this request is denominated in |
requestSize | number | The size in baseTokens that the bettor is requesting. See the token section of how to convert this into nominal amounts |
legs | ParlayMarketLeg[] | An array of legs that make up the parlay |
ParlayMarketLeg
payload format
Name | Type | Description |
---|---|---|
marketHash | string | The market for an individual leg within the parlay |
bettingOutcomeOne | boolean | The side the bettor is betting for an individual leg within the parlay |
Line changes
const channel = realtime.channels.get(`main_line`);
channel.subscribe((message) => {
console.log(message.data);
});
The above command returns JSON structured like this:
{
"marketHash": "0x38cceead7bda65c18574a34994ebd8af154725d08aa735dcbf26247a7dcc67bd",
"marketType": 3,
"sportXeventId": "L7178624"
}
Subscribe to all line changes. Messages are sent for particular combinations of event IDs and market types. Note that only market types with lines will have updates sent. See the active markets section for details on which types have lines.
Channel name format
main_line
Message payload format
Name | Type | Description |
---|---|---|
marketHash | string | The market which is now the main line for this event ID |
marketType | number | The type of market this update refers to. |
sportXeventId | string | The event ID for this update |
To get the actual line, you'll have to fetch the market using the marketHash
Live score updates
const sportXeventId = "L7178624";
const channel = realtime.channels.get(`live_scores:${sportXeventId}`);
channel.subscribe((message) => {
console.log(message.data);
});
The above command returns JSON structured like this:
{
"teamOneScore": 2,
"teamTwoScore": 1,
"sportXeventId": "L7178624",
"currentPeriod": "4th Set",
"periodTime": "-1",
"sportId": 6,
"leagueId": 1263,
"periods": [
{
"label": "1st Set",
"isFinished": true,
"teamOneScore": "4",
"teamTwoScore": "6"
},
{
"label": "2nd Set",
"isFinished": true,
"teamOneScore": "6",
"teamTwoScore": "3"
},
{
"label": "3rd Set",
"isFinished": true,
"teamOneScore": "7",
"teamTwoScore": "5"
},
{
"label": "4th Set",
"isFinished": false,
"teamOneScore": "1",
"teamTwoScore": "2"
},
{
"label": "Game",
"isFinished": false,
"teamOneScore": "0",
"teamTwoScore": "0"
}
],
"extra": "[{\"Name\":\"Turn\",\"Value\":\"1\"},{\"Name\":\"DoubleFaults\",\"Value\":\"{\\\"3/6\\\":\\\"0,0,0,0,0,0\\\",\\\"3/7\\\":\\\"0,2,0,0,0,0\\\",\\\"3/4\\\":\\\"0,0,0,0,0\\\",\\\"3/5\\\":\\\"0,0,0,0\\\",\\\"1/4\\\":\\\"0,0,0,0\\\",\\\"3/2\\\":\\\"0,0,0,0,0,0\\\",\\\"1/5\\\":\\\"0,0,0,0,0\\\",\\\"3/3\\\":\\\"0,0,0,0,0\\\",\\\"1/6\\\":\\\"2,2,0,0,0,0,2,0,0,0\\\",\\\"1/7\\\":\\\"0,0,0,0,0,0\\\",\\\"3/12\\\":\\\"0,0,0,0,0,0,0\\\",\\\"3/1\\\":\\\"0,0,0,0,0\\\",\\\"1/1\\\":\\\"0,0,0,0,0\\\",\\\"1/2\\\":\\\"0,0,0,0,0\\\",\\\"3/11\\\":\\\"0,0,0,0,0,0,2,0\\\",\\\"1/3\\\":\\\"0,0,0,0,0\\\",\\\"3/8\\\":\\\"0,0,1,0,0,0,0,0,0,0\\\",\\\"1/8\\\":\\\"0,0,0,0,0\\\",\\\"3/9\\\":\\\"0,0,0,0,0\\\",\\\"1/9\\\":\\\"0,0,0,0,0,0,0,0\\\",\\\"2/7\\\":\\\"0,0,0,0,0,0\\\",\\\"2/6\\\":\\\"0,0,0,0,0\\\",\\\"2/5\\\":\\\"0,0,0,0,0,0,0,0,0,0\\\",\\\"2/4\\\":\\\"0,0,0,0,0,0,0,0\\\",\\\"2/3\\\":\\\"0,0,0,0\\\",\\\"2/2\\\":\\\"0,0,0,0,0,0,0,0\\\",\\\"2/1\\\":\\\"0,0,0,0,0\\\",\\\"4/1\\\":\\\"0,0,2,0,2,0\\\",\\\"4/3\\\":\\\"0,0,0,0,0\\\",\\\"4/2\\\":\\\"0,0,0,0,0,0,0,0\\\",\\\"3/10\\\":\\\"0,0,0,0,0,0\\\",\\\"2/9\\\":\\\"0,0,0,0,0\\\",\\\"1/10\\\":\\\"0,0,0,0\\\",\\\"2/8\\\":\\\"0,0,0,0\\\"}\"},{\"Name\":\"CourtSurfaceType\",\"Value\":\"Grass\"},{\"Name\":\"Aces\",\"Value\":\"{\\\"3/6\\\":\\\"0,0,0,0,0,0\\\",\\\"3/7\\\":\\\"0,0,0,0,0,0\\\",\\\"3/4\\\":\\\"0,0,0,0,0\\\",\\\"3/5\\\":\\\"0,2,0,0\\\",\\\"1/4\\\":\\\"2,2,0,0\\\",\\\"3/2\\\":\\\"0,0,0,0,0,0\\\",\\\"1/5\\\":\\\"0,0,0,0,0\\\",\\\"3/3\\\":\\\"0,0,0,0,2\\\",\\\"1/6\\\":\\\"0,0,0,0,0,0,0,0,0,0\\\",\\\"1/7\\\":\\\"0,0,0,0,0,0\\\",\\\"3/12\\\":\\\"1,0,0,0,0,0,0\\\",\\\"3/1\\\":\\\"0,0,0,2,0\\\",\\\"1/1\\\":\\\"0,0,0,0,0\\\",\\\"1/2\\\":\\\"0,2,0,0,0\\\",\\\"3/11\\\":\\\"0,0,0,0,0,0,0,0\\\",\\\"1/3\\\":\\\"0,0,0,0,0\\\",\\\"3/8\\\":\\\"0,0,0,0,0,0,0,0,0,0\\\",\\\"1/8\\\":\\\"0,0,0,0,0\\\",\\\"3/9\\\":\\\"0,0,0,0,0\\\",\\\"1/9\\\":\\\"0,0,0,0,0,0,0,0\\\",\\\"2/7\\\":\\\"0,0,0,0,0,0\\\",\\\"2/6\\\":\\\"0,0,0,0,0\\\",\\\"2/5\\\":\\\"0,0,0,0,0,0,0,0,0,0\\\",\\\"2/4\\\":\\\"0,0,0,0,0,0,0,0\\\",\\\"2/3\\\":\\\"0,0,0,0\\\",\\\"2/2\\\":\\\"0,0,0,0,0,0,0,0\\\",\\\"2/1\\\":\\\"0,0,0,0,0\\\",\\\"4/1\\\":\\\"0,0,0,0,0,0\\\",\\\"4/3\\\":\\\"0,0,0,0,0\\\",\\\"4/2\\\":\\\"2,0,0,0,0,0,0,0\\\",\\\"3/10\\\":\\\"0,0,0,0,0,0\\\",\\\"2/9\\\":\\\"0,0,0,0,0\\\",\\\"1/10\\\":\\\"0,0,2,0\\\",\\\"2/8\\\":\\\"2,0,0,0\\\"}\"}]"
}
Subscribe to live score changes for a particular event.
Channel name format
live_scores:{sportXeventId}
Name | Type | Description |
---|---|---|
sportXeventId | string | The event ID you wish to subscribe to |
Message payload format
Name | Type | Description |
---|---|---|
teamOneScore | number | The current score for team one. Referring to teamOneName in the Market object itself |
teamTwoScore | number | The current score for team two. Referring to teamTwoName in the Market object itself |
sportXeventId | string | The event ID for this update |
currentPeriod | string | An identifier for the current period |
periodTime | string | The current time for the period. "-1" if not applicable (for example, in tennis) |
sportId | number | The sport ID for this market |
leagueId | number | The league ID for this market |
periods | Period |
Individual period information |
extra | string | JSON encoded extra data for this live score update |
where a Period
object looks like
Name | Type | Description |
---|---|---|
label | string | The period name |
isFinished | boolean | true if the period is over |
teamOneScore | string | The score of team one. Referring to teamOneName in the Market object itself |
teamTwoScore | string | The score of team two. Referring to teamTwoName in the Market object itself |
To get the actual line, you'll have to fetch the market using the marketHash
Trade updates
const channel = realtime.channels.get(`recent_trades`);
channel.subscribe((message) => {
console.log(message.data);
});
The above command returns JSON structured like this
{
"baseToken": "0x8f3Cf7ad23Cd3CaDbD9735AFf958023239c6A063",
"bettor": "0x814d79A9940CbC5Af4C19cAa118EC065a77CD31f",
"stake": "9999999999999999999",
"odds": "48442571331686290000",
"orderHash": "0xf7321de419b8887eafe5756d25db37ed2796dfc2495a49e286f13c8533fddb67",
"marketHash": "0x32d6c7d300dc44c795e2bdb0c735d9ad74fd2bbece890129d4a5ea0ec6b566f1",
"maker": false,
"betTime": 1625668455,
"settled": false,
"bettingOutcomeOne": true,
"fillHash": "0x027f3237d9dc9dfa6068b60d852c3e9727768683a8c43b2e1a436029f0de924e",
"status": "SUCCESS",
"tradeStatus": "SUCCESS"
}
Subscribe to all trade updates on the exchange. You will receive updates when a trade is settled or a new trade is placed.
Channel name format
recent_trades
Message payload format
See the trades section for the format of the message
Active order updates
const user = "0x082605F78dD83A8423113ecbEB794Fb3FFE470a2";
const token = process.env.USDC_TOKEN_ADDRESS; // get from https://api.sx.bet/metadata
const channel = realtime.channels.get(`active_orders:${token}:${user}`);
channel.subscribe((message) => {
console.log(message.data);
});
The above command returns JSON structured like this
[
[
"0x7bd766486f589f3e272d48294d8881fe68aae7704f7b2ef0a50bf6128be44271",
"0xde53cf70e510eb5aa63a4bd6582579769980e0c1749b64caa7e6773c0c308188",
"INACTIVE",
"1000000000",
"1000000000"
"20306024864520233000",
2209006800,
1625691600,
"1271418014917937117393617219009886912225128221921196717331617268846160092273",
false,
"0xbf099ab02255d5e2a9e063dc43a7afe96e65f5e8fc2ed3d2ba60b0a3fcadb3441bf32271293e85b7a795c9d86a2304035a0da3285113e746547e236bc58885e01c",
"6982204685293715457"
]
]
Subscribe to changes in a particular user's orders. You will receive updates when orders are filled, cancelled, or posted. Note that for performance reasons, updates are delayed by at most 100ms.
Channel name format
active_orders:{token}:{user}
Name | Type | Description |
---|---|---|
token | string | Restrict updates to only orders denominated in this token |
user | string | The user to subscribe to |
Message payload format
The order is packed into an array and the fields are sent in the below order, with the 0th index as the first row. Note that these are the same fields as mentioned in the the orders section, with an additional status
and updateTime
field.
Name | Type | Description |
---|---|---|
orderHash | string | A unique identifier for this order |
marketHash | string | The market for this order |
status | string | "ACTIVE" if this order is still valid, "INACTIVE" otherwise |
fillAmount | string | How much this order has been filled in Ethereum units up to a max of totalBetSize . See the token section of how to convert this into nominal amounts |
totalBetSize | string | The total size of this order in Ethereum units. See the the token section section for how to convert this into nominal amounts. |
percentageOdds | string | The odds that the maker receives in the sportx protocol format. To convert to an implied odds divide by 10^20. To convert to the odds that the taker would receive if this order would be filled in implied format, use the formula takerOdds=1-percentageOdds/10^20 . See the unit conversion section for more details. |
expiry | number | Depcreated field: the time in unix seconds after which this order is no longer valid. Always 2209006800 |
apiExpiry | number | The time in unix seconds after which this order is no longer valid |
salt | string | A random number to differentiate identical orders |
isMakerBettingOutcomeOne | boolean | true if the maker is betting outcome one (and hence taker is betting outcome two if filled) |
signature | string | Signature of the maker on this order |
updateTime | string | Server-side clock time for the last modification of this order. |
Note that the messages are sent in batches in an array. If you receive two updates for the same orderHash
within an update, you can order them by updateTime
after converting the updateTime
to a BigInt or BigNumber.
Order book updates
const marketHash =
"0x04b9af76dfb92e71500975db77b1de0bb32a0b2413f1b3facbb25278987519a7";
const token = "0xa25dA0331Cd053FD17C47c8c34BCCBAaF516C438";
const channel = realtime.channels.get(`order_book:${token}:${marketHash}`);
channel.subscribe((message) => {
console.log(message.data);
});
The above command returns JSON structured like this
[
[
"0x7bd766486f589f3e272d48294d8881fe68aae7704f7b2ef0a50bf6128be44271",
"INACTIVE",
"1000000000",
"0x9883D5e7dC023A441A01Ef95aF406C69926a0AB6",
"1000000000",
"20306024864520233000",
2209006800,
1625691600,
"1271418014917937117393617219009886912225128221921196717331617268846160092273",
false,
"0xbf099ab02255d5e2a9e063dc43a7afe96e65f5e8fc2ed3d2ba60b0a3fcadb3441bf32271293e85b7a795c9d86a2304035a0da3285113e746547e236bc58885e01c",
"6982204685293715457"
]
]
Subscribe to changes in a particular order book. You will receive updates when orders are filled, cancelled, or posted. Note that for performance reasons, updates are delayed by at most 100ms. Updates are packed into arrays to reduce total bandwidth.
Channel name format
order_book:{token}:{marketHash}
Name | Type | Description |
---|---|---|
token | string | Restrict updates to only orders denominated in this token |
marketHash | string | The market to subscribe to |
Message payload format
The order is packed into an array and the fields are sent in the below order, with the 0th index as the first row. Note that these are the same fields as mentioned in the the orders section, with an additional status
and updateTime
field.
Name | Type | Description |
---|---|---|
orderHash | string | A unique identifier for this order |
status | string | "ACTIVE" if this order is still valid, "INACTIVE" otherwise |
fillAmount | string | How much this order has been filled in Ethereum units up to a max of totalBetSize . See the token section of how to convert this into nominal amounts |
maker | string | The market maker for this order |
totalBetSize | string | The total size of this order in Ethereum units. See the the token section section for how to convert this into nominal amounts. |
percentageOdds | string | The odds that the maker receives in the sportx protocol format. To convert to an implied odds divide by 10^20. To convert to the odds that the taker would receive if this order would be filled in implied format, use the formula takerOdds=1-percentageOdds/10^20 . See the unit conversion section for more details. |
expiry | number | Depcreated field: the time in unix seconds after which this order is no longer valid. Always 2209006800 |
apiExpiry | number | The time in unix seconds after which this order is no longer valid |
salt | string | A random number to differentiate identical orders |
isMakerBettingOutcomeOne | boolean | true if the maker is betting outcome one (and hence taker is betting outcome two if filled) |
signature | string | Signature of the maker on this order |
updateTime | string | Server-side clock time for the last modification of this order. |
Note that the messages are sent in batches in an array. If you receive two updates for the same orderHash
within an update, you can order them by updateTime
after converting the updateTime
to a BigInt or BigNumber.