All posts

How to Build & Deploy a Polymarket Trading Bot (2026)

July 12, 2026 · PolyCLOB

Polymarket runs a real central limit order book with a public API, which makes it one of the most bot-friendly venues in prediction markets. If you can write a bit of Python, you can connect, stream live prices, and place orders programmatically — no KYC on the international exchange, no scraping, no browser automation hacks.

This guide walks the whole path end to end: what you need, how the pieces fit together, and working py-clob-client code to authenticate, read the order book, place and cancel orders, and run a minimal strategy loop. Then the part most tutorials skip — how to actually deploy it so it runs 24/7 close to the order book, because on a latency-sensitive venue where your bot runs matters as much as what it does. If you're still deciding between venues, see our Polymarket vs Kalshi comparison first; this post assumes you've picked Polymarket.

One disclaimer up front: nothing here is financial advice. The strategy shown is a deliberately naive skeleton to illustrate the mechanics — not a money printer. Trade small until your code is boringly reliable.

What you'll need

Before writing any bot logic, get these in place:

  • A funded wallet. Polymarket settles in USDC on Polygon. You'll need a wallet with a little USDC and a small amount of POL (MATIC) for gas on the approval transactions. Most bot builders use a dedicated wallet, not their main one.
  • API access. On Polymarket's international exchange you don't need KYC to trade via the API — you derive API credentials by signing with your wallet, so your private key is the only "credential" that matters. Keep it in an environment variable, never in code.
  • Python 3.10+ and the official client:
pip install py-clob-client
  • Token allowances (one-time). To trade, the exchange contract needs approval to move your USDC and outcome tokens. This is a one-time on-chain step per wallet; the easiest way is to connect once in the Polymarket web app, or set the allowances directly with web3. Skipping this is the single most common reason a first order silently fails.

That's the whole shopping list. Everything else is code.

How a Polymarket bot works

Polymarket uses a hybrid-decentralized CLOB: orders are matched off-chain by Polymarket's operator for speed, but every order is cryptographically signed by your wallet and settles on-chain on Polygon. You are never handing custody to an API key — the operator can only match orders you signed.

A working bot is really four cooperating parts:

  • Market data — a live view of the order book. You can poll REST, but for anything time-sensitive you want the WebSocket feed so you see book and price changes as they happen.
  • Signal / strategy — the logic that turns market data into an intent: quote a spread, hit a mispriced level, hedge a position.
  • Order manager — the part that signs orders and talks to the REST API to place, replace, and cancel them, handling rejections and retries.
  • Risk & position tracking — inventory limits, max order size, kill-switches, and a running position updated from your fills.

The rest of this guide builds each part in turn. Every outcome (e.g. "Yes" and "No") is a separate ERC-1155 token with its own token_id, and you always trade a specific token — so the first thing your bot needs is the token ID of the market you're targeting.

Setup: auth and your first API call

Instantiate the client with your wallet key, derive your API credentials, then make a couple of read-only calls to prove the connection works. Public reads (markets, order books) need no signing; only order placement does.

import os
from py_clob_client.client import ClobClient

HOST = "https://clob.polymarket.com"
CHAIN_ID = 137  # Polygon

client = ClobClient(
    host=HOST,
    key=os.environ["PK"],   # the wallet that signs orders — load from env, never hardcode
    chain_id=CHAIN_ID,
    signature_type=0,       # 0 = EOA. For a browser/proxy wallet use 2 + funder="<proxy-address>"
)

# Derive (or create) your L2 API credentials by signing once with the wallet above.
client.set_api_creds(client.create_or_derive_api_creds())

# Read-only calls — no signing required for public data.
markets = client.get_simplified_markets()
book = client.get_order_book(token_id=os.environ["TOKEN_ID"])
print("best bid:", book.bids[0].price, "best ask:", book.asks[0].price)

A note on signature_type: use 0 for a standard EOA wallet whose key you hold. If you're trading through a Polymarket proxy/browser wallet, pass signature_type=2 (or 1 for an email/Magic wallet) and the funder address that actually holds the USDC. Getting this wrong is why orders sign fine but never fund. For a deeper tour of the endpoints, see our Polymarket CLOB API guide (coming soon).

Finding the token you'll trade

Every order targets a specific outcome token, so you need its token_id. The get_simplified_markets() call above returns markets with their token IDs, but the easiest way to find a specific market is Polymarket's Gamma API, which powers market discovery and metadata and needs no auth:

import requests

resp = requests.get(
    "https://gamma-api.polymarket.com/markets",
    params={"active": "true", "closed": "false", "limit": 5},
)
for m in resp.json():
    print(m["question"], "->", m.get("clobTokenIds"))

