Real-time pulse of Polymarket — an order book feed that never freezes.
Project description
polypulse
Real-time pulse of Polymarket — an order book feed that never freezes.
polypulse keeps a live, in-memory Polymarket order book over WebSocket, so reading
the best bid/ask is instant — with no per-read network round-trip. REST /book polling
pays its latency on every read (≈185 ms from a typical host, ≈19 ms warm when
colocated); polypulse pays a one-time subscribe, then updates are pushed.
It adds the production reliability the official tooling lacks: heartbeat, a
PONG-aware watchdog that detects the silent WS freeze (issue #292),
exponential-backoff reconnect, and an optional REST fallback.
Install
pip install polypulse
Quickstart
import asyncio
from polypulse import BookFeed
async def main():
feed = BookFeed(["<token_id>"])
asyncio.create_task(feed.run()) # connects, reconnects, self-heals
await asyncio.sleep(2)
print(feed.best_bid("<token_id>"), feed.mid("<token_id>")) # 0 ms, in-memory
feed.stop()
asyncio.run(main())
Discover markets
No token_id yet? Find active markets and their tokens via the Gamma API:
from polypulse import list_markets, tokens_for_slug, BookFeed
markets = list_markets(tag="weather") # active markets (tag is optional)
tokens = tokens_for_slug(markets, markets[0].slug) # the token ids for one market
feed = BookFeed(tokens) # ...now stream them
Or from the terminal:
polypulse markets --tag weather
Why
REST /book polling pays per-read latency and serves a book that is ~1 s stale.
Polymarket's WebSocket can also silently freeze — the connection stays open but
events stop. polypulse pushes updates as the book changes (no per-read latency) and
guarantees liveness with a watchdog that reconnects the moment the socket goes quiet.
API
BookFeed(token_ids, on_update=None, *, ping_interval=10, watchdog_timeout=30, rest_fallback=True, max_backoff=30, rest_poll_interval=1.0, logger=None)
best_bid / best_ask / mid / spread (token_id)— sync, no networkbook(token_id)— full-depthOrderBooksnapshotstaleness(token_id),source(token_id)— freshness introspectionawait run()/stop()
Behavior notes
- Reads are synchronous and never hit the network — they return whatever the
background
run()task has most recently applied. source(token_id)is"ws"when the latest update came from the live socket, or"rest"when it came from the REST fallback (used only while the WS is down).staleness(token_id)is seconds since that token last updated.on_updatefires on WebSocket events only. While the socket is down, the REST fallback keeps the book fresh for readers (best_bidetc.) but does not invoke the callback; on reconnect you get a freshbooksnapshot (which does fire it).stop()signals shutdown and closes the active connection sorun()returns promptly.
Benchmark
python -m polypulse benchmark
It picks a live market and compares REST /book time-to-first-byte against the
WebSocket feed. Example run (from a non-colocated host — your absolute numbers depend
on where you run it):
market: highest-temperature-in-karachi-on-june-29-2026 (11 tokens)
REST /book TTFB: median 184.5ms min 124.4 max 238.3 (n=8)
WS subscribe → first book: 220.6ms
WS updates in 30s: 130 (4.3/s)
=== verdict ===
REST pays ~185ms EVERY read; WS pays 221ms ONCE, then updates are PUSHED (no per-read latency).
Absolute latency drops sharply when colocated (a eu-west-2 host measured ~19 ms warm REST GETs), but the structural win holds everywhere: REST pays its round-trip on every read; the WS feed pays once.
Watch a live book
polypulse watch <token_id>
Prints top-of-book once a second — a quick sanity check that the feed is live:
3414098972... bid=0.012 ask=0.024 mid=0.018 src=ws
3414098972... bid=0.012 ask=0.024 mid=0.018 src=ws
Values tick as the market moves; src shows ws or rest (REST fallback). An
animated GIF reads even better here — record one to drop in.
Honest note
polypulse was extracted from a live Polymarket trading bot. The trading edge didn't
pan out, but the low-latency feed is solid and battle-tested — so here it is. Out of
scope (for now): generic multi-CLOB support, book integrity hashing, indicators.
License
MIT.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file polypulse-0.1.0.tar.gz.
File metadata
- Download URL: polypulse-0.1.0.tar.gz
- Upload date:
- Size: 17.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
22d6c9066af91b69e73edf3cadcbcfefcd9ef9a3a1cbd3b7839c2a3e501af028
|
|
| MD5 |
3256de0f25bca64f904b467c17c06a72
|
|
| BLAKE2b-256 |
22cc29730080ec70bca1637110a99ff6d181f463b307bb7df1edd02838fc2012
|
Provenance
The following attestation bundles were made for polypulse-0.1.0.tar.gz:
Publisher:
publish.yml on Gavr625/polypulse
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
polypulse-0.1.0.tar.gz -
Subject digest:
22d6c9066af91b69e73edf3cadcbcfefcd9ef9a3a1cbd3b7839c2a3e501af028 - Sigstore transparency entry: 1997724158
- Sigstore integration time:
-
Permalink:
Gavr625/polypulse@2220790988b8b70731f03d36deb0d49e48ce1e56 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/Gavr625
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@2220790988b8b70731f03d36deb0d49e48ce1e56 -
Trigger Event:
push
-
Statement type:
File details
Details for the file polypulse-0.1.0-py3-none-any.whl.
File metadata
- Download URL: polypulse-0.1.0-py3-none-any.whl
- Upload date:
- Size: 14.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b98588f1f2b4cbe625671319b91a5841d2db52e65171d2fcc9807c583c02f335
|
|
| MD5 |
d8f0f6db6a87ff4fe03fe67e448c4724
|
|
| BLAKE2b-256 |
2c5de0617d2bc3673f0c53daee6a9d6975c66997c7a905fb0cbf1c00c29cad45
|
Provenance
The following attestation bundles were made for polypulse-0.1.0-py3-none-any.whl:
Publisher:
publish.yml on Gavr625/polypulse
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
polypulse-0.1.0-py3-none-any.whl -
Subject digest:
b98588f1f2b4cbe625671319b91a5841d2db52e65171d2fcc9807c583c02f335 - Sigstore transparency entry: 1997724302
- Sigstore integration time:
-
Permalink:
Gavr625/polypulse@2220790988b8b70731f03d36deb0d49e48ce1e56 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/Gavr625
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@2220790988b8b70731f03d36deb0d49e48ce1e56 -
Trigger Event:
push
-
Statement type: