Quickstart
Install Synpath, read a market, place an order, check it and cancel it. Under five minutes.
1. Install the SDK
Python 3.10 or newer. One install covers market data, order entry and live streams.
pip install synpathThen 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 doctor2. 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-laptopWhere 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 underNetworkErrorbecause waiting and retrying is the right response. A write whose queue wait would miss its deadline raisesRateBudgetExceededbefore 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 serverclient = synpath.Client(server="http://127.0.0.1:8000")