Each binary market has two outcome tokens (for example "Yes" and "No"), each with its own ID. Pick the side you want to quote and use its ID everywhere the snippets below reference TOKEN_ID. The read-only Gamma and Data APIs are perfect for building watchlists and scanning for liquidity before you commit a single order.

Streaming the order book over WebSocket

Polling get_order_book in a loop works for a slow strategy, but it's wasteful and always a step behind. The market channel pushes a book snapshot on connect and price_change deltas as the book moves — that's what you want feeding a live strategy.

import asyncio, json
import websockets

WS_URL = "wss://ws-subscriber.polymarket.com/ws/market"  # VERIFY endpoint before relying on it

async def stream_book(token_ids):
    async with websockets.connect(WS_URL) as ws:
        # Subscribe to the public market channel for one or more token IDs.
        await ws.send(json.dumps({"assets_ids": token_ids, "type": "market"}))
        async for raw in ws:
            msg = json.loads(raw)
            if msg.get("event_type") == "book":
                print("snapshot", msg["market"])
            elif msg.get("event_type") == "price_change":
                print("delta", msg["changes"])

asyncio.run(stream_book([os.environ["TOKEN_ID"]]))

Keep the exact endpoint and message shape honest — verify them against the current CLOB docs before you trust real money to them, and always handle reconnects: WebSocket connections drop, and a market maker that goes blind while its quotes rest on the book is asking to be picked off.

Placing and cancelling orders

Here's the core loop of any bot: build an order, sign it locally, post it, and cancel it. Prices are the implied probability between 0 and 1; size is the number of shares (contracts).

from py_clob_client.clob_types import OrderArgs, OrderType
from py_clob_client.order_builder.constants import BUY

# A resting limit buy: 5 shares at $0.40 (i.e. a 40% implied-probability bid).
order = OrderArgs(
    token_id=os.environ["TOKEN_ID"],
    price=0.40,
    size=5.0,
    side=BUY,
)
signed = client.create_order(order)               # signs the EIP-712 order locally
resp = client.post_order(signed, OrderType.GTC)   # GTC = rest on the book
order_id = resp["orderID"]

# Later — pull that specific order, or clear everything:
client.cancel(order_id)
# client.cancel_all()

The OrderType you pass decides how the order lives:

  • GTC (good-till-cancelled) — rests until filled or cancelled. The workhorse for maker strategies.
  • GTD (good-till-date) — rests until an expiration you set.
  • FOK (fill-or-kill) — fills the whole size immediately or cancels entirely.
  • FAK (fill-and-kill) — fills whatever it can immediately, cancels the rest.

Takers reach for FOK/FAK; makers live on GTC/GTD. Match the order type to what your strategy actually needs, and always confirm the response — a post_order that comes back without an orderID didn't rest.

A minimal strategy loop

Now wire the pieces into a loop. This is a naive two-sided market maker — it quotes a fixed spread around the midpoint and caps its inventory. It exists to show the shape of a bot, not to be traded as-is. It's the same maker-side idea that makes Polymarket's fee model attractive: makers pay no fee and can earn rebates and liquidity rewards for providing liquidity.

import time
from py_clob_client.clob_types import OrderArgs, OrderType
from py_clob_client.order_builder.constants import BUY, SELL

TOKEN_ID = os.environ["TOKEN_ID"]
HALF_SPREAD = 0.02     # quote 2 cents either side of the mid
ORDER_SIZE = 5.0       # shares per side
MAX_POSITION = 50.0    # hard inventory cap (shares)

position = 0.0  # NOTE: in a real bot, update this from your fills (user channel / get_trades)

def refresh_quotes(client):
    client.cancel_all()  # clear stale quotes each cycle

    mid = float(client.get_midpoint(TOKEN_ID)["mid"])
    bid = round(mid - HALF_SPREAD, 2)
    ask = round(mid + HALF_SPREAD, 2)

    # Skew off: stop adding to a side once the inventory cap is hit.
    if position < MAX_POSITION:
        buy = client.create_order(OrderArgs(token_id=TOKEN_ID, price=bid, size=ORDER_SIZE, side=BUY))
        client.post_order(buy, OrderType.GTC)
    if position > -MAX_POSITION:
        sell = client.create_order(OrderArgs(token_id=TOKEN_ID, price=ask, size=ORDER_SIZE, side=SELL))
        client.post_order(sell, OrderType.GTC)

while True:
    try:
        refresh_quotes(client)
    except Exception as e:
        print("cycle error:", e)
    time.sleep(5)

Even this toy has the bones of the real thing: cancel-and-replace each cycle, a midpoint reference, and an inventory guardrail. What it's missing is what separates a demo from a live bot — real position tracking from fills, adverse-selection protection, and tighter, faster requoting. And a five-second sleep on a home connection is exactly the kind of slowness that gets a maker run over, which brings us to the part that actually determines whether this is viable.

