How to Build a Polymarket Trading Bot with Real-Time Data

Step-by-step guide to building a Polymarket trading bot. Real-time odds monitoring, market scanning, and signal detection with Python code examples.

Dark futuristic banner for “How to Build a Polymarket Trading Bot with Real-Time Data,” showing a friendly robot using a laptop with the Polymarket logo above a colorful real-time trading chart.

Prediction markets move on news. An announcement drops, a poll lands, a game ends, and odds reprice in minutes. If you are watching manually, you catch a fraction of it. A bot can watch hundreds of markets at once, detect moves the moment they happen, and act on rules you Kamino in advance.

This guide walks through building a Polymarket bot from the ground up: the data pipeline, the market scanner, the signal detector, and a complete working script that runs in paper-trading mode. The focus is the data and decision layer, because that is what separates bots that work from bots that guess.

Disclaimer: this is an educational tutorial about software architecture, not financial advice. Prediction market trading involves real risk of loss. Start in paper-trading mode, and only ever trade money you can afford to lose.

Architecture Overview

Every serious trading bot, whatever the market, separates into three layers:

Elgon API (data layer)
    |-- REST: market discovery, current odds, odds history
    |-- WebSocket: real-time trade stream
         |
    Bot Engine
    |-- Market scanner (find markets worth watching)
    |-- Signal detector (spot odds moves and volume spikes)
    |-- Risk manager (position limits, exposure caps)
         |
    Execution Layer
    |-- Polymarket CLOB API (place orders)
    |-- Wallet management (USDC on Polygon)

The data layer is Elgon, which indexes Polymarket (and Kalshi) into one GraphQL schema with rankings, windowed stats, and odds history that Polymarket's native API does not provide. The execution layer is Polymarket's own CLOB API, the only way to place orders. Keeping them separate means you can iterate on strategy without touching plumbing.

This guide builds the data and decision layers. Note that Elgon's prediction market endpoints require a Growth plan.

Setting Up the Data Layer

All data access is GraphQL over HTTP. One small helper covers everything:

import requests

ELGON_API_KEY = "YOUR_API_KEY"
ELGON_URL = "https://graph.elgon.io/graphql"

def elgon(query, variables=None):
    resp = requests.post(
        ELGON_URL,
        headers={"Authorization": ELGON_API_KEY, "Content-Type": "application/json"},
        json={"query": query, "variables": variables or {}},
    )
    resp.raise_for_status()
    payload = resp.json()
    if "errors" in payload:
        raise RuntimeError(payload["errors"])
    return payload["data"]

The Market Scanner: Finding Markets Worth Watching

Not every market is tradable. Thin markets have spreads so wide that being right still loses money. The scanner's job is to filter the universe down to markets with real liquidity and activity:

SCAN = """
query ScanMarkets($minVolume: Float!) {
  filterPredictionMarkets(
    filters: {
      protocol: [POLYMARKET]
      status: [OPEN]
      volumeUsd24h: { gte: $minVolume }
    }
    rankings: [{ attribute: volumeUsd24h, direction: DESC }]
    limit: 50
  ) {
    results {
      id
      eventLabel
      market { question closesAt }
      outcome0 { label lastPriceUsd }
      outcome1 { label lastPriceUsd }
      volumeUsd24h
      liquidityUsd
      uniqueTraders24h
    }
  }
}
"""

def scan_markets(min_volume_usd=10_000):
    data = elgon(SCAN, {"minVolume": min_volume_usd})
    return data["filterPredictionMarkets"]["results"]

A few scanner refinements that pay off quickly:

  • Filter mid-range probabilities. Markets priced at 2% or 97% have little room to move. The interesting range for momentum strategies is roughly 15% to 85%.
  • Rank by trending score (trendingScore1h) instead of volume to surface markets that are moving right now rather than markets that are merely big.
  • Mind the close date. A market closing in an hour behaves very differently from one with a month left.

The Signal Detector: Spotting Odds Movements

The simplest robust signal is an odds delta over a time window: did the YES probability move more than N points in the last hour, and was there real volume behind the move? predictionMarketBars answers both in one query:

import time

BARS = """
query RecentBars($marketId: String!, $from: Int!, $to: Int!) {
  predictionMarketBars(input: {
    marketId: $marketId
    from: $from
    to: $to
    resolution: min15
  }) {
    bars {
      t
      volumeUsd
      trades
      outcome0 { priceUsd { o c } }
    }
  }
}
"""

