> ## 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 and Event Streams

> Subscribe to settlements and other PolyNode events with typed filtering, acknowledgements, reconnect replay, deduplication, and explicit cleanup.

The event WebSocket is the fastest path from a detected Polymarket fill to your application. A `settlements` subscription emits:

* `settlement` when PolyNode detects a matching transaction, with `status` set to `pending` or `confirmed`
* `status_update` when a previously pending transaction is confirmed on-chain

The SDK waits for the server's subscription acknowledgement before returning the subscription. It then routes typed events to handlers or an async iterator.

## Subscribe to settlements

<Tabs>
  <Tab title="TypeScript">
    ```typescript theme={null}
    import { PolyNode } from 'polynode-sdk';

    const apiKey = process.env.POLYNODE_API_KEY;
    if (!apiKey) throw new Error('Set POLYNODE_API_KEY');
    const pn = new PolyNode({ apiKey });

    const sub = await pn.ws
      .subscribe('settlements')
      .status('all')
      .snapshotCount(25)
      .send();

    sub.on('settlement', (event) => {
      console.log(event.status, event.tx_hash, event.taker_side, event.taker_size);
    });
    sub.on('status_update', (event) => {
      console.log('confirmed', event.tx_hash, event.block_number);
    });

    process.once('SIGINT', () => {
      sub.unsubscribe();
      pn.ws.disconnect();
    });
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    import asyncio
    import os
    from polynode import AsyncPolyNode

    async def main() -> None:
        async with AsyncPolyNode(api_key=os.environ["POLYNODE_API_KEY"]) as pn:
            sub = await (
                pn.ws.subscribe("settlements")
                .status("all")
                .snapshot_count(25)
                .send()
            )
            try:
                async for event in sub:
                    if event.event_type == "settlement":
                        print(event.status, event.tx_hash, event.taker_size)
                    elif event.event_type == "status_update":
                        print("confirmed", event.tx_hash, event.block_number)
            finally:
                sub.unsubscribe()
                pn.ws.disconnect()

    asyncio.run(main())
    ```
  </Tab>

  <Tab title="Rust">
    ```rust theme={null}
    use polynode::{PolyNodeClient, PolyNodeEvent, WsMessage};
    use polynode::ws::{StreamOptions, Subscription, SubscriptionType};

    #[tokio::main]
    async fn main() -> polynode::Result<()> {
        let client = PolyNodeClient::new(
            std::env::var("POLYNODE_API_KEY").expect("Set POLYNODE_API_KEY")
        )?;
        let mut stream = client.stream(StreamOptions::default()).await?;
        let ack = stream.subscribe_with_ack(
            Subscription::new(SubscriptionType::Settlements)
                .status("all")
                .snapshot_count(25),
        ).await?;
        println!("subscription {}", ack.subscription_id);

        while let Some(message) = stream.next().await {
            match message? {
                WsMessage::Event(PolyNodeEvent::Settlement(event)) => {
                    println!("{:?} {} {}", event.status, event.tx_hash, event.taker_size);
                }
                WsMessage::Event(PolyNodeEvent::StatusUpdate(event)) => {
                    println!("confirmed {} in {}", event.tx_hash, event.block_number);
                }
                WsMessage::Replay(notice) => eprintln!("{}", notice.warning),
                _ => {}
            }
        }
        stream.close().await?;
        Ok(())
    }
    ```
  </Tab>
</Tabs>

`snapshotCount` / `snapshot_count` returns recent matching events before live delivery. Set it only when your application benefits from a warm start.

## Filter at the server

Filters reduce bandwidth and work before events reach your process.

