Skip to main content

Documentation Index

Fetch the complete documentation index at: https://polynode.mintlify.app/llms.txt

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

The core polynode event. A Polymarket settlement detected before or after on-chain confirmation. Pending settlements arrive 3–5 seconds before on-chain confirmation (1–2 Polygon blocks). This is the data that makes polynode unique.
V2 compatible. Settlement events work identically for both V1 and V2 Polymarket exchanges. V2 settlements are detected automatically — no subscription changes needed. The event format, trade fields, and enrichment are the same. V2 settlement events may include condition_id directly from the V2 matchOrders calldata. See the V2 Migration Guide.
{
  "data": {
    "block_number": null,
    "condition_id": "0xb564e5e20a0d3ab9b07d24ff25b19001d9d6b2d1d3121d4b9a2a0691713643cb",
    "detected_at": 1774515004952,
    "event_slug": "will-mara-corina-machado-enter-venezuela-by-january-31",
    "event_title": "María Corina Machado enters Venezuela by...?",
    "event_type": "settlement",
    "market_image": "https://polymarket-upload.s3.us-east-2.amazonaws.com/will-mara-corina-machado-enter-venezuela-by-january-31-2JimdK2AkPpQ.jpg",
    "market_slug": "will-mara-corina-machado-enter-venezuela-by-april-30",
    "market_title": "Will María Corina Machado enter Venezuela by April 30?",
    "neg_risk": false,
    "outcome": "No",
    "outcomes": ["Yes", "No"],
    "status": "pending",
    "taker_base_fee": null,
    "taker_price": 0.85,
    "taker_side": "BUY",
    "taker_size": 12.1647,
    "taker_token": "105661833938076784158167307763414073000325182187633954283290589114908891807534",
    "taker_wallet": "0x5b78254f3e639788fed14221bf8a8bd15c2fa88a",
    "tick_size": 0.01,
    "token_ids": [
      "52054255865962303053567273154012826122522952121382457828722214406382382900565",
      "105661833938076784158167307763414073000325182187633954283290589114908891807534"
    ],
    "tokens": {
      "105661833938076784158167307763414073000325182187633954283290589114908891807534": "No",
      "52054255865962303053567273154012826122522952121382457828722214406382382900565": "Yes"
    },
    "trades": [
      {
        "maker": "0x0bfb8009df6c46c1fdd79b65896cf224dc4526a7",
        "maker_amount": "96000000",
        "order_hash": "0x2ca8ef3e7a72fac98a54fba5d92cdbf352633e3f7d929252f3204c35ca65ae80",
        "outcome": "Yes",
        "price": 0.16,
        "side": "BUY",
        "signer": "0x9b4371d8fe1fad11c0a2209ebbc4ca97a040b3f2",
        "size": 600.0,
        "taker": "0x5b78254f3e639788fed14221bf8a8bd15c2fa88a",
        "taker_amount": "600000000",
        "token_id": "52054255865962303053567273154012826122522952121382457828722214406382382900565"
      },
      {
        "maker": "0x5b78254f3e639788fed14221bf8a8bd15c2fa88a",
        "maker_amount": "10340000",
        "order_hash": "0x42961ee60f3ad3bea6df8fb1f98abcf090a05cd03b70d697ab899ad4a960059d",
        "outcome": "No",
        "price": 0.85,
        "side": "BUY",
        "signer": "0x28167f8e9da7739f338056e52cea318892cad166",
        "size": 12.1647,
        "taker": "0x5b78254f3e639788fed14221bf8a8bd15c2fa88a",
        "taker_amount": "12164700",
        "token_id": "105661833938076784158167307763414073000325182187633954283290589114908891807534"
      }
    ],
    "tx_hash": "0xa5afa27aa8a7c1f75e24202f4a92d266b0ca965a306e3846574d0c1004a41b63"
  },
  "timestamp": 1774515004952,
  "type": "settlement"
}
Every WebSocket event is wrapped with three top-level fields: type, timestamp, and data. The data object also contains an event_type field that always matches the top-level type — use whichever is convenient for your parsing logic.

Fields

type
string
required
Always "settlement".
timestamp
number
required
Unix milliseconds. Use this as the canonical event time.
data
object
required

Ordering guarantees

Confirmed settlements are delivered in strict block order. All confirmed events from block N arrive before any from block N+1. Within a block, events are ordered by log index. Polygon uses Bor consensus with single-validator sprints, making chain reorganizations effectively nonexistent. Pending settlements are best-effort ordered by detection time. Because they are extracted from the mempool before block inclusion, there is no guaranteed global sequence. Two pending events detected within the same second may arrive in either order. Do not assume pending ordering reflects eventual on-chain ordering. Pending-to-confirmed pairing: every pending settlement will be followed by a corresponding status_update event when the same tx_hash confirms on-chain. You will never receive a confirmed update without a prior pending event for the same transaction. A note on sender and nonce: Polymarket trades are submitted by Polymarket’s relayer EOAs, not by the traders themselves. Users sign EIP-712 orders off-chain, and the relayer submits the on-chain transaction. The transaction sender and nonce reflect the relayer’s state, not the trader’s intent or ordering. For this reason, sender address and nonce are not included in settlement events — they would be misleading to build on.