def detect_move(market_id, lookback_hours=1, threshold=0.05):
    """Return a signal dict if YES moved more than `threshold` over the lookback window."""
    now = int(time.time())
    data = elgon(BARS, {"marketId": market_id, "from": now - lookback_hours * 3600, "to": now})
    bars = [b for b in data["predictionMarketBars"]["bars"] if b["outcome0"]["priceUsd"]["c"]]
    if len(bars) < 2:
        return None

    start = float(bars[0]["outcome0"]["priceUsd"]["o"])
    end = float(bars[-1]["outcome0"]["priceUsd"]["c"])
    move = end - start
    volume = sum(float(b["volumeUsd"] or 0) for b in bars)

    if abs(move) >= threshold and volume > 1_000:
        return {"market_id": market_id, "move": move, "from": start, "to": end, "volume": volume}
    return None

For sub-minute reaction times, the onPredictionTradesCreated WebSocket subscription streams every trade as it is ingested, so you process flow instead of polling. The TypeScript SDK pattern is in our Polymarket API guide; the polling version above is plenty for strategies operating on minutes rather than milliseconds.

Decision Logic: Three Starter Strategies

The signal is an input; the strategy decides what it means. Three classic shapes, simplest first (educational examples, not financial advice):

  • Momentum. A large move on heavy volume tends to continue as information diffuses. Rule: if YES moved more than 5 points in an hour on meaningful volume, follow the direction with a small position.
  • Mean reversion. A large move on thin volume is often one impatient trader, not news. Rule: if YES spiked more than 8 points on low volume and there is no corroborating move in related markets, fade it.
  • Cross-venue divergence. The same event often trades on Kalshi at a different probability. If Polymarket says 60% and Kalshi says 53%, someone is wrong. Elgon serves both venues in one schema, so the comparison is a single query away; our Kalshi API guide shows the cross-venue search pattern. Copy trading is a fourth shape worth knowing: filterPredictionTraders and predictionTraderHoldings expose trader PnL and positions, letting you track wallets with strong records and mirror their entries.

Whatever you pick: paper trade first, log every signal and what happened next, and only consider real execution once the log says the rules have an edge.

The Complete Bot (Paper-Trading Mode)

Putting it together: scan, detect, decide, log. This runs as-is with a Growth-plan API key, about 90 lines:

"""Polymarket momentum scanner bot. Paper trading only: it logs signals, it does not trade."""
import time
import requests

ELGON_API_KEY = "YOUR_API_KEY"
ELGON_URL = "https://graph.elgon.io/graphql"

SCAN_INTERVAL = 300 # re-scan the market universe every 5 minutes
MOVE_THRESHOLD = 0.05 # 5 probability points
LOOKBACK_HOURS = 1
MIN_VOLUME_24H = 10_000 # USD

SCAN = """
query Scan($minVolume: Float!) {
  filterPredictionMarkets(
    filters: { protocol: [POLYMARKET], status: [OPEN], volumeUsd24h: { gte: $minVolume } }
    rankings: [{ attribute: volumeUsd24h, direction: DESC }]
    limit: 50
  ) {
    results {
      id
      market { question }
      outcome0 { label lastPriceUsd }
    }
  }
}
"""

BARS = """
query Bars($marketId: String!, $from: Int!, $to: Int!) {
  predictionMarketBars(input: { marketId: $marketId, from: $from, to: $to, resolution: min15 }) {
    bars { t volumeUsd outcome0 { priceUsd { o c } } }
  }
}
"""

def elgon(query, variables=None):
    resp = requests.post(
        ELGON_URL,
        headers={"Authorization": ELGON_API_KEY, "Content-Type": "application/json"},
        json={"query": query, "variables": variables or {}},
    )
    resp.raise_for_status()
    payload = resp.json()
    if "errors" in payload:
        raise RuntimeError(payload["errors"])
    return payload["data"]

def scan():
    data = elgon(SCAN, {"minVolume": MIN_VOLUME_24H})
    return data["filterPredictionMarkets"]["results"]

def detect(market_id):
    now = int(time.time())
    data = elgon(BARS, {"marketId": market_id, "from": now - LOOKBACK_HOURS * 3600, "to": now})
    bars = [b for b in data["predictionMarketBars"]["bars"] if b["outcome0"]["priceUsd"]["c"]]
    if len(bars) < 2:
        return None
    start = float(bars[0]["outcome0"]["priceUsd"]["o"])
    end = float(bars[-1]["outcome0"]["priceUsd"]["c"])
    if 0.15 <= end <= 0.85 and abs(end - start) >= MOVE_THRESHOLD:
        return {"from": start, "to": end, "volume": sum(float(b["volumeUsd"] or 0) for b in bars)}
    return None

