DEV Community

Cover image for Polymarket API Explained for Developers: CLOB, Gamma, Data & WebSockets
Bo$onaX
Bo$onaX

Posted on

Polymarket API Explained for Developers: CLOB, Gamma, Data & WebSockets

Polymarket API Explained: CLOB, Gamma, Data & WebSockets

Learn the Polymarket API from a developer’s perspective: Gamma, CLOB, Data API, WebSockets, authentication, Python SDKs, rate limits and bots.

Polymarket API Explained for Developers

If you are building a Polymarket trading bot, analytics system, market scanner, dashboard, or automated execution engine, the difficult part is not sending an HTTP request.

The difficult part is understanding which Polymarket API should own which responsibility.

Polymarket currently exposes several developer surfaces: Gamma for market discovery and metadata, the CLOB API for prices, order books and trading, the Data API for positions and historical activity, and WebSocket channels for real-time updates. The official documentation also provides SDKs for interacting with these systems.

There is another important reason to understand the architecture before writing a bot: Polymarket's production trading stack changed substantially in 2026. CLOB V2 went live on the production CLOB host in April 2026, and legacy V1 SDKs and V1-signed orders are no longer supported in production.

This guide builds a developer-oriented mental model of the current Polymarket API, shows how the different services fit together, demonstrates Python integration patterns, and explains what changes when you move from a research script to a production trading system.

Technical note: Polymarket's APIs evolve. Endpoint names, SDK surfaces, limits, and authentication requirements should always be checked against the current official documentation before deploying a live trading system.


What You'll Learn

By the end of this guide, you will understand:

  • How the Polymarket API architecture is divided
  • When to use Gamma API versus CLOB API versus Data API
  • How market IDs, condition IDs, and token IDs relate
  • How to retrieve market metadata with Python
  • How to inspect prices and order books
  • How Polymarket WebSockets differ from REST polling
  • How CLOB authentication works
  • The distinction between L1 wallet authentication and L2 API credentials
  • How the current CLOB V2 architecture affects integrations
  • How to design retries, rate-limit handling, and reconnect logic
  • How to structure a production trading bot around the API
  • What to log and monitor
  • Which security mistakes can turn a bot into a liability

1. The Polymarket API Architecture

The first mistake developers make is thinking of the Polymarket API as one REST API.

It is better to think of it as a collection of services with different jobs.

At a high level:

                         Your Application
                                |
              +-----------------+-----------------+
              |                 |                 |
              v                 v                 v
        Gamma API           CLOB API          Data API
        Discovery        Prices / Books      Positions /
        Metadata          Trading            Analytics
              |                 |                 |
              +-----------------+-----------------+
                                |
                           WebSocket
                         Real-time events
                                |
                                v
                       Trading / Strategy
                                |
                                v
                         Polygon settlement
Enter fullscreen mode Exit fullscreen mode

Polymarket's official market-data documentation describes three primary REST surfaces:

API Primary responsibility Typical use
Gamma API Events, markets and discovery Find markets and metadata
CLOB API Prices, order books and trading Trading and execution
Data API Positions, trades and analytics Portfolio and historical analysis
WebSocket Streaming market/user events Real-time systems

The official documentation explicitly separates Gamma, CLOB and Data API functionality in this way.

That separation should also appear in your application architecture.

A trading bot should generally not use the same request loop for market discovery, order-book updates, account reconciliation, and order execution.


2. Gamma API: Market Discovery and Metadata

The Gamma API is where you typically start.

Its production base URL is:

https://gamma-api.polymarket.com
Enter fullscreen mode Exit fullscreen mode

Gamma exposes market and event discovery endpoints such as:

GET /events
GET /events/{id}
GET /markets
GET /markets/{id}
GET /public-search
GET /tags
GET /series
GET /sports
GET /teams
Enter fullscreen mode Exit fullscreen mode

These endpoints are public market-data endpoints and do not require wallet authentication.

For example, retrieving a market by ID uses:

GET https://gamma-api.polymarket.com/markets/{id}
Enter fullscreen mode Exit fullscreen mode

and the response contains metadata including the question, condition ID, slug, dates, category and other market attributes.

Why Gamma matters to a trading bot

Suppose your strategy trades only:

  • active markets,
  • markets closing within a particular time window,
  • markets containing a specific category,
  • markets with sufficient liquidity,
  • or markets matching a particular question pattern.

You should solve that filtering problem before your execution engine ever considers placing an order.

A useful architecture is:

Gamma
  |
  | market discovery
  v
Market Registry
  |
  | eligible token IDs
  v
Strategy Engine
  |
  v
CLOB
Enter fullscreen mode Exit fullscreen mode

This avoids repeatedly querying metadata during latency-sensitive execution.


3. Market IDs, Condition IDs and Token IDs

This is one of the most important concepts for Polymarket developers.

A market can have multiple identifiers representing different layers of the system.

You will encounter concepts such as:

  • market ID
  • event ID
  • condition ID
  • token ID / asset ID
  • slug

Do not treat these as interchangeable strings.

The Gamma API is primarily concerned with market and event metadata. The CLOB operates on outcome token IDs when you request prices, order books, or create orders.

The official market-data documentation describes a market as mapping to CLOB token IDs, a market address, a question ID and a condition ID.

For a binary market, your application will typically need to identify the appropriate outcome token before asking the CLOB for its book.

Conceptually:

Event
  |
  +-- Market
       |
       +-- Condition ID
       |
       +-- YES token ID
       |
       +-- NO token ID
Enter fullscreen mode Exit fullscreen mode