Running it 24/7: hosting, latency, and deployment

A trading bot is only useful while it's running. The moment your laptop sleeps, your Wi-Fi hiccups, or your ISP reassigns your IP, your quotes are stale and your positions are unmanaged. Two things decide whether a bot survives contact with a live market: uptime and latency.

Why a laptop or home connection is the wrong home

Running from home means every request crosses your ISP, the public internet, and a long geographic path before it reaches the exchange. For a slow, low-frequency strategy that might be tolerable. For anything reactive — market making, news-driven repricing, short-duration crypto markets — it's fatal: when odds move, the orders closest to the matching engine fill first at the best prices, and a maker who's a few hundred milliseconds behind gets adversely selected, filled only on the trades that were about to go against them.

Host close to the matching engine

Polymarket's core trading infrastructure runs in AWS eu-west-2 (London). The single highest-leverage infrastructure decision you can make is to run your bot in or near that region — an EU-hosted server shortens the round-trip to the order book dramatically compared with a US home connection. (Keep any specific millisecond claims honest and measured for your own setup; the point is the direction, and it's large. For real numbers, see our server-location and latency guide, coming soon.) Polymarket even introduced dynamic taker fees on its 15-minute crypto markets specifically because latency arbitrage there was so dominant — proof that proximity is a real, priced edge.

The rest of "production"

Co-location is necessary but not sufficient. A bot you can leave running also needs:

  • A supervised process — run under systemd, a container restart policy, or a process manager so a crash restarts automatically instead of going dark.
  • Secrets management — the wallet key lives in an environment variable or secret store, never in the image or repo.
  • Monitoring and a kill-switch — heartbeat logs, alerts on disconnects, and a way to flatten positions and stop instantly.
  • Reconnect logic — every WebSocket and REST call must assume the network will fail and recover gracefully.

This is exactly the gap PolyCLOB is built to close: a low-latency, no-KYC container runtime that puts your bot next to Polymarket's CLOB on our EU edge, with git-based deploys, auto-restarts, and secrets handled — so you skip the VPS provisioning, region tuning, and process babysitting and just ship your strategy. See the features for specifics.

Common pitfalls and safety

The bugs that cost real money are rarely in your strategy — they're in the plumbing:

  • Forgotten allowances. If you never approved the exchange to move your USDC and outcome tokens, orders will fail or never fund. Do the one-time approval first.
  • Rate limits. The API enforces per-endpoint limits. A cancel-and-replace loop that's too aggressive will get throttled; batch and back off.
  • No idempotency / double-fills. Assume any request can time out after the server accepted it. Reconcile against get_orders rather than trusting local state.
  • Untracked position. The skeleton above never updates position from fills — a real bot must, or its inventory caps are fiction.
  • Leaked keys. A wallet key in a public repo or a log line is a drained wallet. Use env vars and a dedicated, minimally-funded wallet.
  • Thin markets. Backtests and demos assume fills; long-tail Polymarket markets have wide spreads and little depth. Size to the book you're actually trading.

Start on one small, liquid market with tiny size, confirm every order and cancel behaves, and only then scale. If your strategy is arbitrage or pure market making, the mechanics above are the same but the latency stakes are higher — see Polymarket arbitrage bots and market making on Polymarket (both coming soon).

FAQ

Can you use a trading bot on Polymarket?

Yes. Polymarket exposes a public CLOB API (REST + WebSocket) with official Python, TypeScript, and Rust clients, so automated trading is fully supported and widely used. You sign orders with your own wallet and submit them programmatically.

Do you need KYC to use the Polymarket API?

On Polymarket's international exchange you can generate API credentials and trade programmatically with no KYC — you derive an API key by signing with your wallet. The separate CFTC-regulated US exchange requires full KYC.

What is the best language for a Polymarket bot?

Python is the most common choice via the official py-clob-client library, but Polymarket also ships official TypeScript and Rust clients. Pick whichever fits your existing stack and latency requirements.

Where should I host a Polymarket trading bot?

Close to Polymarket's matching engine, which runs in AWS eu-west-2 (London). An EU-hosted server minimizes round-trip latency to the order book; a home connection is the worst option for anything time-sensitive.

Is automated trading allowed on Polymarket?

Yes — the CLOB API exists specifically for programmatic trading, and running bots within the published rate limits and terms is expected. This is not financial or legal advice; review the current terms yourself.

How much money do I need to start a Polymarket bot?

You can test with a few dollars of USDC on small, liquid markets. Start tiny to validate order placement, cancellation, and risk logic before scaling size — early on the goal is correctness, not returns.