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_ids you submit here are the exact same strike_id values returned by get_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's Authorization key — see §3), and still speaks the pre-CFTC field names (stake, odds, line_ids) rather than quantity/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

EnvironmentURL
Sandboxhttps://api.sandbox.prophetx.dev/parlay/api/v1/affiliate/quotes
Productionhttps://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/json

This 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"]
}
FieldTypeRequiredNotes
stakefloatyesMust be greater than the platform's minimum order stake. Used to size the quote request.
line_idsstring[]yes2 to 12 strike_id values, each one appearing once. Order doesn't matter.

5. Response

{
  "offers": [
    { "max_stake": 50, "odds": 220, "updated_at": 1762300000 }
  ]
}
FieldTypeDescription
max_stakefloatThe largest stake this quote is good for. Don't risk more than this on it.
oddsintAmerican odds for the parlay as a whole (e.g. 220, -140).
updated_atintUnix 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, odds here 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:

  1. Already-settled combination? If this exact set of line_ids was 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.
  2. 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.
  3. No one priced it in time? You get 200 with "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_ids only — not the stake. If you ask for the same combination again inside the cache window with a different stake than 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 returned max_stake rather than assuming the quote was freshly computed for the stake you sent — a very recent updated_at is your best signal that it was.
  • Identical in-flight requests are deduplicated. If the same line_ids combination 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>" }.

StatuserrorCause
400invalid_paramsMalformed 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).
400invalid_legs_combinationThe 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.
401unauthorizedMissing or invalid X-Auth-Affiliate header.
408request_timeoutThe request exceeded the server's overall deadline. Retry.
500internal_server_errorSomething 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.


Did this page help you?