This distinction matters because an order-book request needs an asset/token identifier, not merely the human-readable question.

Practical rule

Store identifiers explicitly in your internal model:

from dataclasses import dataclass


@dataclass(frozen=True)
class MarketRef:
    market_id: str
    condition_id: str
    yes_token_id: str
    no_token_id: str
    slug: str
Enter fullscreen mode Exit fullscreen mode

Do not scatter raw IDs throughout your strategy code.


4. CLOB API: The Trading Layer

The CLOB is the part of the Polymarket API that matters most when your application actually needs to trade.

The production CLOB host is:

https://clob.polymarket.com
Enter fullscreen mode Exit fullscreen mode

The official documentation describes Polymarket's CLOB as a hybrid-decentralized trading system: orders are matched off-chain while matched trades settle onchain. Orders are EIP-712 signed messages, and settlement occurs on Polygon.

The CLOB exposes functionality for:

  • prices,
  • order books,
  • spreads,
  • midpoint prices,
  • historical prices,
  • order creation,
  • order cancellation,
  • open orders,
  • trades,
  • account information,
  • and related trading operations.

The public market-data surface includes endpoints such as:

GET /price
GET /prices
GET /book
POST /books
GET /prices-history
GET /midpoint
GET /spread
Enter fullscreen mode Exit fullscreen mode

according to the current official documentation.


5. Reading an Order Book

An automated strategy should rarely make a trading decision from a single displayed price.

The order book provides a more useful execution picture.

Conceptually:

ASKS
0.63   500
0.62   350
0.61   200
----------------
0.60   250   <- best bid
0.59   400
0.58   700
BIDS
Enter fullscreen mode Exit fullscreen mode

From this you can derive:

  • best bid,
  • best ask,
  • spread,
  • depth,
  • available size,
  • estimated execution price,
  • and potential slippage.

The CLOB exposes /book for an individual token and /books for multiple books.

A simple read-only request can be implemented without exposing any trading credentials:

import requests


CLOB_URL = "https://clob.polymarket.com"


def get_order_book(token_id: str) -> dict:
    response = requests.get(
        f"{CLOB_URL}/book",
        params={"token_id": token_id},
        timeout=10,
    )
    response.raise_for_status()
    return response.json()


book = get_order_book("YOUR_TOKEN_ID")

print(book)
Enter fullscreen mode Exit fullscreen mode

Replace YOUR_TOKEN_ID with a real token ID obtained from your market-discovery workflow.

Do not assume the first price is the executable price

A common bot-design error is:

price = get_price(...)
buy(price)
Enter fullscreen mode Exit fullscreen mode

A production execution engine should instead reason about:

signal
  |
  v
target price
  |
  v
current book
  |
  +--> spread
  |
  +--> available depth
  |
  +--> expected fill price
  |
  +--> slippage
  |
  v
risk checks
  |
  v
order
Enter fullscreen mode Exit fullscreen mode

The distinction becomes especially important when the strategy trades meaningful size.


6. Price, Midpoint and Spread

The CLOB provides separate concepts for:

  • price,
  • midpoint,
  • spread,
  • order book.

Do not collapse them into one variable called market_price.

For example:

import requests


def get_json(path: str, params: dict | None = None) -> dict:
    response = requests.get(
        f"https://clob.polymarket.com{path}",
        params=params,
        timeout=10,
    )
    response.raise_for_status()
    return response.json()


token_id = "YOUR_TOKEN_ID"

price = get_json("/price", {"token_id": token_id})
midpoint = get_json("/midpoint", {"token_id": token_id})
spread = get_json("/spread", {"token_id": token_id})

print("Price:", price)
print("Midpoint:", midpoint)
print("Spread:", spread)
Enter fullscreen mode Exit fullscreen mode

For strategy research, these values answer different questions.

Price can be useful for a specific side.

Midpoint gives you a reference between the current bid and ask.

Spread tells you something about the cost of crossing the market.

Order-book depth tells you whether your intended size can actually be executed near the displayed price.


7. Tick Size and Fee Information

A robust trading engine should not hard-code assumptions about every market.

The CLOB provides endpoints for market-specific properties such as tick size and fee rate.

For example, the current documentation exposes:

GET /tick-size
GET /fee-rate
Enter fullscreen mode Exit fullscreen mode

with token_id identifying the relevant asset.

The tick-size endpoint returns a minimum_tick_size, while the fee-rate endpoint returns a base fee rate in basis points.

That means an execution engine should conceptually do this:

market selected
      |
      v
fetch market parameters
      |
      +--> tick size
      |
      +--> fee information
      |
      +--> neg-risk / market configuration
      |
      v
validate order
      |
      v
sign
      |
      v
submit
Enter fullscreen mode Exit fullscreen mode

Do not assume every market can accept arbitrary decimal prices.

The official Python SDK changelog also documents validation around prices being multiples of the tick size.


8. Data API: Positions, Trades and Analytics

The Data API answers a different class of questions.

Its production host is:

https://data-api.polymarket.com
Enter fullscreen mode Exit fullscreen mode

The current documentation includes endpoints for:

GET /positions
GET /closed-positions
GET /activity
GET /value
GET /oi
GET /holders
GET /trades
Enter fullscreen mode Exit fullscreen mode

These are intended for positions, activity, market analytics and historical trade information.

For example, current positions can be requested through:

GET https://data-api.polymarket.com/positions
Enter fullscreen mode Exit fullscreen mode

The response contains fields such as asset, condition ID, size, average price, current value, PnL-related fields, outcome and market metadata.