The full lifecycle

For every Polymarket settlement, polynode emits up to three events on the WebSocket stream as the transaction moves from mempool to confirmed block:
EventWhen it firesSourceWhat it contains
settlement (pending)3–5s before block confirmationMempool calldataAll fills in data.trades[], decoded from the matchOrders calldata. Per-maker prices are estimated from aggregate amounts.
status_updateAt block confirmationReceipt logsConfirmation metadata (block, latency) plus data.confirmed_fills[] — exact per-fill data from the on-chain OrderFilled logs.
settlement (confirmed) (legacy)At block confirmationCalldata replaySame shape as the pending settlement, with status: "confirmed" and block_number set. Same calldata-derived prices.
The pending settlement gives you speed (2–5 second lead before the block). The status_update with confirmed_fills gives you exactness — those are the canonical prices Polymarket itself reads from the chain.

Pending → confirmed pairing

Every pending settlement is followed by a corresponding status_update event when the same tx_hash confirms on-chain. Match them by tx_hash:
const pending = new Map();

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

  if (msg.type === "settlement" && msg.data.status === "pending") {
    pending.set(msg.data.tx_hash, msg.data);
    return;
  }

  if (msg.type === "status_update") {
    const original = pending.get(msg.data.tx_hash);
    pending.delete(msg.data.tx_hash);

    // The status_update has confirmed_fills with the EXACT prices from
    // the on-chain OrderFilled logs. Compare against the pending estimate
    // if you want, or just use confirmed_fills as the source of truth.
    for (const fill of msg.data.confirmed_fills || []) {
      console.log(`Confirmed: ${fill.side} ${fill.size} @ ${fill.price} (block ${msg.data.block_number})`);
    }
  }
};

When to use which

  • Pending settlement only — you want the 2–5 second lead time and you don’t mind ~0.01–0.04 estimation error on the rare multi-maker fill (e.g. copy trading, frontend price ticks).
  • status_update.confirmed_fills only — you need exact prices and don’t care about pre-confirmation. (e.g. analytics, P&L, bookkeeping).
  • Both together (pending settlement + status_update with confirmed_fills) — you want the speed of pending detection AND the exact prices once confirmed. The recommended pattern for most production integrations.
For the full schema of confirmed_fills[] and a longer treatment of when to use each layer, see the Trade Tracking guide and the Status Update event reference.

Pending vs confirmed (field-level)

FieldPendingConfirmed
status"pending""confirmed"
block_numbernullblock number
detected_atwhen polynode first detected TXsame value
Latency~0ms from detection2–5s after detection (1–2 blocks)
Subscribe with "status": "pending" to get the 2–5 second edge. You’ll receive a separate status_update event when the same transaction confirms on-chain, including latency_ms and confirmed_fills[].

Use cases

  • Copy trading — detect whale trades 1–2 blocks before confirmation and execute your own order
  • Market making — adjust spreads based on incoming settlements
  • Analytics — track real-time volume and price movements
  • Alerts — notify on large trades or specific wallet activity

Tracking a specific wallet’s trades

To find a wallet’s trades, iterate data.trades[] and match by maker. Never match by taker. The maker field on each entry is the wallet that placed the order. The taker field is the counterparty, which is not what you want.
Each entry in data.trades[] is one fill, and the maker field on that entry is the wallet whose trade it is. To track a specific wallet, find the entries in trades[] where maker === your_wallet:
ws.onmessage = (event) => {
  const msg = JSON.parse(event.data);
  if (msg.type !== "settlement") return;

  for (const fill of msg.data.trades) {
    if (fill.maker.toLowerCase() === MY_WALLET.toLowerCase()) {
      console.log("My trade:", fill.side, fill.size, fill.outcome, "@", fill.price);
    }
  }
};
That’s it. One rule: maker === your_wallet.

Why not match by taker

If you match by taker === your_wallet, you get counterparty fills, not the wallet’s actual trade. The taker field on each fill is the opposite party, so matching it gives you the wrong wallet’s perspective — including the opposite token and the complement price. For example, if your wallet bought Up at 0.80, matching by taker returns a fill showing the counterparty buying Down at 0.20. For a deeper explanation of why the on-chain OrderFilled events are structured this way, see the Trade Event reference or the Dome migration guide.