How to build a trading bot for Polymarket
A practical guide to building a prediction-market bot: how to read Polymarket markets, design a safe execution loop, host the worker, and adapt the same architecture to Kalshi and Pariflow.
Before you automate trades
Prediction markets can be illiquid, volatile, restricted by geography, and sensitive to settlement rules. Build read-only first, then paper trade, then use tiny live limits.
- Never store private keys in frontend code.
- Add a manual kill switch from day one.
- Log every skipped and placed order.
Step 1
Understand the Polymarket API layers
A solid Polymarket bot usually separates market discovery from order execution. The Gamma Markets API is useful for finding markets, events, metadata, descriptions, and close dates. The CLOB layer is where a trading bot reads order books, tracks trades, and works with order operations.
Gamma API
Use it to discover markets and enrich your database before deciding what is tradable.
CLOB API
Use it for order book data, prices, trades, order placement, cancels, and account-level execution flows.
WebSocket feeds
Use streaming updates when polling is too slow for your strategy or market-making logic.
Practical rule
Keep API adapters thin. Your strategy should not know whether prices came from Polymarket, Kalshi, or Pariflow. It should receive normalized markets, books, positions, and fills.
Step 2
Design the bot like a small trading system
The common mistake is writing one script that fetches a market, guesses a price, and immediately trades. That works in demos and fails in production. Use a small architecture with explicit boundaries.
Market scanner
Find active markets, filter by liquidity, spread, close time, category, and whether the market is still worth trading.
Signal engine
Estimate a fair probability from your model, external data, or cross-venue prices, then compare it with the live order book.
Risk engine
Cap exposure per market, total daily loss, order size, stale quotes, and repeated cancel-replace loops.
Execution engine
Place, cancel, or skip orders through the venue API. Keep this small, logged, and easy to disable.
State store
Persist markets, orders, fills, positions, signals, and errors so the bot can restart without forgetting risk.
Monitoring
Send health checks and alerts to Slack, Telegram, email, or your dashboard before a small issue becomes expensive.
Step 3
Build the first Polymarket bot
Pick one narrow strategy
Start with one behavior: spread alerts, passive market making, cross-venue monitoring, or position risk alerts. Avoid a bot that tries to trade every category on day one.
Create a worker project
Use TypeScript or Python. TypeScript is convenient if your dashboard is already Next.js, but Python is also common for research. The important part is separating the always-on worker from the public site.
POLYMARKET_PRIVATE_KEY="0x..."
POLYMARKET_FUNDER_ADDRESS="0x..."
POLYMARKET_HOST="https://clob.polymarket.com"
DATABASE_URL="postgresql://..."
BOT_MODE="paper"
MAX_ORDER_USD="25"
MAX_DAILY_LOSS_USD="100"
ALERT_WEBHOOK_URL="https://hooks.slack.com/..."Fetch markets from Gamma
Cache market metadata locally. It gives your bot stable IDs, close times, descriptions, and category context before it touches an order book.
type PolymarketMarket = {
id: string
question: string
slug?: string
active?: boolean
closed?: boolean
endDate?: string
}
export async function findMarkets(query: string) {
const url = new URL("https://gamma-api.polymarket.com/markets")
url.searchParams.set("closed", "false")
url.searchParams.set("limit", "20")
url.searchParams.set("q", query)
const response = await fetch(url)
if (!response.ok) throw new Error(`Gamma API failed: ${response.status}`)
const markets = (await response.json()) as PolymarketMarket[]
return markets.filter((market) => market.active && !market.closed)
}Normalize books and signals
Convert venue-specific data into one internal shape. The strategy should compare your fair value with best bid, best ask, spread, size, and last update time.
Run a risk gate before execution
Every signal should pass a risk gate. Check max order size, total exposure, market age, spread, daily loss, stale data, and whether the same market already has open orders.
type Signal = {
marketId: string
tokenId: string
side: "BUY" | "SELL"
limitPrice: number
sizeUsd: number
reason: string
}
export async function runBotTick() {
const markets = await scanner.findLiquidMarkets()
for (const market of markets) {
const book = await orderbooks.get(market.tokenId)
const signal = await strategy.evaluate({ market, book })
if (!signal) continue
if (!risk.canTrade(signal)) {
await logger.info("signal_skipped", { signal, reason: "risk_limit" })
continue
}
if (process.env.BOT_MODE === "paper") {
await paperBroker.record(signal)
continue
}
await execution.placeLimitOrder(signal)
}
}Paper trade before live mode
Let the bot record hypothetical fills for several days. Compare paper results with realistic fill assumptions, not just mid-price screenshots.
Step 4
Host the bot as an always-on worker
A trading bot is usually a background service, not a web page. The dashboard can live on Vercel, but the bot loop should run somewhere designed for long-lived processes and WebSockets.
VPS or Docker host
Best when you want predictable uptime, direct logs, Docker Compose, and full control over secrets.
Managed workers
Fly.io, Railway, Render workers, Cloud Run jobs, or similar platforms can run a worker plus health checks.
FROM node:22-slim AS base
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
COPY . .
CMD ["node", "dist/worker.js"]services:
polymarket-bot:
build: .
restart: unless-stopped
env_file: .env
depends_on:
- postgres
postgres:
image: postgres:16
restart: unless-stopped
environment:
POSTGRES_PASSWORD: change-me
volumes:
- pgdata:/var/lib/postgresql/data
volumes:
pgdata:Production checklist
- Use a separate wallet or exchange account for the bot.
- Keep private keys out of the repo and out of client-side code.
- Start with read-only data collection before placing any live order.
- Set hard notional limits per market, per category, and per day.
- Log every decision: market, price, model probability, spread, size, and reason.
- Add a kill switch that cancels open orders and stops the worker.
- Respect venue terms, rate limits, geography rules, and local law.
Secrets and safety
Use host-level secrets, not committed files. Rotate keys after testing. Prefer limited-permission API keys when the venue supports them. Keep one account for research and a separate account for live automation.
Venue adapters
Extending the bot to Kalshi and Pariflow
The same system can support multiple prediction market venues if you keep each venue behind an adapter. The scanner, strategy, risk engine, and monitoring stay mostly the same. The authentication, endpoints, order model, and settlement rules change.
Kalshi adapter
Kalshi has official REST and WebSocket API docs, demo and production environments, and its own authentication flow. Build a separate adapter for markets, books, orders, fills, and positions, then reuse your shared risk engine.
Pariflow API, MCP, and CLI
Pariflow-style tooling can expose market search, order book reads, portfolio reads, and quote previews through API, MCP, and CLI workflows. MCP is especially useful for agent research flows, while CLI commands are good for smoke tests, market inspection, and operations.
Adapter contract example
Use one internal interface: searchMarkets(), getOrderBook(), getPositions(), previewOrder(), placeOrder(), and cancelOrder(). Some venues or permission levels may only support read and quote-preview methods.
Bot ideas
Useful bots you can build
Cross-venue odds monitor
Track similar markets across Polymarket, Kalshi, and Pariflow, then alert when probabilities diverge enough to deserve manual review.
Liquidity and spread assistant
Watch wide spreads, estimate a fair midpoint, and post passive orders only when size, spread, and time-to-close fit your limits.
News reaction bot
Subscribe to trusted news feeds or internal research notes, tag affected markets, and create a trade candidate queue instead of firing blindly.
Portfolio hedge bot
Read current positions and reduce concentrated exposure when several markets depend on the same event, team, candidate, or macro variable.
Settlement watchdog
Track markets close to resolution, compare source updates with rule text, and flag markets where final settlement risk is high.
Research copilot
Use an MCP client to search markets, inspect order books, summarize sources, and prepare a trade note for a human operator.
References
API docs and source notes
Pariflow API, MCP, and CLI notes in this guide are based on the current Pariflow product implementation visible in the local project docs, including preview-only CLI quote checks and remote MCP market tools.
FAQ
Common questions
Can I host a Polymarket bot on Vercel?
Vercel is fine for a dashboard or webhook endpoint, but it is not ideal for an always-on trading loop or WebSocket listener. Use a worker host, VPS, container platform, or managed job runner for the bot itself.
Should the bot use REST polling or WebSockets?
Use REST for discovery, reconciliation, and backfills. Use WebSockets when you need low-latency order book or trade updates. Many reliable bots use both.
Can the same bot work on Kalshi?
The architecture can, but the adapter must be venue-specific. Kalshi has its own authentication model, endpoints, product rules, settlement model, and regulated exchange requirements.
Where does Pariflow fit?
Pariflow-style API, MCP, and CLI tooling is useful for market search, order book inspection, quote previews, and agent workflows. Keep trading execution permission separate from read and quote tools.
Build the bot slowly, then automate carefully.
The safest path is read-only scanner, paper trading, tiny live limits, monitoring, and only then more advanced execution logic.