Why not use the CLOB for everything?

Because the services answer different questions.

A clean bot architecture might use:

Gamma
  -> What markets exist?

CLOB
  -> What can I trade right now?

Data API
  -> What positions/activity do I have?

WebSocket
  -> What changed since the last event?
Enter fullscreen mode Exit fullscreen mode

That division is much easier to maintain than a single giant API wrapper.


9. WebSockets: Stop Polling When You Need Real-Time State

REST is excellent for snapshots.

It is not always the right abstraction for continuous state.

Polymarket provides public WebSocket market channels for real-time order-book, price and market-lifecycle updates. The current market channel includes book snapshots, price changes, last-trade-price events and tick-size changes.

The market channel is:

/wss/market
Enter fullscreen mode Exit fullscreen mode

A subscription contains asset IDs.

Conceptually:

REST
 |
 +--> initial snapshot
 |
 v
WebSocket
 |
 +--> book update
 +--> price change
 +--> trade
 +--> tick-size change
 +--> lifecycle event
Enter fullscreen mode Exit fullscreen mode

The official market-channel documentation also specifies a client heartbeat: send a ping every 10 seconds and expect a pong response.

That detail matters in production.

A WebSocket client should not simply:

ws.recv()
ws.recv()
ws.recv()
Enter fullscreen mode Exit fullscreen mode

forever.

It needs:

  • heartbeat handling,
  • reconnect logic,
  • subscription restoration,
  • sequence/state validation where applicable,
  • logging,
  • connection metrics,
  • and a recovery path back to REST snapshots.

10. User WebSocket Channel

For authenticated applications, Polymarket also provides a user WebSocket channel.

The user channel can stream order and trade events. Its subscription uses API credentials and supports market subscriptions.

This is particularly useful for execution engines.

Instead of repeatedly asking:

"Is my order filled?"
Enter fullscreen mode Exit fullscreen mode

your application can maintain an event-driven order state.

For example:

ORDER_SUBMITTED
      |
      v
ORDER_ACKNOWLEDGED
      |
      v
LIVE
      |
      +--------+
      |        |
      v        v
PARTIAL    CANCELLED
FILL          |
      |        |
      v        |
FILLED <-------+
Enter fullscreen mode Exit fullscreen mode

The event stream should update your local order state, while periodic reconciliation against the REST API protects against missed messages or connection failures.


11. The Authentication Model: L1 vs L2

One of the most important pieces of the Polymarket API is its authentication model.

The current CLOB documentation describes two authentication levels:

Level Mechanism Purpose
L1 EIP-712 wallet signature Create/derive API credentials
L2 HMAC-SHA256 API credentials Authenticated trading/account requests

The official documentation states that your private key is used to derive L2 credentials, consisting of an API key, secret and passphrase.

This distinction is important because API credentials do not replace order signing.

For order creation, the order itself still requires an appropriate EIP-712 signature. L2 authentication authenticates the API request; it is not the same thing as authorizing the signed order.

Think about it this way:

Wallet private key
       |
       | EIP-712 authentication
       v
L2 API credentials
       |
       | HMAC-authenticated API request
       v
CLOB
       |
       | EIP-712 signed order
       v
Order matching
Enter fullscreen mode Exit fullscreen mode

This is why simply obtaining an API key is not equivalent to having a complete trading integration.


12. Never Put Private Keys in Source Code

This should be non-negotiable.

Bad:

PRIVATE_KEY = "0x123..."
Enter fullscreen mode Exit fullscreen mode

Better:

import os

private_key = os.environ["POLYMARKET_PRIVATE_KEY"]
Enter fullscreen mode Exit fullscreen mode

For local development:

export POLYMARKET_PRIVATE_KEY="..."
Enter fullscreen mode Exit fullscreen mode

For production, use a proper secrets manager or protected environment configuration.

Never commit:

  • private keys,
  • seed phrases,
  • API secrets,
  • passphrases,
  • wallet credentials,
  • .env files containing production credentials.

A trading bot with a leaked private key is not merely a software bug. It can become a direct loss-of-funds event.


13. Current SDK Situation

This is an area where older tutorials can become dangerous.

Polymarket's older py-clob-client repository was archived on May 25, 2026.

Polymarket now maintains an official unified Python SDK under the polymarket-client package. Its repository describes it as the official Python SDK and says it provides a unified interface across public data, authenticated accounts, trading, builder attribution and wallet workflows.

However, the unified SDK is explicitly marked beta.

That produces an important practical distinction:

New project
   |
   +--> Evaluate official unified Python SDK
   |
   +--> Verify current API surface
   |
   +--> Pin/test the version
   |
   +--> Build integration tests
Enter fullscreen mode Exit fullscreen mode

For CLOB-specific integrations, the official documentation currently points developers toward the CLOB V2 Python client:

pip install py-clob-client-v2
Enter fullscreen mode Exit fullscreen mode

The CLOB trading overview lists the Python V2 client alongside the current TypeScript and Rust clients.

Do not copy an old 2025 tutorial's dependency list into a production system without checking whether it targets the current CLOB architecture.


14. A Minimal Python Market-Discovery Client

For market discovery, plain HTTP is often enough.

from __future__ import annotations

import logging
from typing import Any

import requests


logger = logging.getLogger(__name__)

GAMMA_URL = "https://gamma-api.polymarket.com"


