> ## Documentation Index
> Fetch the complete documentation index at: https://docs.polynode.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Settlement Event

> Polymarket settlement — pending (pre-chain) or confirmed (on-chain).

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.

<Info>
  **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](/guides/v2-migration).
</Info>

```json theme={null}
{
  "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"
}
```

<Note>
  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.
</Note>

## Fields

<ResponseField name="type" type="string" required>
  Always `"settlement"`.
</ResponseField>

<ResponseField name="timestamp" type="number" required>
  Unix milliseconds. Use this as the canonical event time.
</ResponseField>

<ResponseField name="data" type="object" required>
  <Expandable title="data fields">
    <ResponseField name="tx_hash" type="string" required>
      Transaction hash (0x-prefixed).
    </ResponseField>

    <ResponseField name="status" type="string" required>
      `"pending"` — detected pre-chain, not yet confirmed.
      `"confirmed"` — included in a block.
    </ResponseField>

    <ResponseField name="detected_at" type="number" required>
      Unix milliseconds when the transaction was first detected by PolyNode.
    </ResponseField>

    <ResponseField name="block_number" type="number | null" required>
      Block number. `null` when `status` is `"pending"`, set when `"confirmed"`.
    </ResponseField>

    <ResponseField name="taker_wallet" type="string" required>
      Taker (settler) wallet address.
    </ResponseField>

    <ResponseField name="taker_token" type="string" required>
      Polymarket conditional token ID.
    </ResponseField>

    <ResponseField name="taker_side" type="string" required>
      `"BUY"` or `"SELL"` — the taker's side of the trade.
    </ResponseField>

    <ResponseField name="taker_price" type="number" required>
      Fill price (0.0–1.0 for binary markets).
    </ResponseField>

    <ResponseField name="taker_size" type="number" required>
      Fill size in shares.
    </ResponseField>

    <ResponseField name="market_title" type="string">
      Human-readable market question. Enriched from Polymarket metadata.
    </ResponseField>

    <ResponseField name="outcome" type="string">
      Outcome name (e.g. "Yes", "No", "Donald Trump"). Enriched from metadata.
    </ResponseField>

    <ResponseField name="market_slug" type="string">
      Market URL slug (e.g. `"bitcoin-100k-2026"`). Enriched from metadata.
    </ResponseField>

    <ResponseField name="event_title" type="string">
      Parent event title. Enriched from metadata.
    </ResponseField>

    <ResponseField name="market_image" type="string">
      Market image URL. Enriched from metadata.
    </ResponseField>

    <ResponseField name="neg_risk" type="boolean">
      Whether this market uses the negRisk contract type. Enriched from metadata.
    </ResponseField>

    <ResponseField name="tick_size" type="number">
      Minimum price increment (e.g. `0.01` or `0.001`). Enriched from metadata.
    </ResponseField>

    <ResponseField name="taker_base_fee" type="number">
      Taker fee rate in basis points. `200` = 2%, `1000` = 10%, `null` = no fee. Short-term crypto markets (5-minute ETH/BTC) typically have `1000` (10%), while standard markets have `200` (2%). Enriched from Polymarket metadata.
    </ResponseField>

    <ResponseField name="condition_id" type="string">
      Market condition ID (CTF contract identifier). Enriched from metadata.
    </ResponseField>

    <ResponseField name="event_slug" type="string">
      Parent event URL slug (e.g. `"will-mara-corina-machado-enter-venezuela-by-january-31"`). Enriched from metadata.
    </ResponseField>

    <ResponseField name="outcomes" type="string[]">
      Ordered list of outcome names (e.g. `["Yes", "No"]`). Index-aligned with `token_ids`. Enriched from metadata.
    </ResponseField>

    <ResponseField name="token_ids" type="string[]">
      Ordered list of token IDs for this market. Index-aligned with `outcomes`. Enriched from metadata.
    </ResponseField>

    <ResponseField name="tokens" type="object">
      Map of token ID → outcome name for all outcomes in this market (e.g. `{"123...": "Yes", "456...": "No"}`). Enriched from metadata.
    </ResponseField>

    <ResponseField name="trades" type="object[]" required>
      Array of matched maker orders in this settlement.

      <Expandable title="trade fields">
        <ResponseField name="maker" type="string" required>
          Maker wallet address.
        </ResponseField>

        <ResponseField name="signer" type="string" required>
          Order signer address (may differ from maker for smart contract wallets).
        </ResponseField>

        <ResponseField name="taker" type="string" required>
          Taker wallet address.
        </ResponseField>

        <ResponseField name="token_id" type="string" required>
          Conditional token ID.
        </ResponseField>

        <ResponseField name="side" type="string" required>
          `"BUY"` or `"SELL"`.
        </ResponseField>

        <ResponseField name="price" type="number" required>
          Fill price (0.0–1.0).
        </ResponseField>

        <ResponseField name="size" type="number" required>
          Fill size in shares.
        </ResponseField>

        <ResponseField name="maker_amount" type="string" required>
          Raw maker amount (6 decimal USDC or tokens).
        </ResponseField>

        <ResponseField name="taker_amount" type="string" required>
          Raw taker amount (6 decimal USDC or tokens).
        </ResponseField>

        <ResponseField name="outcome" type="string">
          Outcome name for this trade's token (e.g. "Yes", "No", "Up"). Enriched from metadata.
        </ResponseField>

        <ResponseField name="order_hash" type="string">
          EIP-712 order hash for this maker's limit order. Persistent across partial fills. Computed from calldata pre-chain, so this is available on pending settlements before block confirmation.
        </ResponseField>

        <ResponseField name="exchange_version" type="string">
          Exchange contract version: `"v2"` for trades settled on Polymarket's V2 exchange contracts. Absent for V1 trades (current default). Use this to distinguish which exchange system processed the settlement.
        </ResponseField>
      </Expandable>
    </ResponseField>
  </Expandable>
</ResponseField>

## 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:

| Event                                   | When it fires                  | Source           | What it contains                                                                                                                     |
| --------------------------------------- | ------------------------------ | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
| **`settlement` (pending)**              | 3–5s before block confirmation | Mempool calldata | All fills in `data.trades[]`, decoded from the matchOrders calldata. Per-maker prices are estimated from aggregate amounts.          |
| **`status_update`**                     | At block confirmation          | Receipt logs     | Confirmation metadata (block, latency) **plus** `data.confirmed_fills[]` — exact per-fill data from the on-chain `OrderFilled` logs. |
| **`settlement` (confirmed)** *(legacy)* | At block confirmation          | Calldata replay  | Same 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`:

```javascript theme={null}
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](/guides/trade-tracking) and the [Status Update event reference](/websocket/events/status-update).

## Pending vs confirmed (field-level)

| Field          | Pending                         | Confirmed                         |
| -------------- | ------------------------------- | --------------------------------- |
| `status`       | `"pending"`                     | `"confirmed"`                     |
| `block_number` | `null`                          | block number                      |
| `detected_at`  | when polynode first detected TX | same value                        |
| Latency        | \~0ms from detection            | 2–5s after detection (1–2 blocks) |

<Tip>
  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[]`.
</Tip>

## 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

<Warning>
  **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.
</Warning>

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`:

```javascript theme={null}
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](/websocket/events/trade#tracking-a-specific-wallets-trades) or the [Dome migration guide](/dome-migration#order-perspective).
