Polymarket Bot Tutorial · Chapter 3 of 32
Choose your Polymarket bot stack: Python (py-clob-client-v2), Node.js (@polymarket/clob-client-v2 v1.0.6), or Rust (official polymarket_client_sdk_v2 / rs-clob-client-v2). Pros, cons, latency, code samples.
What this chapter covers
The language choice is far less consequential than most builders treat it. Polymarket exposes the same REST and WebSocket endpoints to every language; the SDK choice mostly determines how much glue code you write yourself. Python and Node both have officially maintained SDKs; Rust does not, but is feasible for the hot path. This chapter walks the trade-offs, shows the same "fetch order book" task in each language so the diff is concrete, and ends with a mixed-stack pattern that is what most production bots actually settle on.
- Decision framework
- Python (default choice)
- Node.js (full-stack devs)
- Rust (latency-critical hot path)
- Setup commands per stack
- Code skeleton: fetch order book in 3 languages
- When to mix stacks (Python control plane + Rust hot path)
Decision framework
Three questions resolve 90% of the stack choice.
- Do you have a strong existing skill? If you write Python daily, write the bot in Python. If you write TypeScript daily, write the bot in Node. The SDK quality differences below are real but smaller than the cost of fighting an unfamiliar language.
- Is the strategy latency-critical? If your edge depends on reacting in under 50ms (5-minute crypto markets, market-making during news), the hot path benefits from Rust or Go. Most strategies do not need this.
- Will you run more than one strategy? A single Python process can comfortably manage 10-20 markets. Beyond that, async Node or split-process Python scales better.
The honest default for a first bot is Python. Switch only when a measured constraint forces it.
Python (default choice)
Python is the default because the SDK is the most complete and the iteration loop is fastest. py-clob-client (py-clob-client-v2) covers every CLOB V2 endpoint that matters: market and limit orders, FOK/FAK/GTC variants, order book reads, balance/allowance reads, and direct chain operations through web3.py.
Pros: mature SDK, easy data analysis with pandas, large community, web3.py for on-chain reads. Cons: async ergonomics are awkward compared to JavaScript, GIL limits multi-core speedups (rarely matters for an I/O-bound bot), startup time on cold containers is slow.
Recommended for: 80% of builders. Especially anyone whose strategy involves backtesting, statistical analysis, or any data work alongside execution.
Node.js (full-stack devs)
Node.js is the second-best-supported stack. @polymarket/clob-client-v2 at version 1.0.6 covers the same endpoints as the Python SDK with TypeScript types throughout. Async is native and fast; WebSocket handling is excellent.
Pros: best async story, native TypeScript types, large ecosystem for HTTP + WS, easy deploy on the same Node runtime as a web dashboard. Cons: number precision requires BigInt or string handling for large token IDs (ERC-1155 IDs are 256-bit), pandas-equivalent data tools are weaker.
Recommended for: builders who already maintain a JavaScript stack and want one runtime. Also: anyone building a dashboard alongside the bot - sharing types between the bot core and a Next.js dashboard removes a class of bugs.
Rust (latency-critical hot path)
Rust has no official Polymarket SDK. You build against the V2 REST and WebSocket APIs directly using reqwest (HTTP), tokio-tungstenite (WebSocket), and ethers-rs or alloy for signing. Roughly two days of setup work versus 30 minutes in Python.
Pros: native EIP-712 signing without a JS subprocess, sub-millisecond order construction, deterministic memory profile under load, no GC pauses. Cons: no SDK means you re-implement what Python users get for free (clobToken ID parsing, response schema validation, salt/nonce management). The win in latency is 20-100ms versus tuned Python, which matters only for sub-second strategies.
Recommended for: the hot path of a market-making bot, or the trade-firing leg of a news-arb bot. Almost never the whole bot.
Setup commands per stack
Minimum commands to a working signed order against mainnet (single CLOB v2 endpoint).
Python:
python -m venv venv && source venv/bin/activate
pip install py-clob-client-v2 web3 python-dotenv
# Set POLYGON_RPC, PRIVATE_KEY, POLY_FUNDER in .env
Node:
npm init -y
npm install @polymarket/[email protected] ethers dotenv
# Set POLYGON_RPC, PRIVATE_KEY, POLY_FUNDER in .env
Rust:
cargo new --bin pmt
cd pmt
cargo add tokio reqwest serde serde_json ethers alloy tungstenite tokio-tungstenite
# Hand-write signer + endpoint client; no SDK shortcut.
Time-to-first-order: ~10 min Python, ~15 min Node, ~4-8 hours Rust.
Code skeleton: fetch order book in 3 languages
The same task - fetch the order book for a Polymarket token - in each stack. All three hit the same CLOB v2 REST endpoint.
Python (py-clob-client):
from py_clob_client_v2 import ClobClient
client = ClobClient(host="https://clob.polymarket.com", chain_id=137)
book = client.get_order_book("<token_id>")
print(book.bids[:3], book.asks[:3])
Node (@polymarket/clob-client-v2):
import { ClobClient } from "@polymarket/clob-client-v2";
const c = new ClobClient({ host: "https://clob.polymarket.com", chain: 137 });
const book = await c.getOrderBook("<token_id>");
console.log(book.bids.slice(0,3), book.asks.slice(0,3));
Rust (direct HTTP):
let url = format!("https://clob.polymarket.com/book?token_id={}", token);
let book: serde_json::Value = reqwest::get(&url).await?.json().await?;
println!("{:?} {:?}", &book["bids"][..3], &book["asks"][..3]);
Same response shape in all three. The cost of Rust is everywhere else - signing, order construction, error handling - not in the read path.
When to mix stacks (Python control plane + Rust hot path)
The pattern many production bots converge on: Python for everything that involves decisions, Rust for the millisecond execution leg.
Architecture: a Python process reads market state, runs the strategy logic, and writes a small command file (e.g. {"action":"buy","token":"...","size":10,"price":0.45}) to a Unix socket. A Rust daemon listens on that socket, signs the order, and posts it to the CLOB. The Python process can be slow and convenient; the Rust daemon is fast and minimal.
The handoff is the key: by isolating the signed-order step, the Python crash budget is recovered without sacrificing latency. We use exactly this pattern in our production bots - Python emits intentions, a Node daemon on /tmp/clob.sock handles signing. The Node version of the daemon is fine for sub-100ms; Rust only earns its keep below 50ms.