class GammaClient:
    def __init__(self, timeout: float = 10.0) -> None:
        self.timeout = timeout
        self.session = requests.Session()

    def list_markets(self, limit: int = 10) -> list[dict[str, Any]]:
        response = self.session.get(
            f"{GAMMA_URL}/markets",
            params={"limit": limit},
            timeout=self.timeout,
        )
        response.raise_for_status()

        payload = response.json()

        if isinstance(payload, list):
            return payload

        if isinstance(payload, dict) and "data" in payload:
            return payload["data"]

        raise ValueError("Unexpected Gamma API response format")


if __name__ == "__main__":
    logging.basicConfig(level=logging.INFO)

    client = GammaClient()

    try:
        markets = client.list_markets(limit=5)

        for market in markets:
            logger.info(
                "market=%s question=%s",
                market.get("id"),
                market.get("question"),
            )

    except requests.RequestException:
        logger.exception("Gamma request failed")
    except ValueError:
        logger.exception("Unexpected Gamma response")
Enter fullscreen mode Exit fullscreen mode

This example is intentionally read-only.

It does not require:

  • a private key,
  • API credentials,
  • a wallet,
  • or order-signing logic.

That makes it a good first integration test.


15. Using the Official Unified Python SDK

The official unified SDK currently supports a public client interface.

The official repository documents:

pip install polymarket-client
Enter fullscreen mode Exit fullscreen mode

and provides a synchronous PublicClient as well as an asynchronous AsyncPublicClient.

A basic read-only example is:

from polymarket import PublicClient


with PublicClient() as client:
    market = client.get_market(
        slug="YOUR_MARKET_SLUG"
    )

    print(market)
Enter fullscreen mode Exit fullscreen mode

The SDK's public client source confirms that market lookup supports identifiers such as ID, slug, and URL.

Because the unified SDK is still beta, production systems should pin the dependency, run integration tests against the exact version being deployed, and monitor the project's release notes.


16. Building a Production-Oriented API Layer

Do not allow strategy code to directly construct HTTP requests.

Instead:

Strategy
   |
   v
MarketService
   |
   +--> Gamma
   |
   +--> CLOB
   |
   +--> Data API
   |
   +--> WebSocket
Enter fullscreen mode Exit fullscreen mode

For example:

class MarketService:
    def __init__(
        self,
        gamma_client,
        clob_client,
        data_client,
    ):
        self.gamma = gamma_client
        self.clob = clob_client
        self.data = data_client

    def get_market_snapshot(self, market):
        metadata = self.gamma.get_market(market)
        book = self.clob.get_order_book(metadata.yes_token_id)

        return {
            "market": metadata,
            "book": book,
        }
Enter fullscreen mode Exit fullscreen mode

Your strategy should not care whether the data came from:

  • requests,
  • httpx,
  • an SDK,
  • a cache,
  • or a WebSocket-maintained local book.

That separation makes later infrastructure changes much easier.


17. Rate Limits Are Part of the API Contract

A production Polymarket integration needs explicit rate-limit handling.

The current official rate-limit documentation states that API limits are enforced through Cloudflare throttling and that requests exceeding limits can be delayed/queued rather than immediately rejected. The limits operate over sliding windows.

Examples currently documented include:

Surface Endpoint Documented limit
Gamma /markets 300 req / 10s
Gamma /events 500 req / 10s
Data /positions 150 req / 10s
Data /trades 200 req / 10s
CLOB /book 1,500 req / 10s
CLOB /price 1,500 req / 10s
CLOB /midpoint 1,500 req / 10s

The official page contains the complete current table, including trading endpoint burst and sustained limits.

Do not build this:

while True:
    GET /markets
    GET /book
    GET /price
    sleep(0.01)
Enter fullscreen mode Exit fullscreen mode

Build this instead:

Startup
  |
  +--> Discover markets
  |
  +--> Cache metadata
  |
  v
WebSocket stream
  |
  +--> update market state
  |
  v
Strategy
  |
  +--> request REST snapshot only when required
Enter fullscreen mode Exit fullscreen mode

Streaming plus caching is usually a much cleaner architecture than aggressive REST polling.


18. Retry Strategy

Retries should distinguish between transient infrastructure errors and permanent application errors.

A basic pattern:

import random
import time

import requests


RETRYABLE_STATUS_CODES = {429, 500, 502, 503, 504}


def get_with_retry(
    session: requests.Session,
    url: str,
    *,
    params: dict | None = None,
    attempts: int = 5,
    timeout: float = 10.0,
) -> requests.Response:

    for attempt in range(attempts):
        try:
            response = session.get(
                url,
                params=params,
                timeout=timeout,
            )

            if response.status_code not in RETRYABLE_STATUS_CODES:
                response.raise_for_status()
                return response

        except requests.RequestException:
            if attempt == attempts - 1:
                raise

        if attempt == attempts - 1:
            response.raise_for_status()

        delay = min(
            5.0,
            0.25 * (2 ** attempt) + random.uniform(0, 0.1),
        )

        time.sleep(delay)

    raise RuntimeError("Unreachable")
Enter fullscreen mode Exit fullscreen mode

But retries require more thought for trading operations.

A retry of:

GET /book
Enter fullscreen mode Exit fullscreen mode

is usually straightforward.

A retry of:

POST /order
Enter fullscreen mode Exit fullscreen mode

can be materially different.

Before automatically retrying an order submission, your system should determine whether the original request was:

  • rejected,
  • accepted,
  • processed but response lost,
  • or actually matched.

Blindly submitting the same trading instruction again can create duplicate exposure.


19. Idempotency and Order State

A production execution engine should maintain its own order lifecycle.

For example:

strategy signal
      |
      v
risk approval
      |
      v
order intent created
      |
      v
signed order
      |
      v
submission
      |
      v
local state = SUBMITTED
      |
      +--> WebSocket event
      |
      +--> REST reconciliation
      |
      v
LIVE / PARTIAL / FILLED / CANCELLED / REJECTED
Enter fullscreen mode Exit fullscreen mode

The database should store enough information to reconstruct what happened.

At minimum, consider persisting:

client order identifier
market
token ID
side
requested price
requested size
order type
creation timestamp
submission timestamp
exchange order ID
status
matched size
average execution price
error code
Enter fullscreen mode Exit fullscreen mode

This becomes extremely valuable when debugging a bot after a network failure.


20. WebSocket + REST Reconciliation

Never assume a WebSocket connection is perfect.

A robust system treats WebSocket events as a fast state-update mechanism and REST as a reconciliation mechanism.

For example:

                +----------------+
                | Local State DB |
                +-------+--------+
                        ^
                        |
            +-----------+-----------+
            |                       |
            |                       |
      WebSocket                 REST
      real-time                 snapshot
      updates                   reconciliation
Enter fullscreen mode Exit fullscreen mode

If the WebSocket disconnects:

  1. mark the stream stale;
  2. stop making decisions that depend on unknown state if necessary;
  3. reconnect;
  4. restore subscriptions;
  5. obtain a fresh snapshot;
  6. reconcile local state;
  7. resume normal processing.

This is far safer than simply reconnecting and continuing from stale memory.


21. Practical Example: Market Scanner

Let's build a small research-oriented scanner.

The objective is not to trade.

It will:

  1. query Gamma;
  2. identify markets;
  3. obtain token IDs;
  4. query CLOB market data;
  5. calculate a simple spread metric;
  6. print candidates.
from __future__ import annotations

import requests


GAMMA = "https://gamma-api.polymarket.com"
CLOB = "https://clob.polymarket.com"

session = requests.Session()


def get(url: str, **kwargs):
    response = session.get(
        url,
        timeout=10,
        **kwargs,
    )
    response.raise_for_status()
    return response.json()


def get_markets(limit: int = 20):
    payload = get(
        f"{GAMMA}/markets",
        params={"limit": limit},
    )

    if isinstance(payload, list):
        return payload

    return payload.get("data", [])


def get_book(token_id: str):
    return get(
        f"{CLOB}/book",
        params={"token_id": token_id},
    )


def best_prices(book: dict):
    bids = book.get("bids", [])
    asks = book.get("asks", [])

    if not bids or not asks:
        return None, None

    best_bid = max(float(level["price"]) for level in bids)
    best_ask = min(float(level["price"]) for level in asks)

    return best_bid, best_ask


for market in get_markets():
    question = market.get("question")

    # The exact field containing outcome/token information
    # should be validated against the current Gamma response
    # for the markets you intend to support.
    token_ids = market.get("clobTokenIds")

    if not token_ids:
        continue

    try:
        first_token = token_ids[0]
        book = get_book(first_token)

        bid, ask = best_prices(book)

        if bid is None or ask is None:
            continue

        print(
            f"{question}\n"
            f"  bid={bid:.4f} ask={ask:.4f} "
            f"spread={ask - bid:.4f}"
        )

    except (requests.RequestException, ValueError, TypeError) as exc:
        print(f"Skipping market: {exc}")
Enter fullscreen mode Exit fullscreen mode

This is intentionally a research example, not a production trading engine.

The important engineering idea is the separation:

Gamma -> identify candidate
CLOB  -> inspect execution conditions
Strategy -> decide
Risk -> approve/reject
Execution -> trade
Enter fullscreen mode Exit fullscreen mode

22. What a Real Trading Bot Should Look Like

A production Polymarket bot should be closer to this:

flowchart TD
    A[Gamma Market Discovery] --> B[Market Registry]
    B --> C[Eligibility Filter]

    C --> D[WebSocket Market Stream]
    D --> E[Local Market State]

    E --> F[Strategy Engine]
    F --> G[Risk Engine]

    G -->|Approved| H[Execution Engine]
    G -->|Rejected| I[Log Decision]

    H --> J[CLOB V2]
    J --> K[Order Events]

    K --> L[Order State Store]
    L --> M[Portfolio / Position Reconciliation]

    N[Data API] --> M
    M --> G

    O[Monitoring] --> D
    O --> H
    O --> M
Enter fullscreen mode Exit fullscreen mode

This architecture creates explicit boundaries between:

  • discovery,
  • market data,
  • strategy,
  • risk,
  • execution,
  • portfolio state,
  • and observability.

That separation is more important than whether your strategy is 100 lines or 10,000 lines.


23. Performance Considerations

Performance is not simply about making HTTP requests faster.

For an automated trading system, the relevant pipeline is:

market event
   ↓
network transport
   ↓
message parsing
   ↓
state update
   ↓
strategy calculation
   ↓
risk checks
   ↓
order creation
   ↓
signature
   ↓
submission
   ↓
matching
Enter fullscreen mode Exit fullscreen mode

Optimizing only one stage can produce little improvement if another stage dominates the path.

Use WebSockets for continuously changing state

If you need continuous order-book updates, streaming is generally more appropriate than repeatedly requesting snapshots.

Keep market metadata local

Do not repeatedly retrieve the same market description every time a price changes.

Precompute strategy state

If your strategy repeatedly calculates the same features, maintain incremental state rather than rebuilding the entire dataset.

Separate hot and cold paths

