A useful first prediction-market bot is an observer. It fetches a small set of markets, records what it saw, and tells you when a request failed. It does not need permission to spend money.
Kalshi's public market-data guide documents unauthenticated endpoints for series, events, markets, and order books. Start there before adding the authentication and order-management code required for account-specific actions.
Define the first version
Give the project a narrow job: list a few open contracts, their tickers, and the public price fields returned by the API. Keep a timestamp with each observation. Do not add a trade endpoint, an exchange login, or a private key to this version.
This boundary makes debugging easier. If a request fails, you can investigate the endpoint, response, and data shape without wondering whether an order was placed.
Fetch a small set of markets
This example uses Python's standard library. It reads public data and has no order-placement function. The API root follows Kalshi's current market-data quick start; check that documentation when you revisit the project.
import json
from datetime import datetime, timezone
from urllib.parse import urlencode
from urllib.request import urlopen
base = "https://external-api.kalshi.com/trade-api/v2"
query = urlencode({"status": "open", "limit": 5})
with urlopen(f"{base}/markets?{query}", timeout=15) as response:
payload = json.load(response)
observed_at = datetime.now(timezone.utc).isoformat()
markets = payload.get("markets", [])
for market in markets:
print(json.dumps({
"observed_at": observed_at,
"ticker": market.get("ticker"),
"title": market.get("title"),
"yes_bid": market.get("yes_bid_dollars"),
"yes_ask": market.get("yes_ask_dollars"),
}))
if not markets:
print("No markets returned; do not invent a quote.")
A market ticker identifies a particular contract. An event can contain multiple related markets; a series groups recurring events. Keep those identifiers separate in your records instead of joining data on a display title.
Make the data usable
Before adding a strategy, inspect the response you actually receive. A missing field is different from a zero price. Preserve decimal strings when storing prices, and convert them deliberately when doing arithmetic. Do not assume an old example using integer cents still matches every current endpoint.
For a dashboard, store the response time, market identifier, bid, ask, and any status needed to distinguish an open market from a closed one. Reading one page is not a complete market universe: implement documented pagination if your project needs all matching contracts.
A last-traded price is not a promise that you can trade your desired size there. Your next useful exercise is to read an order book, not to rank apparent “edges” from the last price alone.
Handle the boring failures
Set a request timeout. Treat a non-successful HTTP response as an error rather than an empty market list. If a network call fails, retain the failure timestamp and avoid presenting an older observation as fresh.
Use bounded retries for recoverable reads, with backoff and the platform's current rate-limit guidance. Keep a visible stale-data state. A loop that prints the same old quote forever can look healthy while making every later conclusion unreliable.
Keep a separate path to execution
An observer does not prove that authenticated requests work. Kalshi's signed-request guide describes separate headers and signing rules, including the full request path and a millisecond timestamp. Demo and production credentials are separate.
If you later build order execution, make it a different milestone with explicit controls and tests. Paper trading can help test decision logging, but hypothetical fills are still hypotheses. A bot that reads a market correctly has not established a trading advantage.