def run():
    print("Polymarket momentum scanner running (paper trading)...")
    seen = {}
    while True:
        for m in scan():
            signal = detect(m["id"])
            if signal and seen.get(m["id"]) != round(signal["to"], 2):
                seen[m["id"]] = round(signal["to"], 2)
                direction = "UP" if signal["to"] > signal["from"] else "DOWN"
                print(
                    f"[SIGNAL {direction}] {m['market']['question'][:60]} | "
                    f"YES {signal['from']:.0%} -> {signal['to']:.0%} | "
                    f"${signal['volume']:,.0f} vol in {LOOKBACK_HOURS}h | "
                    f"PAPER TRADE: {'BUY YES' if direction == 'UP' else 'BUY NO'}"
                )
            time.sleep(0.5)
        time.sleep(SCAN_INTERVAL)

if __name__ == "__main__":
    run()

Run it and you get a live feed of markets where the odds are actually moving, with the paper trade your momentum rule would have made. That log is your backtest: a week of it tells you more about whether the strategy works than any amount of theorizing.

Execution: Placing Real Trades

When the paper log earns it, execution goes through Polymarket's CLOB API with the official py-clob-client library. That side involves a Polygon wallet, USDC, token approvals, and order management, and it is well covered in Polymarket's developer docs. Two pieces of hard-earned advice:

  • Keep execution behind an interface. A single place_order(market, side, size) function lets you swap paper mode for live mode with one flag, and unit test everything above it.
  • Add a risk manager before adding money. Max position size, max total exposure, max trades per hour, and a kill switch. Bots fail in bursts; caps turn a bad hour into a small loss instead of an account.

Next Steps

  1. Get the data layer running. Connect wallet for Elgon (prediction market endpoints are on the Growth plan) and run the scanner above.
  2. Tune the signal. Try trendingScore1h ranking, tighter thresholds, or volume-weighted moves. Log everything.
  3. Add cross-venue checks. The same query with protocol: [POLYMARKET, KALSHI] turns the bot into a divergence monitor.
  4. Backtest with history. predictionMarketBars goes back over a market's whole life at resolutions down to 1 minute, which is enough to replay your rules against past events.
  5. Graduate carefully. Wire in py-clob-client, start with small size, and keep the paper log running beside the live one.

The general principles of bot data infrastructure (WebSocket vs polling, local state, separating data from strategy) are covered in our crypto trading bot data guide; everything there applies to prediction markets too.

FAQ

How do you build a Polymarket trading bot?

Three layers: a data layer that watches markets and odds (the Elgon API provides ranked market scans, OHLC odds history, and real-time trade streams), a decision layer that turns that data into signals (momentum, mean reversion, cross-venue divergence, copy trading), and an execution layer that places orders through Polymarket's CLOB API. Build and validate the first two in paper-trading mode before touching the third.

Are Polymarket bots profitable?

Some are, most are not, and nobody can promise an edge. Profitable bots typically exploit a structural advantage: faster reaction to information, cross-venue price differences, or providing liquidity. The honest path is to paper trade, log every signal, and measure whether your rule beats doing nothing after fees and slippage. This article is educational and not financial advice.

Can you use bots on Polymarket?

Yes. Polymarket supports programmatic trading through its CLOB API and publishes official client libraries. Automated market makers and traders are a normal part of the venue's liquidity. You are responsible for complying with Polymarket's terms and the laws of your jurisdiction.

What programming language should I use for a Polymarket bot?

Python is the most common choice: Polymarket's official py-clob-client is Python, and the data analysis ecosystem (pandas, numpy) suits backtesting. The Elgon data layer is plain GraphQL over HTTP, so it works identically from Python, TypeScript, Go, or anything else. TypeScript is a strong alternative if you want the Elgon SDK's built-in WebSocket handling.

How do I get real-time Polymarket data for a bot?

Use the Elgon API: filterPredictionMarkets for ranked scans with current odds, predictionMarketBars for odds history down to 1-minute resolution, and the onPredictionTradesCreated WebSocket subscription for a live trade stream filterable by market, event, or trader. Prediction market endpoints require a Growth plan. See our Polymarket API guide for the full data walkthrough.


Ready to build? Get your Elgon API key and start with the scanner script in this guide.