A useful distinction:

Cold path

  • market discovery,
  • historical analysis,
  • configuration,
  • metadata refresh.

Hot path

  • order-book events,
  • signal evaluation,
  • risk checks,
  • order submission,
  • order-state updates.

This architecture lets you optimize the latency-sensitive portion without turning the entire codebase into a low-level networking project.


24. Do Not Confuse Latency With Edge

A fast API client does not automatically create a profitable trading strategy.

Even if your infrastructure reacts quickly, you still face:

  • spread,
  • slippage,
  • liquidity constraints,
  • adverse selection,
  • fees where applicable,
  • market impact,
  • stale information,
  • model error,
  • execution uncertainty,
  • resolution risk.

The correct optimization target is therefore not:

"Make the bot as fast as possible."

It is:

"Make the complete decision-to-execution pipeline reliable enough that the strategy's assumptions remain valid."

Speed is one component of execution quality.


25. Security Architecture

A trading system should have at least three security boundaries.

Boundary 1: Secrets

Private keys and API secrets belong in a secrets-management layer.

Boundary 2: Strategy

Strategy code should not automatically have permission to transfer arbitrary funds or modify infrastructure.

Boundary 3: Execution

The execution engine should enforce limits independently of strategy code.

For example:

class RiskLimits:
    max_order_size: float
    max_position_size: float
    max_daily_loss: float
    max_open_orders: int
Enter fullscreen mode Exit fullscreen mode

Then:

def approve_order(order, limits) -> bool:
    if order.size > limits.max_order_size:
        return False

    if order.projected_position > limits.max_position_size:
        return False

    return True
Enter fullscreen mode Exit fullscreen mode

The point is not that these exact limits are appropriate.

The point is that risk controls should not depend on the strategy remembering to apply them.


26. Geographic and Account Constraints

API access does not mean every user or institution can necessarily trade every product.

Polymarket's documentation includes geographic restrictions and eligibility requirements, and developers should validate the applicable restrictions for their users and deployment. The institutional site also states that institutional eligibility depends on jurisdiction.

Do not build an application that assumes:

API accessible = trading permitted
Enter fullscreen mode Exit fullscreen mode

Those are different questions.

Your onboarding flow should validate the applicable account, geographic and product restrictions before attempting live trading.


27. Failure Modes You Should Expect

Failure 1: Using an outdated SDK

Older tutorials may reference the archived py-clob-client.

Fix: verify the current CLOB V2 documentation and official SDK repositories before installing dependencies.


Failure 2: Treating a market ID as a token ID

The CLOB's price and order-book operations operate on asset/token identifiers.

Fix: explicitly maintain the mapping between market metadata and CLOB token IDs.


Failure 3: Polling everything

A bot that repeatedly requests:

markets
prices
books
positions
orders
Enter fullscreen mode Exit fullscreen mode

can become unnecessarily expensive and difficult to scale.

Fix: cache metadata, use streaming where appropriate, and use REST for snapshots/reconciliation.


Failure 4: Retrying POST blindly

If a response disappears after an order was accepted, automatically submitting the same order again can create unintended exposure.

Fix: implement order-state reconciliation.


Failure 5: Ignoring tick size

A strategy can calculate a mathematically valid price that is not valid for the market's configured increment.

Fix: retrieve and enforce the applicable tick size.


Failure 6: Hard-coding fees

Market economics can change, and the current CLOB provides a fee-rate endpoint.

Fix: retrieve the applicable fee information and include it in execution calculations.


Failure 7: Trusting WebSocket state forever

A disconnected or partially processed stream can leave local state stale.

Fix: reconnect and reconcile against authoritative REST data.


Failure 8: Logging secrets

Avoid:

logger.info("credentials=%s", credentials)
Enter fullscreen mode Exit fullscreen mode

Instead log:

request_id
market_id
token_id
latency
status
error_code
order_id
Enter fullscreen mode Exit fullscreen mode

but never private keys or API secrets.


28. Testing Strategy

A trading integration should have several levels of testing.

Unit tests

Test:

  • price rounding,
  • tick-size validation,
  • spread calculations,
  • position calculations,
  • risk limits,
  • retry behavior,
  • order-state transitions.

Example:

def test_price_matches_tick_size():
    tick_size = 0.01
    price = 0.55

    assert round(price / tick_size) * tick_size == price
Enter fullscreen mode Exit fullscreen mode

Integration tests

Test:

  • Gamma market retrieval,
  • CLOB connectivity,
  • WebSocket subscriptions,
  • Data API queries,
  • authentication,
  • order lifecycle handling.

The official unified SDK repository itself distinguishes ordinary unit tests from opt-in integration tests and separately protects metered tests that can spend funds or mutate live state.

That is a useful model for your own test suite.


Failure-injection tests

Simulate:

  • HTTP 429,
  • HTTP 500,
  • connection reset,
  • malformed JSON,
  • WebSocket disconnect,
  • delayed response,
  • duplicate events,
  • missing events,
  • stale market state.

The goal is not merely to prove that the happy path works.

The goal is to discover what the bot does when the network behaves badly.


29. Observability

At minimum, measure:

API metrics

request_count
error_count
429_count
latency_ms
timeout_count
Enter fullscreen mode Exit fullscreen mode

WebSocket metrics

connection_count
disconnect_count
reconnect_count
last_message_age
subscription_count
Enter fullscreen mode Exit fullscreen mode

Trading metrics

orders_submitted
orders_rejected
orders_cancelled
orders_filled
partial_fills
Enter fullscreen mode Exit fullscreen mode

