Synpath
Getting started

Quickstart

Install Synpath, read a market, place an order, check it and cancel it. Under five minutes.

Market data needs no key at all. Only steps 4 to 6 need exchange credentials, and they run against Kalshi's demo environment, so nothing here risks real money.

1. Install the SDK

Python 3.10 or newer. One install covers market data, order entry and live streams.

pip install synpath

Then add your venue keys. synpath init asks for them one venue at a time and writes them to a .env file only you can read; synpath doctor confirms what loads, without printing a secret. Market data needs no keys, so you can skip this until you trade.

synpath init
synpath doctor

2. Get an API key

synpath login opens Google sign-in in your browser and returns to the terminal. It saves a short-lived session for managing data API keys; signing in does not create a key. Then create a key, labelled for the device that will use it. The Python client reads the saved key automatically.

synpath login
synpath keys create my-laptop

Where the key is saved, and how to list or revoke keys, is on the Authentication page.

3. Make your first request

Pick a venue by name and search its markets. Every venue answers with the same Market shape, so the code below runs unchanged on Polymarket by changing the name.

import synpath

kalshi = synpath.exchange("kalshi")     # "kalshi", "polymarket", or "polymarket_us"

# Search the open markets on Kalshi. `status` defaults to open,
# so everything returned is still tradeable.
markets = kalshi.fetch_markets(query="bitcoin", limit=5)
for market in markets:
    print(f"ID: {market.id}, Title: {market.title}")     # ID: kalshi:KXBTCD-26SEP..., Title: ...
    print(f"  yes bid/ask: {market.yes.quote.bid} / {market.yes.quote.ask}")

# The book, best price first on both sides; side="no" for what NO costs
book = kalshi.fetch_order_book(markets[0].id, depth=5)
print(book.best_bid, book.best_ask)

market.id is the Synpath ID you will trade with: the venue's name, a colon, and the venue's own id. Quotes are None, not 0, when a venue has published nothing, and every number is labelled with what it measures.

4. Place your first order

Open a trading client for the venue with the credentials from step 1, then send an OrderRequest. The price below is far from the market so the order rests while you try the next two steps.

from synpath import KalshiTrading, OrderRequest

async with KalshiTrading(creds) as kalshi:
    order = await kalshi.create_order(OrderRequest(
        market_id=market.id,                # the Synpath ID from step 3
        side="buy",                         # buy takes YES, sell takes NO
        type="limit",                       # limit or market
        time_in_force="gtc",                # gtc, ioc, fok, or gtd
        price="0.05",                       # the YES price, 0 to 1
        amount=5,                           # number of contracts
    ))
    print(f"Order placed: {order.id}")

Order entry is async, so the calls are awaited inside an async with block. Steps 5 and 6 run inside the same block.

5. Check order status

Read the order back by its id. One status, with filled and remaining kept separate.

order = await kalshi.fetch_order(order.id)
print(order.status)      # OrderStatus.OPEN
print(order.filled, order.remaining)

# Everything resting on the account
for o in await kalshi.fetch_orders(status="open"):
    print(o.id, o.market_id, o.side, o.price, o.remaining)

Kalshi's order store can answer 404 for a few hundred milliseconds after a placement. fetch_orderretries a miss briefly, so you do not have to.

6. Cancel an order

cancelled = await kalshi.cancel_order(order.id, market_id=order.market_id)
print(cancelled.status)  # OrderStatus.CANCELED; whatever matched first is in .filled

# Or clear one market, which returns how many were cancelled
count = await kalshi.cancel_all_orders(market_id=order.market_id)

Cancels take the fast lane through the rate limiter and go at once, ahead of any reads already waiting.

7. Rate limits

Rate limiting is built in and shared across the whole process, because a venue counts requests per account and IP, not per client object. You do not configure it for reads.

  • Reads are paced conservatively per venue. Kalshi does not document its public read limits, so Synpath keeps to a few requests a second by default.
  • Writes are metered on a token budget the way Kalshi meters them: on the basic tier, 200 read and 100 write tokens a second, and an order costs 10. Call fetch_limits() once after connecting to adopt your account's real tier.
  • When a limit is hit, a venue's 429 becomes RateLimitExceeded, which sits under NetworkError because waiting and retrying is the right response. A write whose queue wait would miss its deadline raises RateBudgetExceeded before anything is sent.

8. Complex order types

  • Plain orders, everything above, go straight from your script to the venue.
  • Stops, icebergs, TWAPs and bucket orders keep working after your script ends, so they run on a self-hosted server, synpath serve, that comes with the same install.
  • To use them, start the server and point the client at it. The calls are the same as above.
synpath serve          # start your self-hosted server
client = synpath.Client(server="http://127.0.0.1:8000")
The server runs on your own machine, uses your venue keys, and must be running for these orders to work. Every order type is in the Python SDK.

Where to next