Synpath: one API for every prediction market # Synpath โ€“ One API for prediction markets Follow @Synpath_Dev on X **Build on prediction markets without building against each one.**
One open-source API across 16 venues (Kalshi, Polymarket, Polymarket US, Opinion, Hyperliquid, predict.fun, Limitless, SX Bet, Myriad and Gemini, and the sports betting venues Betfair, Smarkets, Novig, Matchbook, ProphetX and Azuro): market data, order entry, live streams, and an execution engine that remembers what it sent. In-process from Python, over REST and WebSocket from anywhere. License: MIT Total Downloads Discord
**๐Ÿ†• New: free tick-level Kalshi order book history.**
Every public Kalshi market, full depth, every change and every trade, straight from Kalshi's live feed. Sign up from your terminal and query it in one line of Python. **[Get it in 60 seconds โ†“](#free-kalshi-tick-data-in-60-seconds)** โญ **[Star Synpath on GitHub](https://github.com/Synpath-ai/synpath)** if the data saves you money. Stars are how we decide what to open up next. --- ## Free Kalshi tick data in 60 seconds **1. Install** ```bash pip install synpath ``` **2. Sign up**: opens Google sign-in in your browser, then returns to the terminal. Free, no card. ```bash synpath login ``` **3. Create your API key**: it's saved on your machine and Python picks it up automatically. ```bash synpath keys create my-laptop ``` **4. Query any moment** ```python import synpath t = 1792065600000 # 2026-10-15 12:00 UTC, in venue Unix milliseconds # The full book exactly as it stood at that millisecond snap = synpath.fetch_order_book_at("kalshi:KXQUANTUM-30", as_of_ms=t) print(snap.book.bids[0].price, snap.book.asks[0].price, len(snap.book.bids), "bid levels") # Every change over an hour: a starting book, then each update in venue-time order changes = synpath.fetch_order_book_range("kalshi:KXQUANTUM-30", start_ms=t, end_ms=t + 3_600_000) # Every trade: price, size and taker side trades = synpath.fetch_trades_range("kalshi:KXQUANTUM-30", start_ms=t, end_ms=t + 3_600_000) ``` Request windows use Kalshi venue time (`metadata.time_basis="venue"`); `observed_at_ms` is the separate recorder receive time. No silent gaps: if we weren't recording at a moment, you get the reason instead of an empty book. Up to an hour per request; plain HTTP works too (`Authorization: Bearer `). More: **[synpath.dev/tick-data](https://www.synpath.dev/tick-data)** ยท **[API reference](https://www.synpath.dev/docs/api-reference/history/book-at)** > โญ **Free data stays free as long as people use it.** If this saves you a data bill, [star the repo](https://github.com/Synpath-ai/synpath). It takes one click and it's how other traders find it. **Also new: smart order routing across Kalshi and Polymarket.** One order, both order books combined, filled from the cheapest price after fees. [See how it works โ†’](https://www.synpath.dev/docs/concepts/buckets) --- ## Supported Exchanges | | Exchange | id | Market data | Order entry | Streams | Order books | |:-:|---|---|:-:|:-:|:-:|---| | Kalshi | [Kalshi](https://kalshi.com) | `kalshi` | โœ“ | โœ“ | WebSocket | one book per market, both sides read it | | Polymarket | [Polymarket](https://polymarket.com) | `polymarket` | โœ“ | โœ“ | WebSocket | one book per outcome token | | Polymarket US | [Polymarket US](https://polymarket.us) | `polymarket_us` | โœ“ | โœ“ retail and exchange APIs | WebSocket and gRPC | one book per market, both sides read it | | | [Opinion](https://opinion.trade) | `opinion` | โœ“ | โœ“ | WebSocket (API key) | one book per outcome token | | | [Hyperliquid](https://app.hyperliquid.xyz) | `hyperliquid` | โœ“ outcome markets (HIP-4) | โœ“ | WebSocket (no key) | one book per market, both sides read it | | | [predict.fun](https://predict.fun) | `predict_fun` | โœ“ (API key) | โœ“ | WebSocket (API key) | one book per market, both sides read it | | | [Limitless](https://limitless.exchange) | `limitless` | โœ“ | โœ“ | WebSocket | one book per market, both sides read it | | | [SX Bet](https://sx.bet) | `sx_bet` | โœ“ sports | โœ“ | WebSocket | one book per market, both sides read it | | | [Gemini Predictions](https://www.gemini.com/predictions) | `gemini` | โœ“ no account, sports taxonomy | โœ“ US accounts with Prediction Markets access | WebSocket (no key for books) | one book per contract, in YES space | | | [Betfair Exchange](https://www.betfair.com/exchange) | `betfair` | โœ“ your own account and app key (delayed key: 1-180 s late, 3 levels) | โœ“ your own account and a live app key | Exchange Stream API (TLS socket) | one book per runner, back and lay read as YES | | | [Smarkets](https://smarkets.com) | `smarkets` | โœ“ no account (quotes delayed without a login) | โœ“ your own login, with Smarkets' approved API access | polled from REST quotes | one book per contract, bids are YES bids | | | [Novig](https://novig.com) | `novig` | โœ“ sports, no key | โœ“ (US, identity and location checks; Paper for play money) | WebSocket (trading key), else polled from the public book | one book per market, both sides read it | | | [Matchbook](https://www.matchbook.com) | `matchbook` | โœ“ no account | โœ“ your own login | polled from REST | one book per runner, back and lay read as YES | | | [ProphetX](https://www.prophetx.co) | `prophetx` | โœ“ sports (Trading API key pair, or a Market Data API key) | โœ“ | WebSocket (Trading API key pair) | one book per line, both sides read it | | | [Azuro](https://azuro.org) | `azuro` | โœ“ sports, no key | โœ“ bets with a worst price | WebSocket (no key) | no book: the pool's quoted odds, one level a side sized by its limits | | | [Myriad](https://myriad.markets) | `myriad` | โœ“ order-book and AMM markets, sports | โœ“ order book; AMM as market orders | WebSocket (no key) | one book per market, both sides read it; AMM books built from the pool | Betfair is a betting exchange, not a prediction market, read here the same way: each runner (selection) is a yes/no market, `betfair:1.234567890-47972`, and the Betfair market holding the runners is the event. Every Betfair call needs your own Betfair account and application key (betting needs Betfair's live key; the free delayed one reads), and the API is refused from several countries (the US, Germany, France, the Netherlands, Singapore, India, Turkey, the UAE and others). Betfair's keys are for your own betting: showing its prices to anyone else needs a data licence from Betfair. See [capabilities](docs/capabilities.md#betfair). Gemini Predictions is Gemini Titan's CFTC-registered event-contract exchange. Each contract is a yes/no market named by its instrument symbol, `gemini:GEMI-NBA-2610232000-CHI-MIL-M-MIL`, and the Gemini event holding the contracts is the event. Sports events carry Gemini's taxonomy (moneyline, spread, total, prop; the sport, scope and statistic), which listings filter on. Market data needs no account; trading needs a Gemini account with Prediction Markets access (US) and an API key. See [capabilities](docs/capabilities.md#gemini). Smarkets, a UK betting exchange, is read the same way: each contract is a yes/no market, `smarkets:164973231-456060183`, and the Smarkets market holding the contracts is the event. Its market data needs no account (quotes are delayed without a login); betting logs in with your own Smarkets username and password, and placing orders needs Smarkets' approved API access (a one-off ยฃ150 fee). Smarkets' API terms allow viewing prices and betting on your own account, and prohibit odds scraping. See [capabilities](docs/capabilities.md#smarkets). Novig, a US sports exchange, lists two-outcome markets (a moneyline, a spread, a total, a Yes/No prop), each one yes/no market, `novig:01a1577c-d666-7e81-852e-68bed1446801`, under its game as the event. Its catalog, books and trades are public and need no key. Trading signs each request with a subaccount's key; Production trades real money for verified US customers outside Arizona, Michigan and Nevada, and Paper (`NOVIG_ENV=paper`) is the same API with play money. Novig's parlay RFQ is supported from the liquidity provider's side: `NovigPricer` prices and last-looks parlays for a registered LP account ([trading](docs/trading.md#novig-parlay-rfq)). See [capabilities](docs/capabilities.md#novig). Matchbook, a betting exchange licensed in the UK and Alderney, is read the same way: each runner is a yes/no market, `matchbook:--`, and the Matchbook market holding the runners is the event. Its market data needs no account; betting logs in with your own Matchbook username and password. Every GET is chargeable on Matchbook's API plans (ยฃ100 per million a month), and its terms allow viewing prices and betting on your own account only. See [capabilities](docs/capabilities.md#matchbook). ProphetX, a US peer-to-peer sports exchange, is read the way SX Bet is: each line (a moneyline, or a spread or total at one number) is a yes/no market whose first side is YES, `prophetx:44285780-90de769f4f4977a04446ed203e957351` (the event, then the YES side's strike id), and the game is the event. Prices are American odds, converted exactly to probabilities. Every call needs a key: the Trading API key pair, which any KYC-approved ProphetX account generates under API Integration, or a Market Data API key issued by ProphetX on request. See [capabilities](docs/capabilities.md#prophetx). Azuro is an onchain sports betting protocol: a shared liquidity pool takes every bet at the odds it quotes, so there is no order book and nothing rests. A two-outcome condition ("Total Goals Over/Under 2.5") is one yes/no market, `azuro:`, with the lower outcome id as YES; a condition with three or more outcomes (1X2, correct score) is one market per outcome, `azuro:-`, where only YES can be bought. The game is the event. Prices are `1 / odds`. Ids are the same on every chain; `Azuro(chain=...)` picks the pool read (Polygon USDT by default, Base WETH). Bets are EIP-712 messages signed by your wallet and placed on chain by Azuro's relayer. Azuro's bet API and streams are refused from some regions. See [capabilities](docs/capabilities.md#azuro). Myriad runs order-book markets on BNB Chain and AMM (pool) markets on BNB Chain, Abstract and Linea behind one API. A market id carries its network, and `amm` for a pool market, since the two kinds number their markets separately: `myriad:56-2385` is an order-book market, `myriad:56-amm-2385` an unrelated pool market. Order entry covers both: signed orders on the order book, and market orders against a pool, each sent as one on-chain transaction from the wallet. See [capabilities](docs/capabilities.md#myriad). ## Why Synpath - **Liquidity is fragmented. Your time shouldn't be.** The same market trades on Kalshi, Polymarket, Polymarket US, Opinion, Hyperliquid, predict.fun, Limitless, SX Bet, Myriad and Gemini, and the sports betting venues Betfair, Smarkets, Novig, Matchbook, ProphetX and Azuro, each with its own API, units and quirks. Synpath gives you one interface for all of them, and smart order routing buys from whichever book is cheapest. Spend your time on alpha, not plumbing. - **Traders deserve advanced order types.** Stops, trailing stops, icebergs, OCO, brackets, TWAP and pegs, on every venue, even where the exchange has none. Orders are journaled before they're sent, so a crash never places one twice. - **A home after Dome and pmxt.** Dome's API shut down in April 2026, and pmxt hasn't shipped since July 2026. Synpath is MIT-licensed, actively maintained, and follows ccxt conventions. ## Installation ```bash pip install synpath # market data, order entry, streams, the engine and the server pip install "synpath[grpc]" # + Polymarket US exchange gRPC streams ``` Python 3.10 or newer. The hot paths (order books, reading the venues' book streams, order signing and the order router) run on a Rust core that ships prebuilt inside the wheel for Linux, macOS and Windows; nothing else to install. **Hosted API.** Run `synpath login`, then `synpath keys create` ([step by step โ†‘](#free-kalshi-tick-data-in-60-seconds)). It serves tick-level Kalshi order book and trade history, and cross-venue market matching. Trading stays on your machine: Synpath never holds your keys or funds. ## Quick Start **Markets and quotes** ```python import synpath kalshi = synpath.Kalshi() market = kalshi.fetch_markets(limit=1)[0] print(market.title) # Will Elon Musk visit Mars before Aug 1, 2099? print(market.yes.quote.bid, market.yes.quote.ask) # 0.1 0.12 ``` **Order books and search** ```python book = kalshi.fetch_order_book(market.id, depth=5) book.best_bid, book.best_ask # best first on both sides kalshi.fetch_order_book(market.id, side="no") # what NO costs markets = kalshi.fetch_markets(query="trump", limit=10) fee = kalshi.fetch_fee_schedule(markets[0].id) fee.estimate(price=0.50, contracts=100) # 1.75 ``` **Same code, every venue** ```python for venue_id in synpath.exchanges: # [..., 'betfair', 'smarkets', 'novig', 'matchbook', 'prophetx', 'azuro', 'myriad', 'gemini'] (Betfair, predict.fun and ProphetX need keys) with synpath.exchange(venue_id) as venue: page = venue.fetch_markets(limit=5) client = synpath.Client() # or one client, routed by the id client.fetch_market("polymarket:2252244") # every id starts with its venue ``` **Order entry** ```python from decimal import Decimal from synpath import KalshiTrading, OrderRequest, Side, load_credentials, require async with KalshiTrading(require("kalshi", load_credentials())) as kalshi: order = await kalshi.create_order(OrderRequest( market_id=market.id, side=Side.BUY, # buy takes YES, sell takes NO quantity=Decimal("10"), price=Decimal("0.42"), # always the YES price )) ``` **Live streams** ```python from synpath import PolymarketMarketStream, BookEvent async with PolymarketMarketStream() as stream: await stream.watch_order_book(["polymarket:2252244"]) # both sides of the market async for event in stream: if isinstance(event, BookEvent): print(event.market_id, event.side, event.best_bid, event.best_ask) ``` **Cross-venue matching: the same market on other venues** ```python synpath.match_market("kalshi:KXHIGHTATL-26SEP23-B80") # .matched: the same question on another venue, or None synpath.match_market("kalshi:KXMLB-26-LAD", venue="polymarket_us") # on one venue in particular synpath.match_event("kalshi:KXHIGHTATL-26SEP23") # each venue's events on the same event ``` Matching covers Kalshi, Polymarket, Polymarket US, Opinion, Hyperliquid, predict.fun, Limitless and SX Bet. It is hosted at `api.synpath.dev` and needs a Synpath API key (`synpath login`, then `synpath keys create`). A match is a deterministic parse both listings land on, not a similarity score, and `side_map` says when one venue asks the question the other way round. Crypto up-or-down markets are never matched, since each venue sets its own price to beat. Browse matched events at [synpath.dev](https://www.synpath.dev); API: [match a market](https://www.synpath.dev/docs/api-reference/matching/market), [match an event](https://www.synpath.dev/docs/api-reference/matching/event). **Smart order routing: one order across Kalshi and Polymarket** ```python from synpath import Bucket, BucketMember, OrderType, PolymarketTrading from synpath.engine import Engine, EngineConfig creds = load_credentials() async with KalshiTrading(require("kalshi", creds)) as kalshi, \ PolymarketTrading(require("polymarket", creds)) as poly, \ Engine({"kalshi": kalshi, "polymarket": poly}, EngineConfig(journal_path="trading.db")) as engine: # The same market on both venues; flip=True where a venue asks the question the other way round bucket = await engine.save_bucket(Bucket(book="alpha", name="Fed cut in December", members=[ BucketMember(market_id="kalshi:KXFEDDECISION-26DEC-C25"), BucketMember(market_id="polymarket:2252244", flip=True), ])) # Both order books combined, each level net of fees, filled from the cheapest price outward await engine.submit(OrderRequest( market_id=bucket.market_id, side=Side.BUY, quantity=Decimal("500"), type=OrderType.MARKET, price=Decimal("0.45"), # the worst price you accept book="alpha", )) ``` **REST server** ```bash python -m synpath.server # http://127.0.0.1:8000/docs curl "localhost:8000/venues/kalshi/markets?limit=1" ``` Trading has its own app, with per-account keys and an append-only audit log: ```bash python -m synpath.server bootstrap --control control.db # the first key python -m synpath.server schema --trading --out openapi.json npx openapi-typescript openapi.json --default-non-nullable false -o src/synpath.d.ts ``` ## Documentation See the [API Reference](https://www.synpath.dev/docs) for detailed documentation and more examples. ## Development ```bash git clone https://github.com/Synpath-ai/synpath cd synpath pip install -e ".[dev]" # builds the Rust core too: needs a Rust toolchain (rustup.rs) pytest # offline, against recorded venue payloads pytest -m live # market data against the real venues pytest -m demo # order entry on a venue's demo environment SYNPATH_PURE_PYTHON=1 pytest # the same tests on the pure-Python fallback cargo test -p synpath-core # the Rust core on its own ``` The Rust core lives in `crates/synpath-core` (pure Rust) and `bindings/python` (its PyO3 wrapper, built as `synpath._core`). Every class it provides has a pure-Python twin with the same interface, and the library uses the twin when the extension is not built. Venue adapters are plain Python: adding a venue needs no Rust. The `demo` tests place and cancel real orders on a demo exchange with the credentials in your environment, and leave the account flat. `python -m synpath.trading doctor` says which venues are configured without printing a secret. ## Prior Art [ccxt](https://github.com/ccxt/ccxt) set the conventions this library follows. ## License MIT