Affiliate Parlay Quotes
Get a live, synchronous price for a parlay slip before you show it to your users.
A separate endpoint from the rest of the Market Data API: instead of reading a standing price off the book, you submit a candidate parlay slip and get back a price that one of ProphetX's market-making partners ("SPs") is willing to risk right now. Use it to show a parlay price alongside the single-leg prices from Market Data Integration.
1. How this fits with Market Data Integration
- The
line_idsyou submit here are the exact samestrike_idvalues returned byget_markets/get_multiple_markets— fetch your legs' prices there first, then quote the combination here. - Different host path, different header, different vocabulary. This endpoint lives under
/parlay/..., not/affiliate/..., is authenticated with its own header (not the Market Data API'sAuthorizationkey — see §3), and still speaks the pre-CFTC field names (stake,odds,line_ids) rather thanquantity/price/strike_id. The values line up with Market Data Integration's CFTC-named fields; only the field names on the wire differ.
2. Environments
| Environment | URL |
|---|---|
| Sandbox | https://api.sandbox.prophetx.dev/parlay/api/v1/affiliate/quotes |
| Production | https://cash.api.prophetx.co/parlay/api/v1/affiliate/quotes |
There's one endpoint — POST to the URL above.
3. Authentication
Send your token in the X-Auth-Affiliate header — raw token, no Bearer prefix:
POST /parlay/api/v1/affiliate/quotes HTTP/1.1
Host: api.sandbox.prophetx.dev
X-Auth-Affiliate: your-affiliate-parlay-token
Content-Type: application/jsonThis is a separate credential from the Market Data API's Authorization key, issued separately even if you already have one for get_markets/get_tournaments. Contact ProphetX's support team at [email protected] to request one.
4. Request
{
"stake": 25,
"line_ids": ["line_1", "line_5", "line_9"]
}| Field | Type | Required | Notes |
|---|---|---|---|
stake | float | yes | Must be greater than the platform's minimum order stake. Used to size the quote request. |
line_ids | string[] | yes | 2 to 12 strike_id values, each one appearing once. Order doesn't matter. |
5. Response
{
"offers": [
{ "max_stake": 50, "odds": 220, "updated_at": 1762300000 }
]
}| Field | Type | Description |
|---|---|---|
max_stake | float | The largest stake this quote is good for. Don't risk more than this on it. |
odds | int | American odds for the parlay as a whole (e.g. 220, -140). |
updated_at | int | Unix timestamp in seconds of when this price was computed. |
offers can come back as an empty array — that's a valid 200, not an error. It means no market maker priced your combination in time (see §6); there's no price to show for that slip right now.
This is the raw price an SP risked, not a patron-facing price. Unlike a bet actually placed through ProphetX,
oddshere has no fee baked in — it's the market maker's own number. If you're showing this price to end users as something they could get by betting through you, account for that difference; don't assume it matches what a patron would see at confirmation on ProphetX itself.
6. How pricing works
There's no order book for a parlay — every quote is a fresh request-for-quote (RFQ) to ProphetX's market-making partners, with polling and caching layered on top so most calls still return quickly:
- Already-settled combination? If this exact set of
line_idswas already fully priced recently, you get that result back immediately, and a refresh is kicked off in the background for next time — you don't pay the full round-trip again for a slip you already asked about. - Otherwise, an RFQ is broadcast over Kafka to ProphetX's market-making partners, and the server polls for responses: roughly 800ms of waiting for a fresh answer, a one-time check of a short-lived quote cache at that point, then up to ~2 seconds total before giving up.
- No one priced it in time? You get
200with"offers": []— not an error. Try again; a market maker may answer this time.
Two caching behaviors worth knowing before you build around them:
- The cache key is the set of
line_idsonly — not the stake. If you ask for the same combination again inside the cache window with a differentstakethan your first request, you can get back a quote that was priced against the earlier stake, not the one you just sent. Compare your intended stake against the returnedmax_stakerather than assuming the quote was freshly computed for the stake you sent — a very recentupdated_atis your best signal that it was. - Identical in-flight requests are deduplicated. If the same
line_idscombination is already being asked about (by you or another caller) within a short dedup window, your request doesn't trigger a second RFQ — it waits on and shares the answer from the one already in flight. - Under heavy load, you may get cache-only results. If the server is at its concurrent-request limit, it skips broadcasting a new RFQ entirely and returns whatever is already in cache (a final result, or the last short-lived quote) rather than queuing or rejecting your request. You can still get
"offers": []in this case if nothing was cached yet.
7. Errors
Errors are JSON: { "error": "<code>", "message": "<text>" }.
| Status | error | Cause |
|---|---|---|
400 | invalid_params | Malformed body, stake ≤ 0, fewer than 2 or more than 12 line_ids, a duplicate or empty line_id, or a leg that fails validation (event/market not open, market not found, invalid outcome or strike, prematch-only market, blacklisted competitors). |
400 | invalid_legs_combination | The legs conflict with each other (e.g. both sides of the same moneyline, or a spread/total that can't both hit). See below for the extra data.conflicts detail. |
401 | unauthorized | Missing or invalid X-Auth-Affiliate header. |
408 | request_timeout | The request exceeded the server's overall deadline. Retry. |
500 | internal_server_error | Something failed server-side — e.g. one or more line_ids couldn't be resolved, or the price request couldn't be dispatched. |
On invalid_legs_combination, the response body carries which legs conflicted and why:
{
"error": "invalid_legs_combination",
"message": "Invalid legs combination",
"data": {
"conflicts": [
{
"eventId": 10079773,
"ruleType": "moneyline_both_sides",
"reason": "both sides of moneyline not allowed",
"legs": [
{ "sportEventId": 10079773, "marketId": 219, "outcomeId": 4, "lineId": "line_1", "line": 0 },
{ "sportEventId": 10079773, "marketId": 219, "outcomeId": 7, "lineId": "line_2", "line": 0 }
]
}
]
}
}ruleType is one of moneyline_both_sides, spread_both_sides, total_both_sides, moneyline_opposite_spread, or duplicate.
8. Example
curl -X POST \
-H "X-Auth-Affiliate: $AFFILIATE_PARLAY_TOKEN" \
-H "Content-Type: application/json" \
-d '{"stake": 25, "line_ids": ["line_1", "line_5", "line_9"]}' \
"https://api.sandbox.prophetx.dev/parlay/api/v1/affiliate/quotes"9. Quick reference card
Sandbox : https://api.sandbox.prophetx.dev/parlay/api/v1/affiliate/quotes
Production : https://cash.api.prophetx.co/parlay/api/v1/affiliate/quotes
Auth header : X-Auth-Affiliate: <token> (separate credential from the Market Data API key)
POST { "stake": <float>, "line_ids": [<2-12 strike_ids>] }
→ { "offers": [ { "max_stake", "odds", "updated_at" } ] } (offers can be empty)
Need access, or run into a combination that should price but doesn't? Reach out to your ProphetX contact.
Updated 2 days ago
