Polymarket WebSocket API: live trades, positions and resolutions
5 live channels at wss://stream.vike.io/v1/stream/polymarket: every trade, every position change, every on-chain resolution, sharp price moves and new markets, pushed as they land. One free key; drop the connection and resume where you left off.
“Check this subscription before I connect: pm.trades, min_usd 1000, only top-5% wallets”
stream_check_filter“What did pm.resolutions send in the last hour?”
stream_events- Connect
wss://stream.vike.io/v1/stream/polymarketwithX-API-KEYon the handshake. - Subscribe
Send
{"action": "subscribe", "id", "channel", "filter"}; up to 100 on one connection. - Resume
Keep the last
cursor; subscribe with it after a drop and get what you missed, up to 3 days back.
Ten lines of Python
Every trade of $1,000 or more, printed as it lands.
import json, os
from websockets.sync.client import connect
with connect("wss://stream.vike.io/v1/stream/polymarket",
additional_headers={"X-API-KEY": os.environ["VIKE_API_KEY"]}) as ws:
ws.send(json.dumps({"action": "subscribe", "id": "s1", "channel": "pm.trades",
"filter": {"min_usd": 1000}}))
for message in ws:
frame = json.loads(message)
if frame["type"] == "ping":
ws.send(json.dumps({"action": "pong"}))
elif frame["type"] == "event":
e = frame["event"]
print(e["subject"]["title"], e["data"]["usd"])
vike-data stream polymarket --channel pm.trades --filter min_usd=1000 # prints NDJSON, reconnects by itself
- Every event has the same envelope:
schema,event_id,source,kind,ts,cursor,chain,subject,data,labels. - The server pings every 15 s; answer with
pong. 3 missed pongs close the connection. - 1 connection per key on this path, 3 per account; a newer one wins and the older is closed with a resume cursor.
- A list is OR inside a key, keys are AND across, an empty filter matches everything.
- A channel is produced only while someone subscribes to it (and 15 minutes after).
- Missed something without a socket? The same events are on REST:
/v1/polymarket/stream-events.
Build your subscription
Pick a channel and fill the filters: the subscribe message and the CLI command update as you type.
The 5 Polymarket channels
What each sends, every field of a real event, the filters it takes and the subscribe call.
pm.trades
Every trade, with the wallet, side, size, price, dollar value and maker or taker role.
- Path
- wss://stream.vike.io/v1/stream/polymarket
- Kind
- pm_trade
- Filters
- market, min_usd, pnl_tier, role, side, volume_tier, wallet
- Read
- every 15 s
Fields
| Field | Type | Description |
|---|---|---|
subject.wallet | string | The proxy wallet that traded |
subject.condition_id | string | The market |
subject.token_id | string | The outcome token |
subject.slug | string | Market slug |
subject.title | string | Market title |
subject.outcome | string | Outcome name |
subject.outcome_index | integer | 0 = the first outcome (Yes, Up) |
data.side | string | buy | sell |
data.size | number | Shares |
data.price | number | Price, 0 to 1 |
data.usd | number | Dollar value of the trade: size × price |
data.role | string | maker | taker |
data.tx_hash | string | Polygon transaction |
data.log_index | integer | Log index in the transaction |
data.block_number | integer | Polygon block |
labels.pnl_tier | string | The wallet’s realized-P&L tier |
labels.volume_tier | string | The wallet’s volume tier |
labels.is_top1 | boolean | Among the top 1% by P&L |
A real event captured 2026-11-03
{
"schema": "vike.event.v1",
"event_id": "pm.trades:0xae2bb65bc5df0ddb6da049d9828116b6a1ae5ba5facc0c4a104e557d95ebbf6c:937",
"source": "pm.trades",
"kind": "pm_trade",
"ts": "2026-10-31T12:10:13.000Z",
"cursor": "000096495438-000937",
"chain": "polygon",
"subject": {
"wallet": "0x5c50e006298ae03ea9e73533f68b205b750ee0c9",
"condition_id": "0x96672f8eee7ad956267a8f30ab5e17556d66c6f7da92a55bca7f0b525f2061ce",
"token_id": "114456141470340804944829704354275432124926411877510254720302351729914418144391",
"slug": "atp-machac-landalu-2026-10-31",
"title": "Rolex Paris Masters, Qualification: Tomas Machac vs Martin Landaluce",
"outcome": "Martin Landaluce",
"outcome_index": 1
},
"data": {
"side": "buy",
"size": 10812.304043,
"price": 0.46243612648287047,
"usd": 5000.0,
"role": "taker",
"tx_hash": "0xae2bb65bc5df0ddb6da049d9828116b6a1ae5ba5facc0c4a104e557d95ebbf6c",
"log_index": 937,
"block_number": 96495438
},
"labels": {
"pnl_tier": "bot1",
"volume_tier": "whale",
"is_top1": false
}
}
Filters
| Field | Type | Description |
|---|---|---|
market | list ≤ 200 | markets are condition ids: 0x followed by 64 hex characters |
min_usd | number | must be a finite number >= 0 |
pnl_tier | list ≤ 50 | belowmedian, bot1, bot10, bot5, median, top1, top10, top5, unknown, unranked |
role | one value | maker, taker |
side | one value | buy, sell |
volume_tier | list ≤ 50 | dolphin, fish, shrimp, unknown, whale |
wallet | list ≤ 1,000 | wallets are 0x followed by 40 hex characters |
Subscribe
import json, os
from websockets.sync.client import connect
with connect("wss://stream.vike.io/v1/stream/polymarket",
additional_headers={"X-API-KEY": os.environ["VIKE_API_KEY"]}) as ws:
ws.send(json.dumps({"action": "subscribe", "id": "s1", "channel": "pm.trades",
"filter": {"min_usd": 1000}}))
for message in ws:
frame = json.loads(message)
if frame["type"] == "ping":
ws.send(json.dumps({"action": "pong"}))
elif frame["type"] == "event":
e = frame["event"]
print(e["subject"]["title"], e["data"]["side"], e["data"]["usd"])
vike-data stream polymarket --channel pm.trades --filter min_usd=1000
{
"action": "subscribe",
"id": "s1",
"channel": "pm.trades",
"filter": {
"min_usd": 1000
}
}
pm.positions
What each trade did to the wallet’s position: open, increase, reduce, close or flip, with shares before and after and the average buy price.
- Path
- wss://stream.vike.io/v1/stream/polymarket
- Kind
- pm_position
- Filters
- action, market, min_usd, wallet
- Read
- every 15 s
Fields
| Field | Type | Description |
|---|---|---|
subject.wallet | string | The proxy wallet that traded |
subject.condition_id | string | The market |
subject.token_id | string | The outcome token |
subject.slug | string | Market slug |
subject.title | string | Market title |
subject.outcome | string | Outcome name |
subject.outcome_index | integer | 0 = the first outcome (Yes, Up) |
data.side | string | buy | sell |
data.size | number | Shares |
data.price | number | Price, 0 to 1 |
data.usd | number | Dollar value of the trade: size × price |
data.role | string | maker | taker |
data.tx_hash | string | Polygon transaction |
data.log_index | integer | Log index in the transaction |
data.block_number | integer | Polygon block |
data.action | string | open | increase | reduce | close | flip |
data.shares_before | number | Net shares before the trade |
data.shares | number | Net shares after the trade |
data.avg_price | number | Average BUY price after the trade; null if it bought nothing |
labels.pnl_tier | string | The wallet’s realized-P&L tier |
labels.volume_tier | string | The wallet’s volume tier |
labels.is_top1 | boolean | Among the top 1% by P&L |
A real event captured 2026-11-03
{
"schema": "vike.event.v1",
"event_id": "pm.positions:0x48e9342600be46b278f9d301fe7c5979045962c880766f560c228ef0798d1cd9:800",
"source": "pm.positions",
"kind": "pm_position",
"ts": "2026-11-02T14:28:37.000Z",
"cursor": "000096616174-000800",
"chain": "polygon",
"subject": {
"wallet": "0x958bdb8aa30b8e5bd1fe4ee071f309c84e97f826",
"condition_id": "0xc6d7e35bc46f765775da937872c39c44dc4956e39fc604f7700220231276f496",
"token_id": "82372917980430341396992188476134048035148682101989443667424605671912074667768",
"slug": "nba-por-den-2026-11-02-total-232pt5",
"title": "Trail Blazers vs. Nuggets: O/U 232.5",
"outcome": "Under",
"outcome_index": 1
},
"data": {
"side": "buy",
"size": 22464.82,
"price": 0.5,
"usd": 11232.41,
"role": "maker",
"tx_hash": "0x48e9342600be46b278f9d301fe7c5979045962c880766f560c228ef0798d1cd9",
"log_index": 800,
"block_number": 96616174,
"action": "open",
"shares_before": 0.0,
"shares": 22464.82,
"avg_price": 0.5
},
"labels": {
"pnl_tier": "bot1",
"volume_tier": "whale",
"is_top1": false
}
}
Filters
| Field | Type | Description |
|---|---|---|
action | list ≤ 50 | close, flip, increase, open, reduce |
market | list ≤ 200 | markets are condition ids: 0x followed by 64 hex characters |
min_usd | number | must be a finite number >= 0 |
wallet | list ≤ 1,000 | wallets are 0x followed by 40 hex characters |
Subscribe
import json, os
from websockets.sync.client import connect
with connect("wss://stream.vike.io/v1/stream/polymarket",
additional_headers={"X-API-KEY": os.environ["VIKE_API_KEY"]}) as ws:
ws.send(json.dumps({"action": "subscribe", "id": "s1", "channel": "pm.positions",
"filter": {"wallet": ["0x…"]}}))
for message in ws:
frame = json.loads(message)
if frame["type"] == "ping":
ws.send(json.dumps({"action": "pong"}))
elif frame["type"] == "event":
e = frame["event"]
print(e["subject"]["title"], e["data"]["action"], e["data"]["shares"])
vike-data stream polymarket --channel pm.positions --filter wallet=0x…
{
"action": "subscribe",
"id": "s1",
"channel": "pm.positions",
"filter": {
"wallet": [
"0x…"
]
}
}
pm.resolutions
Every on-chain resolution, traded or not: which outcome won and the payouts as stored on-chain; traded says whether the market was ever on Vike’s tape.
- Path
- wss://stream.vike.io/v1/stream/polymarket
- Kind
- pm_resolution
- Filters
- market
- Read
- every 60 s
Fields
| Field | Type | Description |
|---|---|---|
subject.condition_id | string | The market |
subject.slug | string | Market slug |
subject.question | string | Market question |
subject.outcomes | array | Outcome names |
data.winning_index | integer | Index of the winning outcome; -1 when no single outcome won |
data.winning_outcome | string | Name of the winning outcome |
data.payouts | array | Payout numerators as stored on-chain |
data.invalid | boolean | No single winner (a tie or a void) |
data.resolved_block | integer | Block of the resolution |
data.traded | boolean | The market was ever on Vike’s trade tape |
A real event captured 2026-11-03
{
"schema": "vike.event.v1",
"event_id": "pm.resolutions:0x1049c6dab88bea723bc960c9a6d8a1aac6ac1c1503124192d54986106db56ba4",
"source": "pm.resolutions",
"kind": "pm_resolution",
"ts": "2026-10-31T12:10:55.000Z",
"cursor": "000096495466-0x1049c6dab88bea723bc960c9a6d8a1aac6ac1c1503124192d54986106db56ba4",
"chain": "polygon",
"subject": {
"condition_id": "0x1049c6dab88bea723bc960c9a6d8a1aac6ac1c1503124192d54986106db56ba4",
"slug": "ethereum-above-2270-on-october-31-2026-8am-et",
"question": "Ethereum above 2,270 on October 31, 8AM ET?",
"outcomes": [
"Yes",
"No"
]
},
"data": {
"winning_index": 1,
"winning_outcome": "No",
"payouts": [
0,
1
],
"invalid": false,
"resolved_block": 96495466,
"traded": true
},
"labels": {}
}
Filters
| Field | Type | Description |
|---|---|---|
market | list ≤ 200 | markets are condition ids: 0x followed by 64 hex characters |
Subscribe
import json, os
from websockets.sync.client import connect
with connect("wss://stream.vike.io/v1/stream/polymarket",
additional_headers={"X-API-KEY": os.environ["VIKE_API_KEY"]}) as ws:
ws.send(json.dumps({"action": "subscribe", "id": "s1", "channel": "pm.resolutions",
"filter": {"market": ["0x…"]}}))
for message in ws:
frame = json.loads(message)
if frame["type"] == "ping":
ws.send(json.dumps({"action": "pong"}))
elif frame["type"] == "event":
e = frame["event"]
print(e["subject"].get("question"), e["data"]["winning_outcome"])
vike-data stream polymarket --channel pm.resolutions --filter market=0x…
{
"action": "subscribe",
"id": "s1",
"channel": "pm.resolutions",
"filter": {
"market": [
"0x…"
]
}
}
pm.price_moves
An outcome’s mid price moved 0.05 or more within 300 seconds, up or down, with the best bid and ask at the end.
- Path
- wss://stream.vike.io/v1/stream/polymarket
- Kind
- pm_price_move
- Filters
- market, min_move, move, token_id
- Read
- every 30 s
Fields
| Field | Type | Description |
|---|---|---|
subject.token_id | string | The outcome token |
subject.condition_id | string | The market |
subject.slug | string | Market slug |
subject.outcome | string | Outcome name |
data.mid_from | number | Mid price at the window start |
data.mid_to | number | Mid price at the window end |
data.move | number | mid_to − mid_from |
data.direction | string | up | down |
data.window_s | integer | Window, seconds |
data.bid | number | Best bid at the end |
data.ask | number | Best ask at the end |
A real event captured 2026-11-03
{
"schema": "vike.event.v1",
"event_id": "pm.price_moves:1511524245571629932608167359009556854548643053077909198541619162127863963771:5978161:down",
"source": "pm.price_moves",
"kind": "pm_price_move",
"ts": "2026-10-31T12:09:28.581Z",
"cursor": "1793448568581-1511524245571629932608167359009556854548643053077909198541619162127863963771",
"chain": "polygon",
"subject": {
"token_id": "1511524245571629932608167359009556854548643053077909198541619162127863963771",
"condition_id": "0xfe3230e9efc339c84aec884d7b4a3c2633198c58f5085536efea1102121e5e66",
"slug": "xrp-updown-5m-1793448300",
"outcome": "Down"
},
"data": {
"mid_from": 0.085,
"mid_to": 0.01,
"move": -0.075,
"direction": "down",
"window_s": 300,
"bid": 0.01,
"ask": 0.01
},
"labels": {}
}
Filters
| Field | Type | Description |
|---|---|---|
market | list ≤ 200 | markets are condition ids: 0x followed by 64 hex characters |
min_move | number | must be a finite number >= 0; must be a price move above 0 and at most 1 (0.05 = five points) |
move | one value | down, up |
token_id | list ≤ 200 | token ids are 1 to 80 decimal digits |
Subscribe
import json, os
from websockets.sync.client import connect
with connect("wss://stream.vike.io/v1/stream/polymarket",
additional_headers={"X-API-KEY": os.environ["VIKE_API_KEY"]}) as ws:
ws.send(json.dumps({"action": "subscribe", "id": "s1", "channel": "pm.price_moves",
"filter": {"min_move": 0.1}}))
for message in ws:
frame = json.loads(message)
if frame["type"] == "ping":
ws.send(json.dumps({"action": "pong"}))
elif frame["type"] == "event":
e = frame["event"]
print(e["subject"]["slug"], e["subject"]["outcome"], e["data"]["move"])
vike-data stream polymarket --channel pm.price_moves --filter min_move=0.1
{
"action": "subscribe",
"id": "s1",
"channel": "pm.price_moves",
"filter": {
"min_move": 0.1
}
}
pm.new_markets
A new market as soon as it is listed, with its outcomes and token ids.
- Path
- wss://stream.vike.io/v1/stream/polymarket
- Kind
- pm_new_market
- Filters
- class
- Read
- every 60 s
Fields
| Field | Type | Description |
|---|---|---|
subject.condition_id | string | The market |
subject.slug | string | Market slug |
subject.question | string | Market question |
subject.outcomes | array | Outcome names |
subject.token_ids | array | Outcome token ids |
data.class | string | updown_5m | updown_15m | other |
data.first_seen | string | When Vike first saw the market |
data.end_date | string | Scheduled end |
data.neg_risk | boolean | Neg-risk market |
data.tick_size | number | Price step of the book |
data.active | boolean | Active in the market catalogue |
data.closed | boolean | Closed in the market catalogue |
A real event captured 2026-11-03
{
"schema": "vike.event.v1",
"event_id": "pm.new_markets:0x1d0a0fa7b01e63b1c2fe909c3a552d919e159ba827c12d5392d8e4a93dbbd5b9",
"source": "pm.new_markets",
"kind": "pm_new_market",
"ts": "2026-11-02T09:51:30.403Z",
"cursor": "1793613090403-0x1d0a0fa7b01e63b1c2fe909c3a552d919e159ba827c12d5392d8e4a93dbbd5b9",
"chain": "polygon",
"subject": {
"condition_id": "0x1d0a0fa7b01e63b1c2fe909c3a552d919e159ba827c12d5392d8e4a93dbbd5b9",
"slug": "bnb-updown-5m-1793613300",
"question": "BNB Up or Down - November 2, 4:55AM-5:00AM ET",
"outcomes": [
"Up",
"Down"
],
"token_ids": [
"95853262724305275692474634613186630565505623432729531159596758216209572513482",
"109054760289612549909159513403492642659819464853087188514831720675883642802397"
]
},
"data": {
"class": "updown_5m",
"first_seen": "2026-11-02T09:51:30+00:00",
"end_date": "2026-11-02T10:00:00+00:00",
"neg_risk": true,
"tick_size": 0.01,
"active": true,
"closed": false
},
"labels": {}
}
Filters
| Field | Type | Description |
|---|---|---|
class | list ≤ 50 | other, updown_15m, updown_5m |
Subscribe
import json, os
from websockets.sync.client import connect
with connect("wss://stream.vike.io/v1/stream/polymarket",
additional_headers={"X-API-KEY": os.environ["VIKE_API_KEY"]}) as ws:
ws.send(json.dumps({"action": "subscribe", "id": "s1", "channel": "pm.new_markets",
"filter": {"class": ["updown_5m"]}}))
for message in ws:
frame = json.loads(message)
if frame["type"] == "ping":
ws.send(json.dumps({"action": "pong"}))
elif frame["type"] == "event":
e = frame["event"]
print(e["subject"]["question"], e["data"]["end_date"])
vike-data stream polymarket --channel pm.new_markets --filter class=updown_5m
{
"action": "subscribe",
"id": "s1",
"channel": "pm.new_markets",
"filter": {
"class": [
"updown_5m"
]
}
}