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 trades channel ("orders") streams real-time trade events from Polymarket. By default, you receive confirmed events - trades that have been mined on-chain. Covers both V1 and V2 Polymarket contracts.
Want even earlier signals? Pending trade events deliver trades 3–5 seconds before confirmation by decoding transactions from the Polygon mempool. Use status: "all" to receive both.
V1 vs V2: Every order_filled event has a version field (1 or 2). V2 trades add builder and metadata fields (bytes32 hex, currently zero). fee_refund events are V1-only - V2 emits net fees directly on order_filled (no refund flow). See the V2 Migration Guide.

Event Types

EventDescription
order_filledAn order was filled on-chain
fee_refundA maker fee rebate was processed

order_filled

{
  "type": "event",
  "subscription_id": "sub_2f4b15b33798",
  "data": {
    "status": "confirmed",
    "event_type": "order_filled",
    "token_id": "61192765571543...",
    "token_label": "Up",
    "side": "SELL", 
    "market_slug": "btc-updown-15m-1770244200",
    "condition_id": "0x04f954e4f30f...",
    "shares": 2000000,
    "shares_normalized": 2, 
    "price": 0.04, 
    "tx_hash": "0xf30a29f249...",
    "log_index": "0x21e",
    "title": "Bitcoin Up or Down - February 4, 5:30PM-5:45PM ET",
    "timestamp": 1770244731,
    "order_hash": "0x8bf54f44e5d7...",
    "user": "0xe9cbb1c9b3f7f411...", 
    "taker": "0x98f36c3d6300b905...",
    "role": "maker", 
    "outcome": "Up",
    "outcome_index": 0,
    "complement_token_id": "9876543210...",
    "complement_token_label": "Down",
    "is_neg_risk": false,
    "market_id": "1329542",
    "image": "https://polymarket-upload.s3.us-east-2.amazonaws.com/BTC+fullsize.png",
    "fee": 0.008,
    "version": 2,
    "builder": "0x0000000000000000000000000000000000000000000000000000000000000000",
    "metadata": "0x0000000000000000000000000000000000000000000000000000000000000000"
  }
}

Field Reference


Maker vs Taker emissions

A single matchOrders transaction emits multiple order_filled events - one per maker matched, plus one synthetic taker-aggregate emission summarizing the taker’s overall fill. The role field tells you which one you’re looking at:
roleDescriptionWhen to use
"maker"Per-maker fill. One row per maker order matched in the tx. taker is the real taker user address.Default for almost all use cases - counting fills, attributing volume to maker wallets, copytrading specific makers.
"taker"Synthetic taker-aggregate. One row per matchOrders tx, summing across all makers. taker equals the exchange contract address.Use when you want one row per taker action - e.g. dashboards keyed on the taker, or to avoid double-counting volume across the per-maker rows.
Pending events set role directly from the maker/taker builder; confirmed events derive it from the chain log (taker == exchange contract"taker", else "maker"). Empirically the two agree 100%, so you can dedupe pending → confirmed by tx_hash + order_hash regardless of role.
Don’t sum volume across both roles - the taker-aggregate row covers the same shares as the per-maker rows. Pick one set: filter on role === "maker" for maker-attribution, or role === "taker" for taker-attribution.

fee_refund

fee_refund events are emitted for V1 trades only. V2 has no refund flow - the net fee is emitted directly on order_filled at match time.
Emitted when a maker fee rebate is processed on-chain.
{
  "type": "event",
  "subscription_id": "sub_2f4b15b33798",
  "data": {
    "event_type": "fee_refund",
    "user": "0x47a51f21d742...",
    "token_id": "53031995840519...",
    "condition_id": "0xdfb2f9d3ed88...",
    "market_slug": "btc-updown-15m-1770242400",
    "title": "Bitcoin Up or Down - February 4, 5:00PM-5:15PM ET",
    "order_hash": "0xbda8ab86c90f...", 
    "tx_hash": "0x97b2ae331881...",
    "refund": 9.9904, 
    "fee_charged": 0.0096
  }
}

Understanding Fee Refunds

Polymarket implements a maker fee rebate program. Here’s how it works:
1

Trade executes

An order_filled event arrives with a fee field representing the gross fee charged.
2

Rebate processes

A fee_refund event for the same order_hash arrives 2–5ms later (same block, usually same tx). Rarely up to 50ms.
3

Calculate net fee

The maker’s actual net fee is fee_refund.fee_charged (or equivalently order_filled.fee - fee_refund.refund).
If you don’t need exact fee accounting, you can ignore fee_refund events. The fee field on order_filled is directionally correct for most purposes. Fee refunds are only necessary for precise PnL tracking or cost basis accounting.

Example: Processing Trades

ws.onmessage = (event) => {
  const msg = JSON.parse(event.data);

  if (msg.type === 'event') {
    const data = msg.data;

    if (data.event_type === 'order_filled') {
      const value = data.shares_normalized * data.price; 
      console.log(`${data.side} ${data.outcome} ${data.shares_normalized} @ ${data.price}`);
      console.log(`Market: ${data.title}`);
      console.log(`Value: $${value.toFixed(2)}`);
      console.log(`Maker: ${data.user}`);
      console.log(`Taker: ${data.taker}`);
    }

    if (data.event_type === 'fee_refund') {
      console.log(`Fee refund: $${data.refund} for order ${data.order_hash}`);
      console.log(`Net fee: $${data.fee_charged}`); 
    }
  }
};