Strategy metrics

signals_generated
signals_rejected_by_risk
signals_executed
Enter fullscreen mode Exit fullscreen mode

Portfolio metrics

position_size
cash_balance
open_order_count
realized_pnl
unrealized_pnl
Enter fullscreen mode Exit fullscreen mode

The Polymarket status system separately tracks the Trading API, WebSocket, Markets/Position data and on-chain settlement infrastructure.

Your monitoring should therefore avoid treating "Polymarket is up" as one binary condition.

A bot can have:

Gamma: healthy
CLOB: healthy
WebSocket: disconnected
Data API: healthy
Enter fullscreen mode Exit fullscreen mode

and still be unable to operate correctly.


30. Production Deployment Checklist

Before enabling live trading:

  • [ ] Verify the current official API documentation
  • [ ] Confirm the SDK version
  • [ ] Test market discovery
  • [ ] Test token-ID mapping
  • [ ] Validate tick size
  • [ ] Validate fee assumptions
  • [ ] Test order signing
  • [ ] Test authentication
  • [ ] Test cancellation
  • [ ] Test WebSocket reconnection
  • [ ] Test REST reconciliation
  • [ ] Implement risk limits
  • [ ] Implement rate-limit handling
  • [ ] Implement structured logging
  • [ ] Remove credentials from logs
  • [ ] Run a dry-run strategy
  • [ ] Test failure scenarios
  • [ ] Pin dependencies
  • [ ] Set up alerts
  • [ ] Verify jurisdiction/account eligibility
  • [ ] Start with deliberately constrained exposure

31. Advanced Improvement: Build a Local Market State Engine

For sophisticated bots, the API client should not be the strategy's state store.

Instead, maintain:

@dataclass
class MarketState:
    token_id: str
    best_bid: float | None
    best_ask: float | None
    midpoint: float | None
    spread: float | None
    last_trade_price: float | None
    updated_at: float
Enter fullscreen mode Exit fullscreen mode

Then WebSocket messages mutate this state.

Your strategy consumes:

state = market_state.get(token_id)
Enter fullscreen mode Exit fullscreen mode

rather than issuing a network request every time it wants a price.

This changes the architecture from:

strategy -> API -> response -> strategy
Enter fullscreen mode Exit fullscreen mode

to:

WebSocket -> local state -> strategy
Enter fullscreen mode Exit fullscreen mode

That is a much stronger foundation for event-driven systems.


32. Advanced Improvement: Separate Decision Time From Execution Time

Store both.

For example:

signal_created_at
risk_check_at
order_signed_at
order_submitted_at
exchange_ack_at
first_fill_at
final_fill_at
Enter fullscreen mode Exit fullscreen mode

Then calculate:

signal -> risk
risk -> signing
signing -> submission
submission -> acknowledgement
acknowledgement -> fill
Enter fullscreen mode Exit fullscreen mode

This lets you identify where execution actually spends time.

Without these timestamps, "the bot is slow" is not an actionable diagnosis.


33. Advanced Improvement: Treat Market Metadata as Versioned State

Market metadata can change.

Do not assume:

market = load_once_forever()
Enter fullscreen mode Exit fullscreen mode

A better design periodically refreshes metadata and records changes:

Market Registry
      |
      +--> metadata version
      +--> active/closed state
      +--> token mapping
      +--> tick size
      +--> market configuration
Enter fullscreen mode Exit fullscreen mode

If your bot behaves differently after a market configuration change, you should be able to reconstruct what metadata it was using at the time.


34. Advanced Improvement: Add a Kill Switch

Every automated trading system should have a mechanism to stop opening new exposure.

For example:

class TradingMode:
    ENABLED = "enabled"
    REDUCE_ONLY = "reduce_only"
    DISABLED = "disabled"
Enter fullscreen mode Exit fullscreen mode

Then the execution layer checks the mode before accepting an order.

This gives you a way to respond to:

  • broken market data,
  • unexpected API behavior,
  • strategy anomalies,
  • excessive losses,
  • corrupted state,
  • infrastructure incidents.

A kill switch should be implemented outside the strategy itself.


35. Current Polymarket API Architecture in One Diagram

The entire system can be reduced to this mental model:

flowchart LR
    A[Gamma API] --> B[Market Registry]
    B --> C[Token IDs]

    C --> D[CLOB REST]
    C --> E[CLOB WebSocket]

    D --> F[Prices / Books]
    E --> F

    G[Data API] --> H[Positions / Trades / Analytics]

    F --> I[Strategy]
    H --> I

    I --> J[Risk Engine]
    J --> K[CLOB V2 Order Signing]
    K --> L[Order Submission]

    L --> M[User WebSocket]
    M --> N[Order State]

    N --> H
Enter fullscreen mode Exit fullscreen mode

If you remember only one thing from this article, remember this:

Gamma tells your application what exists. CLOB tells it what can be traded and executes orders. Data API tells it what happened to positions and activity. WebSockets tell it what is changing now.

That division is the foundation of a maintainable Polymarket integration.


36. Frequently Asked Questions

What is the Polymarket API?

The Polymarket API is the collection of developer interfaces used to access Polymarket market data, trading infrastructure, positions, analytics and real-time events. The primary REST surfaces are Gamma, CLOB and Data API, complemented by WebSocket channels.

Does Polymarket have a Python API?

Yes. Polymarket maintains an official Python SDK, currently distributed as polymarket-client. The project is currently in beta. Polymarket also documents a Python CLOB V2 client for CLOB trading integrations.

