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
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
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
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}
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
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
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
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
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
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
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)
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)
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
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)
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
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
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
The current documentation includes endpoints for:
GET /positions
GET /closed-positions
GET /activity
GET /value
GET /oi
GET /holders
GET /trades
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
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?
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
A subscription contains asset IDs.
Conceptually:
REST
|
+--> initial snapshot
|
v
WebSocket
|
+--> book update
+--> price change
+--> trade
+--> tick-size change
+--> lifecycle event
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()
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?"
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 <-------+
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
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..."
Better:
import os
private_key = os.environ["POLYMARKET_PRIVATE_KEY"]
For local development:
export POLYMARKET_PRIVATE_KEY="..."
For production, use a proper secrets manager or protected environment configuration.
Never commit:
- private keys,
- seed phrases,
- API secrets,
- passphrases,
- wallet credentials,
-
.envfiles 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
For CLOB-specific integrations, the official documentation currently points developers toward the CLOB V2 Python client:
pip install py-clob-client-v2
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")
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
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)
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
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,
}
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)
Build this instead:
Startup
|
+--> Discover markets
|
+--> Cache metadata
|
v
WebSocket stream
|
+--> update market state
|
v
Strategy
|
+--> request REST snapshot only when required
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")
But retries require more thought for trading operations.
A retry of:
GET /book
is usually straightforward.
A retry of:
POST /order
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
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
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
If the WebSocket disconnects:
- mark the stream stale;
- stop making decisions that depend on unknown state if necessary;
- reconnect;
- restore subscriptions;
- obtain a fresh snapshot;
- reconcile local state;
- 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:
- query Gamma;
- identify markets;
- obtain token IDs;
- query CLOB market data;
- calculate a simple spread metric;
- 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}")
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
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
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
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
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
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
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
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)
Instead log:
request_id
market_id
token_id
latency
status
error_code
order_id
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
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
WebSocket metrics
connection_count
disconnect_count
reconnect_count
last_message_age
subscription_count
Trading metrics
orders_submitted
orders_rejected
orders_cancelled
orders_filled
partial_fills
Strategy metrics
signals_generated
signals_rejected_by_risk
signals_executed
Portfolio metrics
position_size
cash_balance
open_order_count
realized_pnl
unrealized_pnl
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
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
Then WebSocket messages mutate this state.
Your strategy consumes:
state = market_state.get(token_id)
rather than issuing a network request every time it wants a price.
This changes the architecture from:
strategy -> API -> response -> strategy
to:
WebSocket -> local state -> strategy
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
Then calculate:
signal -> risk
risk -> signing
signing -> submission
submission -> acknowledgement
acknowledgement -> fill
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()
A better design periodically refreshes metadata and records changes:
Market Registry
|
+--> metadata version
+--> active/closed state
+--> token mapping
+--> tick size
+--> market configuration
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"
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
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:
- 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.
- Polymarket CLOB API: Order Books, Prices and Order Execution
- Anchor:
Polymarket CLOB API - Why: Deepens the execution/API layer introduced here.
- Polymarket Gamma API: Market Discovery and Metadata
- Anchor:
Polymarket Gamma API - Why: Targets developers primarily interested in market discovery.
- How Polymarket WebSockets Work
- Anchor:
Polymarket WebSocket API - Why: Expands the real-time architecture section.
- Polymarket API Authentication: L1, L2 and EIP-712
- Anchor:
Polymarket API authentication - Why: Provides a dedicated security/authentication resource.
- How to Read a Polymarket Order Book With Python
- Anchor:
read a Polymarket order book with Python - Why: Targets a practical implementation query.
- Polymarket Trading Bot Architecture
- Anchor:
Polymarket trading bot architecture - Why: Extends the architecture diagrams into a full system-design article.
- Polymarket CLOB V2 Migration Guide for Python Developers
- Anchor:
Polymarket CLOB V2 migration - Why: Captures developers maintaining older integrations.
Useful Resources
Official 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
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)