Creating markets
An approved app can list player-prop markets on STX events. You name the event, the player, the stat and the line; STX builds the market, settles it from its stats feed, and returns one outcome for each market you asked for.
Creating markets is granted by STX, per app. It is not part of the standard ISV access.
STX enables your app for market creation:
- adds the
markets.writeapp scope to your client. Only STX can grant it. - sets your client’s IP allow-list to your servers’ egress addresses. Every write scope requires one, and a call from any other address is refused.
Ask your STX contact to enable it on the demo exchange first.
1. Get an app token
Section titled “1. Get an app token”Request an app token with the client_credentials grant, as described in Tokens and security. Send it as Authorization: Bearer <access_token> on every call below, from an allow-listed address.
2. Find the event
Section titled “2. Find the event”GET /api/v1/events lists upcoming events. Take the event’s id. Markets can be created only while the event is scheduled and has not started.
3. Get the players
Section titled “3. Get the players”GET /api/v1/events/{event_id}/players returns each team’s roster and the stats a market can be created on.
{ "event_id": "ee2ee8f9-4326-4e4f-841a-1bc3f598dbd9", "teams": [ { "team": "Utah Jazz", "players": [ { "player_id": "4873138", "name": "Ace Bailey", "jersey": "19", "position": "G" } ] } ], "stats": [ { "stat": "POINTS", "name": "Points" }, { "stat": "REBOUNDS", "name": "Rebounds" } ]}- Rosters are the teams’ current rosters, not game-day actives, and can be up to 15 minutes old.
teamsis empty until a roster is published. Try again later.- Send
player_idback exactly as returned.
4. Create the markets
Section titled “4. Create the markets”POST /api/v1/markets takes the event and up to 25 markets, each described by a stat_detail:
{ "event_id": "ee2ee8f9-4326-4e4f-841a-1bc3f598dbd9", "markets": [ { "stat_detail": { "player_id": "4873138", "stat": "POINTS", "prop_type": "ou", "line": 18.5 } }, { "stat_detail": { "player_id": "4873138", "stat": "REBOUNDS", "prop_type": "ou", "line": 4.5 } } ]}| Field | Value |
|---|---|
player_id |
From the players endpoint. |
stat |
One of the event’s stats. |
prop_type |
ou (over/under). Optional; ou is the only type today. |
line |
A positive half line, such as 18.5. |
The response is 200 with one outcome per market, in request order:
{ "event_id": "ee2ee8f9-4326-4e4f-841a-1bc3f598dbd9", "markets": [ { "status": "created", "market_id": "5766e668-b5ad-4db4-8dd1-e1b7f27d9974", "stat_detail": { "player_id": "4873138", "stat": "POINTS", "prop_type": "ou", "line": 18.5 } }, { "status": "exists", "market_id": "4b2b8139-7968-4a20-b760-f6751af88f06", "stat_detail": { "player_id": "4873138", "stat": "REBOUNDS", "prop_type": "ou", "line": 4.5 } } ]}created: a new market. STX sets its title, rules and price range.exists: an open market for the same player, stat and line is already on the event. Itsmarket_idis returned and nothing new is created, so resending a request is always safe.rejected: not created.reasonis one ofinvalid_market,unsupported_market_type,unsupported_prop_type,unsupported_stat,invalid_line,unknown_player,event_not_openornot_created.
A whole request can also fail:
| Status | Meaning |
|---|---|
400 |
Malformed body, or more than 25 markets. |
401 |
Missing or invalid token. |
403 |
Not an app token with markets.write, or a call from outside the IP allow-list. |
404 |
Unknown event. |
422 |
event_not_open, unsupported_event (no player props for this event) or roster_unavailable. |
5. Quote the markets
Section titled “5. Quote the markets”The new markets behave like any other STX market. Find them with GET /api/v1/markets and the market data channels, and quote them with your market-making account’s API key through the orders endpoints. STX settles them from its stats feed when the game ends.
With the TypeScript SDK
Section titled “With the TypeScript SDK”@stxapp/stx-typescript 0.8.0 and later wrap these calls. createMarkets() returns the same per-market results, and validate: true checks players, stats and lines before sending (it reads the event’s players, so the token also needs events).
import { AppScopes, OAuthClient } from "@stxapp/stx-typescript/oauth";
const oauth = new OAuthClient({ clientId, clientSecret });const stx = oauth.appClient([AppScopes.EVENTS, AppScopes.MARKETS_WRITE]);
const roster = await stx.eventPlayers(eventId);const player = roster.teams[0].players[0];
const results = await stx.createMarkets( eventId, [ { playerId: player.player_id, stat: "POINTS", line: 18.5 }, { playerId: player.player_id, stat: "REBOUNDS", line: 4.5 }, ], { validate: true },);for (const r of results) console.log(r.status, r.status === "rejected" ? r.reason : r.market_id);