Can I access Polymarket market data without authentication?

Yes. The official market-data documentation states that public market data is available without an API key, authentication or wallet.

Which API should I use to build a trading bot?

A typical bot will use Gamma for market discovery, CLOB for order books and execution, Data API for portfolio/activity information, and WebSockets for real-time market and user events.

What are L1 and L2 authentication?

L1 uses an EIP-712 wallet signature and is used to create or derive API credentials. L2 uses HMAC-SHA256 API credentials for authenticated API operations. Orders themselves still require appropriate wallet-based order signing.

Is the old py-clob-client still the right SDK?

The original Polymarket/py-clob-client repository was archived in May 2026. The current documentation points developers toward the CLOB V2 client, while Polymarket also maintains a newer unified Python SDK that is currently beta.

Should I poll the CLOB or use WebSockets?

For continuously changing market state, WebSockets are generally the more appropriate architecture. REST remains useful for snapshots, queries and reconciliation. The official market channel provides order-book and price events.

Does a faster Polymarket API integration guarantee trading profits?

No. API performance does not guarantee profitability. Execution quality depends on liquidity, spread, slippage, fees, adverse selection, strategy quality, model risk and other factors.


37. Conclusion

The most important lesson when working with the Polymarket API is that you should stop thinking about it as a single endpoint collection.

Think in systems.

Gamma handles discovery and metadata.

CLOB handles prices, order books and trading.

Data API handles positions, trades and analytics.

WebSockets handle continuously changing state.

Wallet signatures and L2 credentials provide the authentication and authorization layers needed for authenticated trading.

And your own application must provide the parts Polymarket cannot provide for you:

  • strategy logic,
  • risk controls,
  • state management,
  • retries,
  • reconciliation,
  • observability,
  • secrets management,
  • testing,
  • and operational safeguards.

The difference between a five-minute API experiment and a serious trading system is not the ability to call GET /book.

It is everything surrounding that call.

If you are building a Polymarket bot, start with read-only market discovery, build a reliable local market-state layer, add WebSocket processing, implement risk controls, and only then introduce authenticated execution.

That sequence keeps the system understandable—and makes failures much easier to diagnose.

Educational/trading-risk disclaimer: This article is for software-development and educational purposes only. Automated prediction-market trading involves financial risk, execution risk, liquidity risk and the possibility of losing capital. API behavior and market rules can change. Always verify current Polymarket documentation and test integrations before deploying live funds.


Related Articles

Build these as a topic cluster around the Polymarket API pillar article:

  1. How to Build a Polymarket Trading Bot in Python
  • Anchor: build a Polymarket trading bot in Python
  • Why: Converts API knowledge into a complete automation workflow.
  1. Polymarket CLOB API: Order Books, Prices and Order Execution
  • Anchor: Polymarket CLOB API
  • Why: Deepens the execution/API layer introduced here.
  1. Polymarket Gamma API: Market Discovery and Metadata
  • Anchor: Polymarket Gamma API
  • Why: Targets developers primarily interested in market discovery.
  1. How Polymarket WebSockets Work
  • Anchor: Polymarket WebSocket API
  • Why: Expands the real-time architecture section.
  1. Polymarket API Authentication: L1, L2 and EIP-712
  • Anchor: Polymarket API authentication
  • Why: Provides a dedicated security/authentication resource.
  1. How to Read a Polymarket Order Book With Python
  • Anchor: read a Polymarket order book with Python
  • Why: Targets a practical implementation query.
  1. Polymarket Trading Bot Architecture
  • Anchor: Polymarket trading bot architecture
  • Why: Extends the architecture diagrams into a full system-design article.
  1. Polymarket CLOB V2 Migration Guide for Python Developers
  • Anchor: Polymarket CLOB V2 migration
  • Why: Captures developers maintaining older integrations.

Useful Resources

Official Polymarket

Polymarket

Useful for understanding the live market interface and validating how market metadata is presented to users.

Official API Documentation

Polymarket Developer Documentation

The primary technical authority for current endpoints, authentication, SDKs, rate limits and trading behavior.

Relevant API Reference

Polymarket Market Data Overview

A particularly useful starting point because it explains how Gamma, CLOB and Data API responsibilities are separated.

Official Python SDK

Polymarket Python SDK on GitHub

The official unified Python client. It is currently beta, so developers should check its release history and test the exact dependency version they deploy.

Medium: Polymarket API for Developers

Polymarket API for Developers: Data, CLOB, and Polygon RPC

A useful third-party overview of the API architecture. It should be treated as supplementary rather than authoritative; current API behavior should be verified against Polymarket's official documentation.

YouTube: Polymarket API With Python

A practical third-party walkthrough covering API setup, market data, order books and Python-based trading. It was published before the current CLOB V2 migration, so developers should cross-check implementation details against current official documentation.

Official Polymarket X

Polymarket on X

Useful for product and ecosystem announcements. Technical implementation details should still be verified against the official developer documentation.

Official Developer Resources

Polymarket GitHub Organization

Contains official SDKs, client libraries and developer tooling. The organization's repositories include the current Python SDK and CLOB V2 clients.

DEV.to resource: No sufficiently relevant DEV.to article was verified during research, so it is intentionally omitted rather than publishing an unverified URL.


About the Author

Bo$onaX

I write about Polymarket trading bots, prediction-market infrastructure, algorithmic trading, Python automation, Web3 development, and quantitative strategies.

Contact:
X: [https://x.com/xxniiinxx]
Telegram: [https://t.me/bosonax]

Top comments (0)