| Filter                 | TypeScript                    | Python                          | Rust                            | Use                                 |
| ---------------------- | ----------------------------- | ------------------------------- | ------------------------------- | ----------------------------------- |
| Wallets                | `.wallets([...])`             | `.wallets([...])`               | `.wallets(vec![...])`           | maker or taker addresses            |
| Tokens                 | `.tokens([...])`              | `.tokens([...])`                | `.tokens(vec![...])`            | outcome token IDs                   |
| Market slugs           | `.slugs([...])`               | `.slugs([...])`                 | `.slugs(vec![...])`             | exact market slugs                  |
| Conditions             | `.conditionIds([...])`        | `.condition_ids([...])`         | `.condition_ids(vec![...])`     | condition IDs                       |
| Minimum / maximum size | `.minSize(n)` / `.maxSize(n)` | `.min_size(n)` / `.max_size(n)` | `.min_size(n)` / `.max_size(n)` | USD-size bounds                     |
| Status                 | `.status('all')`              | `.status('all')`                | `.status("all")`                | pending, confirmed, or both         |
| Replay cursor          | `.since(ms)`                  | `.since(ms)`                    | `.since(ms)`                    | events newer than Unix milliseconds |

Example wallet filter:

<Tabs>
  <Tab title="TypeScript">
    ```typescript theme={null}
    const sub = await pn.ws.subscribe('settlements')
      .wallets(['0xabc...', '0xdef...'])
      .minSize(1000)
      .status('all')
      .send();
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    sub = await (
        pn.ws.subscribe("settlements")
        .wallets(["0xabc...", "0xdef..."])
        .min_size(1000)
        .status("all")
        .send()
    )
    ```
  </Tab>

  <Tab title="Rust">
    ```rust theme={null}
    let ack = stream.subscribe_with_ack(
        Subscription::new(SubscriptionType::Settlements)
            .wallets(vec!["0xabc...".into(), "0xdef...".into()])
            .min_size(1000.0)
            .status("all"),
    ).await?;
    ```
  </Tab>
</Tabs>

See [Subscribing](/websocket/subscribing) for preset-specific filter behavior.

## Presets

The SDKs use the same subscription names:

| Preset                       | Typical events                                                                                 |
| ---------------------------- | ---------------------------------------------------------------------------------------------- |
| `settlements`                | settlement detection and confirmation updates                                                  |
| `trades`                     | settlements, confirmed trades, status updates                                                  |
| `dome` / `fills`             | normalized per-fill events                                                                     |
| `combos`                     | combo executions and status updates                                                            |
| `redemptions` / `redemption` | redemptions                                                                                    |
| `wallets`                    | settlement, trade, position, deposit, conversion, and redemption activity for selected wallets |
| `markets`                    | activity for selected markets                                                                  |
| `large_trades`               | settlement and trade events above a size threshold                                             |
| `blocks`                     | block summaries                                                                                |
| `oracle`                     | oracle lifecycle events                                                                        |
| `chainlink`                  | spot or explicit TWAP price events                                                             |
| `global`                     | the default public event set; combo events require `combos`                                    |

## Acknowledgements

Do not assume a subscription is active before the SDK's subscribe call resolves. The acknowledgement includes the server-assigned subscription ID and may include warnings.

For Chainlink subscriptions it also reports the selected price source and explicit TWAP windows:

<Tabs>
  <Tab title="TypeScript">
    ```typescript theme={null}
    const prices = await pn.ws.subscribe('chainlink')
      .feeds(['BTC/USD'])
      .twapWindows([30])
      .send();

    console.log(prices.id, prices.priceSource, prices.twapWindows, prices.warnings);
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    prices = await (
        pn.ws.subscribe("chainlink")
        .feeds(["BTC/USD"])
        .twap_windows([30])
        .send()
    )
    print(prices.id, prices.price_source, prices.twap_windows, prices.warnings)
    ```
  </Tab>

  <Tab title="Rust">
    ```rust theme={null}
    let ack = stream.subscribe_with_ack(
        Subscription::new(SubscriptionType::Chainlink)
            .feeds(vec!["BTC/USD".into()])
            .twap_windows(vec![30]),
    ).await?;
    println!("{:?} {:?} {:?}", ack.price_source, ack.twap_windows, ack.warnings);
    ```
  </Tab>
