Polymarket SDK
Complete reference for ctx.polymarket — programmatic prediction-market trading on Polymarket from worker code.
Polymarket = on-chain prediction markets
This SDK wraps Polymarket's three public APIs:
- Gamma API for events, markets, and discovery
- CLOB API for order book / trading (auth via wallet signature)
- Data API for positions, public trades, fills
19 methods total. Server-only.
Setup
- Add a Polymarket trading block to your workspace canvas.
- Open the inspector → API Keys → pick a key (Settings → API Keys → Polymarket to add new). Polymarket requires a wallet private key (your Polygon EVM key).
- Connect the block to your Worker block via an edge.
ctx.polymarketis now available.
def tick(ctx):
bal = ctx.polymarket.get_balance()
ctx.log.info(f"USDC: {bal}")If no Polymarket block is connected, public methods still work, but per-user methods raise:
RuntimeError: No Polymarket block connected. Connect a Polymarket block to this worker.Why a private key here?
Unlike Hyperliquid (browser-only signing), Polymarket's py-clob-client SDK signs orders with the user's private key server-side. You explicitly opt in by pasting the key — it's stored Fernet-encrypted alongside other API keys. If you don't want a private key on our server, don't add a Polymarket key — public methods still work for read-only research.
Concepts
| Term | Meaning |
|---|---|
| Event | A real-world question, e.g. "Will Trump win the 2028 election?" |
| Market | A specific binary outcome under an event ("YES" or "NO") |
| Token | The tradable ERC-1155 share representing a single outcome |
| Token ID | The unique 70-digit numeric identifier for that token |
| Probability | The token's price between 0 and 1 (0.42 = 42% implied) |
Discovery flow: search_events("Trump") → pick event → event["markets"][i]["clobTokenIds"] → trade by token_id.
Public Discovery
These methods don't need a connected Polymarket block.
search_events
Search events by name fragment.
ctx.polymarket.search_events(query: str = "", limit: int = 20) -> listresults = ctx.polymarket.search_events("election", limit=10)
for ev in results:
ctx.log.info(f"{ev['title']} ({ev['markets']} markets)")get_event
Single event with all child markets and token IDs.
ctx.polymarket.get_event(event_id: str) -> dictsearch_markets
Search markets directly (more granular than events).
ctx.polymarket.search_markets(query: str = "", limit: int = 20) -> listget_market
Detail for one market by id.
ctx.polymarket.get_market(market_id: str) -> dictget_active_markets
List currently-active markets, paginated. Useful for scanners.
ctx.polymarket.get_active_markets(
limit: int = 50,
offset: int = 0,
active: bool = True,
closed: bool = False,
) -> listget_market_trades
Recent public trades on a market — anyone's executions.
ctx.polymarket.get_market_trades(token_id: str, limit: int = 100) -> listMarket Data (auth required for CLOB endpoints)
get_orderbook
L2 order book for a token.
ctx.polymarket.get_orderbook(token_id: str) -> dictReturns: {"market", "asset_id", "bids": [{"price", "size"}], "asks": [{"price", "size"}], "hash"}
get_midpoint
Midpoint price (mean of best bid + best ask) — quick fair-value reference.
ctx.polymarket.get_midpoint(token_id: str) -> float
# returns a Python float, e.g. 0.42get_last_trade_price
The price of the most recent execution.
ctx.polymarket.get_last_trade_price(token_id: str) -> dictget_price
Best bid (side='buy') or best ask (side='sell') for a token.
ctx.polymarket.get_price(token_id: str, side: str = "buy") -> dictbid = ctx.polymarket.get_price(token_id, "buy")
ask = ctx.polymarket.get_price(token_id, "sell")
spread_cents = (float(ask["price"]) - float(bid["price"])) * 100get_spread
Bid/ask spread snapshot for a token.
ctx.polymarket.get_spread(token_id: str) -> dictAccount & Positions
get_balance
USDC balance + on-chain allowance for the CLOB.
ctx.polymarket.get_balance() -> dictget_positions
Current outcome-token positions across all markets.
ctx.polymarket.get_positions() -> listfor p in ctx.polymarket.get_positions():
if float(p.get("size", 0)) > 0:
ctx.log.info(f"{p.get('outcome')}: {p['size']} @ "
f"avg {p.get('avgPrice')}, "
f"PnL ${p.get('cashPnl', 0):.2f}")get_history
Trade history (your fills with fees).
ctx.polymarket.get_history(limit: int = 50) -> listOrders
create_order
Place a limit order. Polymarket only supports limit orders — no market orders. Use a near-mid price for fast fill.
ctx.polymarket.create_order(
token_id: str,
side: str, # "BUY" | "SELL"
size: float, # number of shares
price: float, # 0..1 probability
) -> dict# Buy 100 YES shares of "BTC > $80k by EOY 2026" at 0.40
r = ctx.polymarket.create_order(
token_id="71321045679252212594626385532...",
side="BUY", size=100, price=0.40,
)
order_id = r.get("orderID")Min size
Polymarket has a minimum order value (~$1 USDC at the time of writing). Orders below that get rejected.
cancel_order
Cancel a single order.
ctx.polymarket.cancel_order(order_id: str) -> dictcancel_all_orders
Cancel ALL open orders. Kill switch.
ctx.polymarket.cancel_all_orders() -> dictget_open_orders
Active (unfilled) orders.
ctx.polymarket.get_open_orders() -> listget_order
Detail of a single order by id.
ctx.polymarket.get_order(order_id: str) -> dictCommon Patterns
Public-only research mode
Don't have a Polymarket private key configured? You can still do public market research:
def tick(ctx):
# No block needed — works on Free plan
events = ctx.polymarket.search_events("Bitcoin", limit=5)
for ev in events:
ctx.log.info(f"{ev['title']}: {ev['volume']:.0f} USDC volume")Error handling
def tick(ctx):
try:
positions = ctx.polymarket.get_positions()
except Exception as e:
ctx.log.error(f"Polymarket unreachable: {e}")
returnPolymarketError is raised for CLOB errors. Network failures raise httpx.HTTPStatusError.
Cloud-Run proxy
PRO+ workers route Polymarket Gamma + Data traffic through the Cloud Run proxy with rotating exit IPs. The CLOB calls go through py-clob-client directly (it manages its own session).
Recipes
Bid below midpoint, exit at favorable price
def tick(ctx):
token = ctx.state.get("target_token")
if not token:
return
mid = ctx.polymarket.get_midpoint(token)
spread = ctx.polymarket.get_spread(token)
# Already in?
open_orders = ctx.polymarket.get_open_orders()
if any(o.get("asset_id") == token for o in open_orders):
return
# Bid 2 cents below mid
bid_price = round(mid - 0.02, 2)
if bid_price < 0.05: # too cheap to be useful
return
ctx.polymarket.create_order(
token_id=token, side="BUY",
size=50, price=bid_price,
)
ctx.log.info(f"Bid {bid_price} for token, mid={mid:.3f}")Volume-weighted event scanner
Find the most-traded events of the day to focus research.
def tick(ctx):
events = ctx.polymarket.search_events("", limit=200)
by_volume = sorted(
[e for e in events if e.get("active") and float(e.get("volume24hr", 0)) > 0],
key=lambda x: float(x.get("volume24hr", 0)),
reverse=True,
)[:10]
for e in by_volume:
vol = float(e.get("volume24hr", 0))
ctx.log.info(f"{e['title']:60} ${vol:,.0f}")
ctx.monitor.metric(f"poly_24h_{e['id']}", vol)Risk-on cleanup before market close
import time
from datetime import datetime, timezone
def tick(ctx):
# 5 minutes before EOD, cancel everything
now = datetime.now(timezone.utc)
if now.hour == 23 and now.minute >= 55:
result = ctx.polymarket.cancel_all_orders()
ctx.log.info(f"EOD cancel-all: {result}")Reference
| Polymarket endpoint | SDK method |
|---|---|
GET gamma /events?search= | search_events |
GET gamma /events/{id} | get_event |
GET gamma /markets?search= | search_markets |
GET gamma /markets/{id} | get_market |
GET gamma /markets?active=true | get_active_markets |
GET data /trades?market= | get_market_trades |
POST clob /book | get_orderbook |
POST clob /midpoint | get_midpoint |
POST clob /price | get_price |
POST clob /spread | get_spread |
POST clob /last-trade-price | get_last_trade_price |
GET data /positions?user= | get_positions |
POST clob /balance-allowance | get_balance |
POST clob /trades | get_history |
POST clob /order | create_order |
DELETE clob /order/{id} | cancel_order |
DELETE clob /orders | cancel_all_orders |
GET clob /orders | get_open_orders |
GET clob /order/{id} | get_order |