polyflux-client is the official Python library for the Polyflux feed: pip install polyflux-client gives you every Polymarket trade as a typed object the millisecond it happens, and a local catalog that turns any trade’s asset_id into a market you can actually name. It’s the five-line raw client with the production plumbing — parsing, reconnection, market resolution — already done.
Source is on GitHub, package on PyPI. Here’s the tour.
Streaming: typed trades, zero plumbing
import asyncio
from polyflux import Client
async def main():
client = Client("YOUR_API_KEY") # get one at /auth
async for trade in client.trades():
print(trade.side, trade.size, trade.price, trade.wallet_address)
asyncio.run(main())
client.trades() yields only trade events (redeems, splits, and merges are filtered out), parsed into a Trade dataclass:
asset_id— the clob token id of the outcome tradedwallet_address— who tradedsize,price— position size and odds (0.00–1.00), as floatsside/operation_type—buyorsell, with anis_buyshortcuttimestamp— epoch time, plus.timefor a UTCdatetime.notional—size × price, the trade’s USDC value.raw— the full original message, so nothing the feed sends is ever out of reach
Two behaviors matter for anything that runs unattended. Drops reconnect automatically with exponential backoff — your async for just keeps going. A bad key fails fast: rejected keys raise AuthError immediately instead of retrying forever, so you find out at startup, not in the logs a day later. (Want the unfiltered feed, redeems and all? client.events() yields every raw event dict.)
The catalog: from asset_id to a market you can name
A trade tells you what happened — but its market arrives as an asset_id like 98022490269…, which is precise and completely unreadable. Calling an API to resolve every trade would put a network round-trip in your hot path, with all the latency you came here to avoid.
MarketCatalog solves it locally:
from polyflux import Client, MarketCatalog
catalog = MarketCatalog(
event_fields=["slug", "title", "tags"],
market_fields=["question", "outcomes"],
)
await catalog.start() # warms from ./data, downloads if stale
record = catalog.get(trade.asset_id) # O(1) dict read — no network call
if record:
print(record["market"]["question"]) # "Will …?"
It builds an asset_id → market map from Polymarket’s Gamma API, and the design is worth understanding:
- On disk: everything. The catalog keeps a raw dump of every active Polymarket event — about 500 MB, the complete payload the API returns. Disk is cheap, and the dump means a restart warms up from file instead of re-downloading.
- In memory: only what you ask for.
event_fields/market_fieldsact as a mask — only those fields are projected from the dump into RAM. The default tags-only projection holds every active market in ~67 MB; a rich trading projection (ids, questions, volumes, dates) runs ~160 MB. Wildcards work too ("volume*"), and afields={...}dict lets you compute custom values per market. - It stays current. The catalog re-downloads in the background (hourly by default) and swaps the mapping atomically — lookups never block on a refresh. Set
refresh_interval=Nonefor offline analysis against the dump you have. - New markets resolve on demand. A trade in a market created minutes ago won’t be in the dump yet. With
on_miss="background", unknown ids are fetched in batches as they appear;await catalog.resolve(asset_id)fetches one immediately when you need the answer now. - It can shrink further. A
keeppredicate decides which markets enter memory at all — e.g. only events tagged crypto — so a focused bot pays for exactly its universe.
Putting both together: a whale alert with names
The two pieces compose into the classic first bot — whale watching, but with human-readable output:
import asyncio
from polyflux import Client, MarketCatalog
BIG = 5_000 # shares
async def main():
catalog = MarketCatalog(market_fields=["question"], on_miss="background")
await catalog.start()
client = Client("YOUR_API_KEY")
async for trade in client.trades():
if (trade.size or 0) < BIG:
continue
record = catalog.get(trade.asset_id)
name = record["market"]["question"] if record else trade.asset_id
print(f"🐳 {trade.side} {trade.size:,.0f} @ {trade.price} — {name}")
asyncio.run(main())
🐳 buy 18,400 @ 0.63 — Will X happen by March? beats a wall of token ids — and everything after the async for is your logic, not plumbing.
When to use the library vs. raw WebSockets
Use polyflux-client when you’re building something that runs — bots, alerts, position trackers — and reconnection, typing, and market resolution are problems you’d rather not own. Drop to a raw WebSocket when you want to see the feed with zero abstraction, or you’re in a language other than Python. Same feed, same events, either way.
pip install polyflux-client, grab a key, and the whale alert above is running in the time it takes the catalog to warm up.