</Tabs>

One event-stream connection can carry several normal subscriptions. Chainlink feed and TWAP selection is connection-scoped, so combine all desired Chainlink feeds and windows in one Chainlink subscription on that connection. Use another connection for a different selection.

## Reconnect behavior

Automatic reconnect is enabled by default. After a disconnect, the SDK:

1. opens a replacement socket
2. resubscribes each active subscription with its original filters
3. adds a best-effort `since` cursor based on the latest accepted event
4. overlaps the cursor slightly and removes duplicate delivery in the client
5. reports replay state to your application

This reduces avoidable gaps, but it is not an unlimited gapless guarantee. Server history is bounded, and a disconnect can outlast it. Treat every replay notice as an observable recovery interval.

<Tabs>
  <Tab title="TypeScript">
    ```typescript theme={null}
    pn.ws.onReplay((notice) => {
      console.warn(notice.phase, notice.since, notice.guaranteed, notice.warning);
    });
    pn.ws.onReconnect((attempt) => console.warn('reconnected', attempt));
    pn.ws.onError((error) => console.error(error.message));
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    pn.ws.on_replay(
        lambda notice: print(
            notice.phase, notice.since, notice.guaranteed, notice.warning
        )
    )
    pn.ws.on_reconnect(lambda attempt: print("reconnected", attempt))
    pn.ws.on_error(lambda error: print(error))
    ```
  </Tab>

  <Tab title="Rust">
    ```rust theme={null}
    match message? {
        WsMessage::Replay(notice) => {
            eprintln!("{} (guaranteed={})", notice.warning, notice.guaranteed);
        }
        WsMessage::Error { code, message } => eprintln!("{code:?}: {message}"),
        _ => {}
    }
    ```
  </Tab>
</Tabs>

<Note>
  Managed short-form streams also reconnect at every market boundary. That deliberate rotation is required to replace the old market slugs and connection-scoped Chainlink selection. See [Short-form and TWAP](/sdks/short-form).
</Note>

## Slow consumers

TypeScript and Python async iterators use a bounded per-subscription queue. Register an overflow callback if processing may fall behind:

<Tabs>
  <Tab title="TypeScript">
    ```typescript theme={null}
    sub.onOverflow((notice) => {
      console.error('dropped events', notice.droppedEvents, notice.queueCapacity);
    });
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    sub.on_overflow(
        lambda notice: print("dropped events", notice.dropped_events)
    )
    ```
  </Tab>

  <Tab title="Rust">
    Rust applies backpressure to the bounded stream instead of silently evicting settlement events. Keep the `next()` loop lightweight and hand expensive work to a bounded worker queue.
  </Tab>
</Tabs>

Handler callbacks are still invoked immediately. Queue overflow applies to the async-iterator buffer.

## Forward-compatible events

New event types do not disappear merely because your SDK predates their typed model:

* TypeScript emits an `unknown` event containing `wire_event_type`, `raw`, and an optional decode error.
* Python yields `UnknownEvent` with the complete raw payload.
* Rust emits `WsMessage::UnknownEvent` or `WsMessage::UnknownMessage`.

Log the event type and upgrade the SDK; avoid logging credentials or an entire payload when it may contain user data.

## Cleanup

Stop subscriptions and close their owning socket during shutdown:

<Tabs>
  <Tab title="TypeScript">
    ```typescript theme={null}
    sub.unsubscribe();
    pn.ws.disconnect();
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    sub.unsubscribe()
    pn.ws.disconnect()
    ```
  </Tab>

  <Tab title="Rust">
    ```rust theme={null}
    stream.unsubscribe(Some(ack.subscription_id)).await?;
    stream.close().await?;
    ```
  </Tab>
</Tabs>

Next: [Short-form and Chainlink TWAP](/sdks/short-form) or the full [event reference](/websocket/overview).
