Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.predexon.com/llms.txt

Use this file to discover all available pages before exploring further.

The Order Router lets you trade an outcome without picking a venue. Identify the outcome by predexonId — a stable identifier shared across venues — and the router splits the order across every venue that lists it, picking the fee-aware best fill.

When to use it

You want to…Use
Trade a specific venue, with your own market identifiersPlace Order on the accounts path
Trade an outcome, wherever the best price isRouter
Quote expected fill before committingGet Quote
The router only routes to venues you’ve enabled on the account — it never trades on your behalf at a venue you haven’t opted into.

Step 1: Find a predexon_id

Every outcome in the Data API carries a predexon_id field (the Data API uses snake_case; the Trading API accepts and returns it as predexonId). The same outcome on different venues shares the same identifier when our canonical matching graph has linked them.
Python
import requests
HEADERS = {"x-api-key": "YOUR_API_KEY"}

markets = requests.get(
    "https://api.predexon.com/v2/polymarket/markets",
    headers=HEADERS,
    params={"status": "active", "sort": "volume", "limit": 1},
).json()

predexon_id = markets["markets"][0]["outcomes"][0]["predexon_id"]
print(predexon_id)  # e.g. "px-ab12cd34ef56"
Get Quote projects how an order would fill across every venue holding the outcome. Use it to sanity-check expected price and fill likelihood before placing.
Python
BASE = "https://trade.predexon.com"
account_id = "YOUR_ACCOUNT_ID"

quote = requests.get(
    f"{BASE}/api/accounts/{account_id}/router/quote",
    headers=HEADERS,
    params={"predexonId": predexon_id, "side": "buy", "amount": 100},
).json()

print(f"Estimated avg price: {quote['summary']['estimatedAvgPrice']}")
print(f"Fill likelihood: {quote['summary']['fillLikelihood']}")
for v in quote["venues"]:
    print(f"  {v['venue']}: best={v['bestPrice']}, fill={v['estimatedFillSize']} @ {v['estimatedFillPrice']}")

Step 3: Place a router order

Python
order = requests.post(
    f"{BASE}/api/accounts/{account_id}/router/orders",
    headers=HEADERS,
    json={
        "predexonId": predexon_id,
        "side": "buy",
        "type": "market",
        "amount": 100,
    },
).json()

print(f"{order['routerOrderId']} — status: {order['status']}")
print(f"Bought {order['summary']['totalSize']} shares at avg price {order['summary']['avgPrice']}")
for leg in order["fills"]:
    print(f"  {leg['venue']}: {leg['status']} size={leg['size']} price={leg['price']}")
The response carries the order envelope (routerOrderId, predexonId, title, outcome, status, createdAt, etc.), the per-venue fills[], and a summary aggregate. On a market order, summary.avgPrice is the executed weighted-average price. On full failure the response status is 502 with errorCode: "all_venues_failed".

Bridge-enabled buys

By default, the router only uses balances already sitting at each venue. Pass bridgeEnabled: true on a market buy to let the router top up the target venue from your deposit wallet if it’s short. Bridge slippage is bounded internally. The flag is rejected on limit orders and sells.
Python
order = requests.post(
    f"{BASE}/api/accounts/{account_id}/router/orders",
    headers=HEADERS,
    json={
        "predexonId": predexon_id,
        "side": "buy",
        "type": "market",
        "amount": 100,
        "bridgeEnabled": True,
    },
).json()

Step 4: Monitor and cancel

Python
# Poll
order = requests.get(
    f"{BASE}/api/accounts/{account_id}/router/orders/{order['routerOrderId']}",
    headers=HEADERS,
).json()

# Cancel every open leg
requests.delete(
    f"{BASE}/api/accounts/{account_id}/router/orders/{order['routerOrderId']}",
    headers=HEADERS,
)
Get Router Order re-reads any open or partial legs from their venues on each call, so the response always reflects the latest fill state. Cancel returns per-venue results — each leg is cancelled, not_found (already filled or cancelled), already_filled, or failed.

Retries

Pass clientOrderId as a safe-to-retry key. It’s forwarded to each bridge leg and to the venue plane so retries don’t double-bridge or double-place at the venue. The router does not currently dedupe by clientOrderId at the order level, so two simultaneous POSTs with the same key can both proceed — serialize retries client-side.

Auditing routing decisions

Default responses stay slim — just fills and summary. Pass ?explain=true on either POST /router/orders or GET /router/orders/{routerOrderId} to receive a _routing.considered[] array detailing every venue the router considered, with per-venue top-of-book, fee, and (on POST) projected fill.
Python
order = requests.post(
    f"{BASE}/api/accounts/{account_id}/router/orders?explain=true",
    headers=HEADERS,
    json={
        "predexonId": predexon_id,
        "side": "buy",
        "type": "market",
        "amount": 100,
    },
).json()

for v in order["_routing"]["considered"]:
    marker = "✓" if v["droppedReason"] is None else f"dropped: {v['droppedReason']}"
    print(f"  {v['venue']}: top {v['topLevelPrice']} @ {v['feeBpsAtTop']}bps — {marker}")
For limit orders, decisionSnapshot on GET detail carries the full per-venue book state at decision time. ?explain=true on GET produces the same _routing.considered[] shape POST emits, but avgPrice and feeBpsAtTop are null on the GET path (not recoverable from a snapshot alone).

Next Steps

Quote Reference

Full endpoint schema

Place Order Reference

Full endpoint schema

Funding & Withdrawals

Required for bridgeEnabled buys