py_clob_client is Polymarket’s official Python SDK for trading on the CLOB (Central Limit Order Book) API. This reference documents every public method — signature, parameters, return type, and working code example — so you can look up exactly how to call any function without reading source code.
For a step-by-step tutorial on the full Polymarket API ecosystem, see the Polymarket API Tutorial. For side-by-side comparison with Kalshi’s Python SDK, see the Prediction Market API Reference.
Try it live: Test Polymarket endpoints in the API Playground — no setup required.
Why This Page Exists (What the README Doesn’t Cover)
Polymarket substantially rewrote the py-clob-client README in early 2026 — it now includes full copy-pasteable examples for unauthenticated and authenticated clients, market/limit orders, and token allowances. If you just need to get started, the README is now good enough. This page exists for everything after that:
- The Common Pitfalls table documents the bugs and silent failures the README still doesn’t mention — wei conversion,
signature_typeconfusion, FOK on thin markets, missing allowances - The Known Issues & Workarounds section tracks active GitHub bugs with solutions, updated as new issues appear
- The deep-dive sub-pages go far beyond the README’s examples with production error handling, retry patterns, and real-world code
- The Choosing the Right Method Pattern decision tree helps you pick the right workflow for your use case
- The Polymarket US compatibility note answers whether py-clob-client works with the CFTC-regulated US platform (it doesn’t)
- The RFQ documentation covers institutional trading parameters the README barely mentions
New (April 2026): Polymarket has published
py-clob-client-v2andclob-client-v2(TypeScript) as early-stage successor repos. Both were created in April 2026 with empty descriptions and minimal activity — this reference still documents v1 (py-clob-clientv0.34.6), which remains the production SDK with 1.1M monthly PyPI downloads. We’ll add a v2 migration section once Polymarket ships a release and publishes migration notes. Watch both repos if you’re building new integrations.
Quick Method Lookup
| Method | Category | What It Does |
|---|---|---|
get_balance() | Balance | USDC balance in wei |
get_balance_allowance() | Balance | Balance + token allowance |
get_positions() | Positions | All open positions |
get_price() | Order Book | Current price for token/side |
get_midpoint() | Order Book | Mid between best bid/ask |
get_order_book() | Order Book | Full bids/asks |
get_order_books() | Order Book | Batch — multiple tokens |
create_order() | Trading | Sign a limit order |
create_market_order() | Trading | Sign a market order |
create_and_post_order() | Trading | Sign + post in one call |
post_order() | Trading | Submit signed order |
post_orders() | Trading | Batch submit (up to 15) |
get_order() | Management | Single order by ID |
get_orders() | Management | All open orders |
cancel() | Management | Cancel one order |
cancel_all() | Management | Cancel all orders |
create_or_derive_api_creds() | Auth | Derive API credentials |
set_api_creds() | Auth | Activate credentials |
Common Pitfalls
Before diving into the method reference, here are the issues that trip up most developers — none of which are documented in the official README:
| Pitfall | What Happens | Fix |
|---|---|---|
| Wei conversion | get_balance() returns "1250000000" and you think you have 1.25B USDC | Divide by 1e6 — USDC uses 6 decimals, not 18 |
Wrong signature_type | Auth silently fails, orders rejected | Use 0 for MetaMask/EOA, 1 for Magic/email, 2 for browser/Gnosis Safe (most common) |
Missing funder | Orders attributed to wrong address | Required for proxy wallets — this is the address holding your funds |
| FOK on thin markets | Market orders fail completely | If liquidity is thin, use FAK (fill-and-kill) for partial fills instead of FOK |
| No rate limit handling | Script crashes on 429 | SDK doesn’t handle retries — implement exponential backoff yourself. See Rate Limits Guide |
| Stale order book | Prices moved between read and trade | Use WebSocket for real-time data. See WebSocket Guide |
| Missing allowance (EOA) | Orders fail with no clear error | Check get_balance_allowance() first — allowance must be > 0 |
| Hyphen vs underscore | import py-clob-client fails | Package installs as py-clob-client but imports as py_clob_client |
Deep-Dive Method Guides
For expanded examples, error handling, and production patterns, see the dedicated guides for the most-used methods:
- get_order_book() — OrderBookSummary, batch retrieval, spread calculation, depth analysis
- get_balance_allowance() — BalanceAllowanceParams, AssetType, wei conversion, pre-trade validation
- get_positions() — Position tracking, P&L calculation, Data API alternative
- create_order() — OrderArgs, MarketOrderArgs, GTC/FOK/FAK, tick_size, neg_risk, batch orders
Known Issues & Workarounds (April 2026)
These are active bugs and pain points from the py-clob-client GitHub Issues page as of April 2026. If you’re hitting an error, check here first.
| Issue | Problem | Workaround |
|---|---|---|
| #326 — Fee docs mismatch | Fee documentation contradicts the CLOB API /fee-rate endpoint response for Sports markets, so hardcoded fee assumptions may undercharge or overcharge | Always read the live /fee-rate endpoint per market at runtime — do not cache fees from the docs. Re-verify when Sports markets resolve |
| #323 — Market-order rounding | Market order price uses round_normal instead of round_down, producing different fills from the official TypeScript client on identical inputs | Until a fix lands, floor the price yourself with math.floor(price * 10**tick_exp) / 10**tick_exp before building MarketOrderArgs. Track PR #325 |
| #319 — API balance reads zero | get_balance() returns 0 via the SDK while the Polymarket UI shows a positive balance (e.g. $3.40) for the same wallet | Double-check funder and signature_type (see Common Pitfalls). Cross-check with the Data API /positions endpoint. If they disagree, re-derive API creds |
| #316 — Cancel returns only IDs | cancel() / cancel_all() return just order IDs, so you can’t see sizeMatched at the moment of cancellation | After cancelling, immediately call get_order(order_id) to read the final matched size. PR #320 proposes returning full objects |
| #311 — Selling positions fails | Errors when trying to sell positions through the CLOB despite valid balances | Verify the outcome-token allowance via get_balance_allowance(AssetType.CONDITIONAL, token_id=...) before placing the SELL. If the conditional allowance is 0, re-approve on-chain |
| #301 — Minimum order size | Orders rejected with “Size (1.08) lower than the minimum: 5” | Polymarket enforces a minimum order size per market. Check the market’s min_order_size via the Gamma API before placing orders. Not all markets have the same minimum |
| #300 — Inconsistent balance allowance | get_balance_allowance() returns discrepant balance calculations | Compare against get_balance() as a sanity check. If the values diverge, re-derive your API credentials with create_or_derive_api_creds() |
| #299 — macOS install fails (Python 3.13) | pip install py-clob-client fails due to ckzg wheel build errors on macOS | Pin Python to 3.11 or 3.12. Alternatively, install ckzg separately first: pip install ckzg then retry. Python 3.13 support is pending upstream fixes |
| #297 — Proxy wallet allowances stuck at 0 | CLOB balance shows correctly but allowances remain at 0, preventing trading | Verify your funder address matches the proxy wallet holding funds. Re-approve allowances on-chain if the proxy contract was recently upgraded |
| #295 — Can’t redeem closed markets | Unable to redeem positions in resolved markets via the SDK | Use the Web3 contract directly to call the redeemPositions() function on the CTF Exchange contract, or redeem through the Polymarket UI |
| #294 — Sell order signature errors | Reversed maker/taker semantics causing invalid signature errors on SELL orders | Ensure you’re on v0.34.6. If the error persists, verify your signature_type matches your wallet type and that you’re using the correct funder for proxy wallets |
| #293 — API trades not visible in UI | Orders placed via the API don’t appear in the Polymarket web UI | This happens when the API wallet address doesn’t match the UI session wallet. Verify you’re using the same private key / proxy address in both contexts |
| #292 — WSS silent freeze | CLOB WebSocket server accepts connection and subscription but sends no book data, so clients silently stall | Add a watchdog: if no book message arrives within 15s of subscribing, force-reconnect. See the Polymarket WebSocket Guide for reference code |
| #288 — EOA signature errors | 400 invalid signature placing simple market orders with EOA + MetaMask | Confirm signature_type=0 for EOA (not 2) and that chain_id=137. If still failing, drop back to limit orders as a workaround |
| #287 — False balance/allowance errors | “not enough balance / allowance” despite full approvals and correct balance | Related to #297. Re-derive API creds with create_or_derive_api_creds(); if a proxy wallet, verify funder matches the proxy holding funds |
#284 — 401 on /order | 401 Unauthorized on POST /order even though L2 API creds and other private endpoints work for the same session | Re-create API creds — this usually indicates stale or wrong-scope credentials. Verify the host is https://clob.polymarket.com, not a regional mirror |
Tip: Watch the GitHub Issues page and PRs — there are currently 67 open PRs. Notable pending fixes include round_down for market orders (#325), configurable HTTP timeouts and error context (#318), proxy support on
ClobClient(#315), type hint improvements (#313), thesetup.py→pyproject.tomlmigration (#312), and Enum fixes (#308).
Installation and Setup
Installing py_clob_client
pip install py-clob-client
The package name uses hyphens (py-clob-client) but the Python import uses underscores (py_clob_client).
Community alternative: The
polymarket-apispackage (v0.5.7, Python 3.12+) wraps CLOB, Gamma, Data, Web3, WebSocket, and GraphQL clients with Pydantic validation. See the full comparison below if you’re deciding between the two.
ClobClient Initialization
ClobClient is the main entry point for all SDK operations.
Signature:
ClobClient(
host: str,
key: str = None,
chain_id: int = None,
signature_type: int = 0,
funder: str = None
)
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
host | str | Yes | CLOB API base URL. Use https://clob.polymarket.com for production |
key | str | For trading | Your Ethereum private key (hex string with or without 0x prefix) |
chain_id | int | For trading | Blockchain chain ID. Use 137 for Polygon mainnet |
signature_type | int | No | Wallet type: 0 = EOA (MetaMask), 1 = Magic/email wallet, 2 = Gnosis Safe / browser wallet proxy (most common) |
funder | str | For proxy wallets | The proxy wallet address that holds your funds on Polymarket |
Example — Read-only client (no authentication):
from py_clob_client.client import ClobClient
client = ClobClient("https://clob.polymarket.com")
# Public endpoints work without auth
price = client.get_price(token_id="<token-id>", side="BUY")
Example — Authenticated client (EOA wallet):
from py_clob_client.client import ClobClient
# Use signature_type=2 for browser/Gnosis Safe wallets (most common)
client = ClobClient(
"https://clob.polymarket.com",
key="<your-private-key>",
chain_id=137
)
client.set_api_creds(client.create_or_derive_api_creds())
Example — Authenticated client (Magic/email wallet):
from py_clob_client.client import ClobClient
client = ClobClient(
"https://clob.polymarket.com",
key="<your-private-key>",
chain_id=137,
signature_type=1, # Magic/email; use 2 for browser/Gnosis Safe (most common)
funder="<your-proxy-wallet-address>"
)
client.set_api_creds(client.create_or_derive_api_creds())
Authentication: create_or_derive_api_creds()
Derives or creates API credentials from your wallet signature. Call this once, then reuse the credentials.
Signature:
client.create_or_derive_api_creds() -> ApiCreds
Returns: An ApiCreds object containing your API key, secret, and passphrase. Pass this to set_api_creds() to enable authenticated endpoints.
Usage:
creds = client.create_or_derive_api_creds()
client.set_api_creds(creds)
# Now you can use authenticated endpoints (trading, balance, etc.)
You only need to derive credentials once per session. The credentials are deterministic — deriving them again with the same wallet produces the same result.
Readonly API Keys
Added in v0.34.x. You can create read-only API credentials that allow market data and position queries but prevent order placement. Useful for monitoring dashboards and analytics agents that should never trade.
Token Allowances (EOA Wallets)
If you use a standard EOA wallet (MetaMask, hardware wallet), you must set token allowances before trading. This is a one-time on-chain transaction that approves the Polymarket exchange contract to spend your USDC.
Check if allowances are set:
result = client.get_balance_allowance(
BalanceAllowanceParams(asset_type=AssetType.COLLATERAL)
)
if int(result['allowance']) == 0:
print("No allowance set — you need to approve USDC spending first")
# See: https://docs.polymarket.com for approval instructions
If allowance is 0, all order placements will fail silently or with a cryptic error. This is the #1 issue new developers hit with py_clob_client on EOA wallets. Proxy wallet users (email/browser wallets) typically don’t need to worry about this.
Balance Methods
get_balance() Method
Returns your USDC balance on Polymarket. This is the simplest way to check how much USDC you have available for trading.
Signature:
client.get_balance() -> str
Parameters: None.
Returns: A string representing your USDC balance in wei (1 USDC = 10^6 wei). You need to convert this to a human-readable amount.
Example:
from py_clob_client.client import ClobClient
client = ClobClient(
"https://clob.polymarket.com",
key="<your-private-key>",
chain_id=137
)
client.set_api_creds(client.create_or_derive_api_creds())
balance_wei = client.get_balance()
balance_usdc = int(balance_wei) / 1e6
print(f"Balance: {balance_usdc:.2f} USDC")
# Example output: Balance: 1250.00 USDC
Common pitfall: The return value is in wei (micro-USDC), not USDC. Divide by 1e6 (or 10**6) to get the USDC amount. Forgetting this conversion is a frequent source of bugs.
get_balance_allowance() Method
Returns both your balance and the token allowance for a specific asset type. This is more detailed than get_balance() and is essential for checking whether you have sufficient allowance set for trading.
Signature:
client.get_balance_allowance(params: BalanceAllowanceParams = None) -> dict
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
params | BalanceAllowanceParams | No | Specifies the asset type and optional token ID |
BalanceAllowanceParams fields:
| Field | Type | Description |
|---|---|---|
asset_type | AssetType | AssetType.COLLATERAL for USDC, or AssetType.CONDITIONAL for outcome tokens |
token_id | str | Required when asset_type is CONDITIONAL — the specific outcome token ID |
Returns: A dictionary with balance and allowance fields, both as strings in wei.
Example — Check USDC balance and allowance:
from py_clob_client.client import ClobClient
from py_clob_client.clob_types import BalanceAllowanceParams, AssetType
client = ClobClient(
"https://clob.polymarket.com",
key="<your-private-key>",
chain_id=137
)
client.set_api_creds(client.create_or_derive_api_creds())
result = client.get_balance_allowance(
BalanceAllowanceParams(asset_type=AssetType.COLLATERAL)
)
print(f"Balance: {int(result['balance']) / 1e6:.2f} USDC")
print(f"Allowance: {result['allowance']}")
Example — Check conditional token balance:
result = client.get_balance_allowance(
BalanceAllowanceParams(
asset_type=AssetType.CONDITIONAL,
token_id="<outcome-token-id>"
)
)
print(f"Token balance: {result['balance']}")
print(f"Token allowance: {result['allowance']}")
When to use get_balance() vs get_balance_allowance():
- Use
get_balance()for a quick USDC balance check (e.g., before placing an order). - Use
get_balance_allowance()when you need to verify allowances are set (important for EOA wallets) or when checking conditional token balances for specific outcomes.
Position Methods
get_positions() Method
Retrieves your current positions across all markets. Each position represents your holdings of a specific outcome token.
Signature:
client.get_positions() -> list
Parameters: None required. The method fetches all positions for the authenticated user.
Returns: A list of position dictionaries, each containing details about a specific token holding.
Response fields per position:
| Field | Type | Description |
|---|---|---|
asset | dict | Token details including token_id and condition_id |
size | str | Number of shares held |
avgPrice | str | Average entry price per share |
side | str | Side of the position |
Example — List all positions:
from py_clob_client.client import ClobClient
client = ClobClient(
"https://clob.polymarket.com",
key="<your-private-key>",
chain_id=137,
signature_type=1, # Magic/email; use 2 for browser/Gnosis Safe (most common)
funder="<your-proxy-address>"
)
client.set_api_creds(client.create_or_derive_api_creds())
positions = client.get_positions()
for pos in positions:
token_id = pos["asset"]["token_id"]
size = float(pos["size"])
avg_price = float(pos["avgPrice"])
print(f"Token: {token_id[:16]}... | Shares: {size} | Avg Price: ${avg_price:.2f}")
Example — Check if you have an existing position before placing an order:
def has_position(client, target_token_id):
"""Check if we already hold a position in this outcome."""
positions = client.get_positions()
for pos in positions:
if pos["asset"]["token_id"] == target_token_id:
return float(pos["size"]) > 0
return False
# Only buy if we don't already have a position
if not has_position(client, "<token-id>"):
order = OrderArgs(token_id="<token-id>", price=0.50, size=10.0, side=BUY)
signed = client.create_order(order)
client.post_order(signed, OrderType.GTC)
Note: For querying positions via the public Data API (which doesn’t require authentication), you can use:
curl "https://data-api.polymarket.com/positions?user=YOUR_WALLET_ADDRESS"
Or via the Polymarket CLI:
polymarket data positions YOUR_WALLET_ADDRESS
Order Types
MarketOrderArgs
MarketOrderArgs is the data class for specifying market orders — orders that execute immediately against resting liquidity at the best available price.
Import:
from py_clob_client.clob_types import MarketOrderArgs, OrderType
from py_clob_client.order_builder.constants import BUY, SELL
Fields:
| Field | Type | Required | Description |
|---|---|---|---|
token_id | str | Yes | The outcome token ID to trade |
amount | float | Yes | Dollar amount in USDC to spend (for BUY) or number of shares to sell (for SELL) |
side | str | Yes | BUY or SELL |
order_type | OrderType | Yes | Must be OrderType.FOK (fill-or-kill) for market orders |
fee_rate_bps | int | No | Custom fee rate in basis points. Defaults to the standard rate |
Example — Buy $25 worth of YES tokens at market price:
from py_clob_client.clob_types import MarketOrderArgs, OrderType
from py_clob_client.order_builder.constants import BUY
market_order = MarketOrderArgs(
token_id="<token-id>",
amount=25.0,
side=BUY,
order_type=OrderType.FOK
)
signed = client.create_market_order(market_order)
response = client.post_order(signed, OrderType.FOK)
print(response)
Example — Sell 10 shares at market price:
from py_clob_client.order_builder.constants import SELL
sell_order = MarketOrderArgs(
token_id="<token-id>",
amount=10.0,
side=SELL,
order_type=OrderType.FOK
)
signed = client.create_market_order(sell_order)
response = client.post_order(signed, OrderType.FOK)
Market orders use FOK (fill-or-kill) — the order must fill completely or it’s rejected entirely. If there isn’t enough resting liquidity to fill your full amount, the order fails. For partial fills, use a limit order with FAK (fill-and-kill) instead.
OrderArgs
OrderArgs is the data class for limit orders — orders that specify an exact price and rest on the order book until filled.
Import:
from py_clob_client.clob_types import OrderArgs, OrderType
from py_clob_client.order_builder.constants import BUY, SELL
Fields:
| Field | Type | Required | Description |
|---|---|---|---|
token_id | str | Yes | The outcome token ID to trade |
price | float | Yes | Price per share in USDC (0.01 to 0.99) |
size | float | Yes | Number of shares to buy or sell |
side | str | Yes | BUY or SELL |
expiration | int | No | Unix timestamp for order expiration. If omitted, order is GTC |
Example — Place a limit buy order:
from py_clob_client.clob_types import OrderArgs, OrderType
from py_clob_client.order_builder.constants import BUY
order = OrderArgs(
token_id="<token-id>",
price=0.50,
size=10.0,
side=BUY
)
signed = client.create_order(order)
response = client.post_order(signed, OrderType.GTC)
print(response)
Key difference from MarketOrderArgs: OrderArgs uses price + size (price per share and number of shares), while MarketOrderArgs uses amount (total USDC to spend). Limit orders can use GTC (good til cancelled), FOK, or FAK order types, while market orders must use FOK.
Order Placement Methods
Placing orders is the core trading workflow. py_clob_client separates order creation (signing) from order submission (posting) to give you maximum control. For simple trades, use create_and_post_order() as a shortcut. For production bots that need logging, validation, or batching, use the two-step create_order() → post_order() pattern.
Choosing between limit and market orders depends on your urgency. Limit orders (OrderArgs) rest on the book until filled and give you price control — use them when you can wait for your price. Market orders (MarketOrderArgs) execute immediately against resting liquidity but give you no control over the fill price — use them when speed matters more than a few cents of slippage. Most autonomous agents use limit orders with GTC (good-til-cancelled) to avoid overpaying.
create_order() and post_order()
Order placement in py_clob_client is a two-step process: first sign the order locally, then post it to the CLOB API.
Signatures:
client.create_order(order_args: OrderArgs) -> SignedOrder
client.post_order(signed_order: SignedOrder, order_type: OrderType, post_only: bool = False) -> dict
create_order() Parameters:
| Parameter | Type | Description |
|---|---|---|
order_args | OrderArgs | The order specification (token, price, size, side) |
post_order() Parameters:
| Parameter | Type | Description |
|---|---|---|
signed_order | SignedOrder | The signed order from create_order() |
order_type | OrderType | OrderType.GTC, OrderType.FOK, or OrderType.FAK |
post_only | bool | If True, rejects the order if it would immediately match. Use for market-making strategies |
Example:
from py_clob_client.clob_types import OrderArgs, OrderType
from py_clob_client.order_builder.constants import BUY
order = OrderArgs(
token_id="<token-id>",
price=0.45,
size=20.0,
side=BUY
)
signed = client.create_order(order)
response = client.post_order(signed, OrderType.GTC)
print(f"Order ID: {response['orderID']}")
Post-only example (for market makers):
signed = client.create_order(order)
response = client.post_order(signed, OrderType.GTC, post_only=True)
# Rejects if the order would cross the spread
create_and_post_order()
Convenience method that combines create_order() and post_order() into a single call. For simple cases where you don’t need to inspect the signed order before posting.
Signature:
client.create_and_post_order(order_args: OrderArgs, order_type: OrderType = OrderType.GTC) -> dict
Example:
from py_clob_client.client import ClobClient
from py_clob_client.clob_types import OrderArgs
from py_clob_client.order_builder.constants import BUY
client = ClobClient("https://clob.polymarket.com", key="<key>", chain_id=137)
client.set_api_creds(client.create_or_derive_api_creds())
resp = client.create_and_post_order(OrderArgs(
price=0.50,
size=100.0,
side=BUY,
token_id="<token-id>"
))
print(resp)
Use the two-step create_order() + post_order() workflow when you need to log, validate, or modify the signed order before submitting. Use create_and_post_order() for straightforward trades.
create_market_order()
Creates and signs a market order for immediate execution.
Signature:
client.create_market_order(order_args: MarketOrderArgs) -> SignedOrder
Example:
from py_clob_client.clob_types import MarketOrderArgs, OrderType
from py_clob_client.order_builder.constants import BUY
market_order = MarketOrderArgs(
token_id="<token-id>",
amount=50.0,
side=BUY,
order_type=OrderType.FOK
)
signed = client.create_market_order(market_order)
response = client.post_order(signed, OrderType.FOK)
Batch Orders
Place up to 15 orders in a single API call. Essential for market makers updating multiple price levels.
Signature:
client.post_orders(signed_orders: list[SignedOrder], order_type: OrderType) -> dict
Example:
from py_clob_client.clob_types import OrderArgs, OrderType
from py_clob_client.order_builder.constants import BUY
orders = []
for price in [0.48, 0.49, 0.50]:
order = OrderArgs(
token_id="<token-id>",
price=price,
size=50.0,
side=BUY
)
orders.append(client.create_order(order))
response = client.post_orders(orders, OrderType.GTC)
Order Management Methods
After placing orders, you need to track their status, monitor fills, and cancel orders that are no longer wanted. These methods give you full visibility into your open and historical orders. In production bots, the typical pattern is: place orders, poll get_orders() periodically to check fill status, and cancel stale unfilled orders with cancel() or cancel_all().
get_order()
Retrieves a single order by its ID.
Signature:
client.get_order(order_id: str) -> dict
Example:
order = client.get_order("<order-id>")
print(f"Status: {order['status']}")
print(f"Filled: {order['size_matched']} / {order['original_size']}")
get_orders()
Retrieves all your open orders, optionally filtered by market.
Signature:
client.get_orders(params: GetOrdersParams = None) -> list
Example:
open_orders = client.get_orders()
for order in open_orders:
print(f"Order {order['id']}: {order['side']} {order['original_size']} @ {order['price']}")
cancel()
Cancels a single order by ID.
Signature:
client.cancel(order_id: str) -> dict
Example:
response = client.cancel(order_id="<order-id>")
print(response)
cancel_all()
Cancels all your open orders across all markets.
Signature:
client.cancel_all() -> dict
Example:
response = client.cancel_all()
print(f"Cancelled all open orders")
RFQ (Request for Quote) Methods
Added in v0.31.0. Significantly refined in v0.34.5 (split into requester/quoter views). RFQ methods enable negotiated pricing for large orders. Instead of placing an order directly on the book, you request a quote from market makers who respond with firm prices.
Who uses this: Institutional traders, high-volume agents, and market makers executing large positions where market impact is a concern. The typical flow is: requester asks for a quote → market makers respond with prices → requester accepts the best quote → the trade executes off-book. Most retail traders and simple bots should use standard limit/market orders.
get_rfq_quote()
Requests a price quote for a specified token and size.
Signature:
client.get_rfq_quote(params: RfqQuoteParams) -> dict
RfqQuoteParams fields:
| Field | Type | Required | Description |
|---|---|---|---|
token_id | str | Yes | The outcome token to get a quote for |
side | str | Yes | BUY or SELL |
size | float | Yes | Number of shares to buy or sell |
Returns: A dictionary containing quote_id, price, size, expiry (Unix timestamp), and maker (the market maker’s address).
Example — Request a quote for a large position:
from py_clob_client.clob_types import RfqQuoteParams
quote = client.get_rfq_quote(RfqQuoteParams(
token_id="<token-id>",
side="BUY",
size=5000.0
))
print(f"Quote: {quote['size']} shares @ ${quote['price']} (expires {quote['expiry']})")
accept_rfq_quote()
Accepts a previously received quote by its ID, triggering the off-book trade.
Signature:
client.accept_rfq_quote(quote_id: str) -> dict
Example:
response = client.accept_rfq_quote(quote["quote_id"])
print(f"RFQ trade executed: {response}")
RFQ vs Standard Orders
| Factor | Standard Orders | RFQ |
|---|---|---|
| Market impact | Visible on the order book | Off-book, no impact |
| Price discovery | You set the price (limit) or take the book (market) | Market makers compete to quote you |
| Size limits | Batch up to 15 orders | Single large block trades |
| Speed | Immediate (GTC rests, FOK fills or fails) | Requires quote round-trip |
| Best for | < $10K positions, automated strategies | > $10K positions, institutional flow |
Note: RFQ endpoint views were split into requester and quoter perspectives in v0.34.5 (Jan 2026). If you’re running an older version, upgrade to get the correct response schema for your role.
Price and Order Book Methods
These methods provide market data — current prices, midpoints, and full order books. They work without authentication, so you can use them from a read-only client. For real-time data, consider the Polymarket WebSocket API instead of polling these endpoints. Polling is simpler to implement but consumes rate limit budget and introduces latency between price changes and your bot’s awareness of them.
When building a trading bot, the typical pattern is to use get_order_book() for initial state, then switch to WebSocket for live updates. Use get_midpoint() for quick price checks, and get_price() when you need the best available price on a specific side (bid vs ask).
get_price()
Returns the current price for a specific token and side.
Signature:
client.get_price(token_id: str, side: str) -> str
Parameters:
| Parameter | Type | Description |
|---|---|---|
token_id | str | The outcome token ID |
side | str | "BUY" or "SELL" — the side you want the price for |
Example:
buy_price = client.get_price(token_id="<token-id>", side="BUY")
sell_price = client.get_price(token_id="<token-id>", side="SELL")
print(f"Buy at: ${buy_price} | Sell at: ${sell_price}")
get_midpoint()
Returns the midpoint between the best bid and best ask.
Signature:
client.get_midpoint(token_id: str) -> str
Example:
mid = client.get_midpoint(token_id="<token-id>")
print(f"Midpoint: ${mid}")
get_order_book()
Returns the full order book for a token.
Signature:
client.get_order_book(token_id: str) -> dict
Returns: A dictionary with bids and asks arrays, each containing [price, size] entries sorted by price. The response now also includes enriched market metadata fields (question, slug, end date, outcome labels) that previously required separate Gamma API calls.
Example:
book = client.get_order_book(token_id="<token-id>")
print("Top 5 bids:")
for bid in book["bids"][:5]:
print(f" ${bid['price']} — {bid['size']} shares")
print("Top 5 asks:")
for ask in book["asks"][:5]:
print(f" ${ask['price']} — {ask['size']} shares")
get_order_books()
Batch endpoint — fetch order books for multiple tokens in one request.
Signature:
client.get_order_books(params: list[BookParams]) -> list
Example:
from py_clob_client.clob_types import BookParams
books = client.get_order_books([
BookParams(token_id="<token-id-1>"),
BookParams(token_id="<token-id-2>"),
])
Bridge Operations
The Polymarket Bridge API now includes a POST /withdraw endpoint for bridging USDC.e to other chains. This endpoint is accessed via the Bridge API (https://bridge.polymarket.com/withdraw), not through py_clob_client directly. See the Polymarket API Guide — Bridge API for endpoint documentation and example code.
Choosing the Right Method Pattern
For quick reference, here is how common trading workflows map to SDK methods:
| Workflow | Methods | Notes |
|---|---|---|
| Check if I can trade | get_balance() → get_balance_allowance() | Verify USDC balance (divide by 1e6) and that allowance > 0 |
| Find a market to trade | Use Gamma API (requests.get) → get_order_book() | Gamma for discovery, CLOB for execution data |
| Place a simple bet | create_and_post_order() | One-step convenience method |
| Place a bet with validation | create_order() → inspect → post_order() | Two-step when you need to log or validate before submitting |
| Place many orders at once | Loop create_order() → post_orders() | Batch up to 15 orders in a single API call |
| Buy immediately at market price | create_market_order() → post_order(FOK) | Use FOK for full fill; consider FAK if liquidity is thin |
| Check position before trading | get_positions() → check → create_order() | Avoid doubling down on existing positions |
| Cancel everything | cancel_all() | Emergency reset; also useful at bot shutdown |
| Monitor fills | get_order() or get_orders() | Poll periodically or use WebSocket for real-time fill events |
For expanded examples and production patterns on each method, see the deep-dive guides linked in the method lookup table at the top of this page.
Version History
| Version | Date | Key Changes | Migration Notes |
|---|---|---|---|
| v0.34.6 | Feb 19, 2026 | Tick size cache fix — resolves stale tick sizes causing order rejections | Recommended upgrade for all users. No breaking changes |
| v0.34.5 | Jan 13, 2026 | RFQ endpoint split into requester/quoter views; RFQ params cleanup | Breaking for RFQ users: response schema changed — requester and quoter now get different response fields |
| v0.34.4 | Jan 6, 2026 | Orderbook hash resolution fix | No breaking changes |
| v0.34.3 | Jan 6, 2026 | HeartBeats V1 client update; post-only order handling improvements | No breaking changes. Post-only rejections now return clearer errors |
| v0.34.1 | Dec 24, 2025 | Pagination fix for get_orders() and get_trades() | No breaking changes. Fixes missing results on paginated queries |
| v0.34.0 | Dec 21, 2025 | RFQ: invert price for requester order fix | Breaking for RFQ users: price field semantics changed for requester-side orders |
| v0.31.0 | 2025 | RFQ methods, readonly API keys, JSON serialization fix | Major feature release. No breaking changes for existing code |
For the full changelog, see py-clob-client releases on GitHub.
Polymarket US Compatibility
py_clob_client does not work with Polymarket US. This is the most common source of confusion for US-based developers.
Polymarket US is a CFTC-regulated Designated Contract Market (DCM) that launched after receiving CFTC approval in November 2025. It operates on a completely separate infrastructure from the global Polymarket platform:
| Global Polymarket | Polymarket US | |
|---|---|---|
| API endpoint | clob.polymarket.com | api.polymarket.us |
| Auth scheme | EIP-712 (wallet signing) | Ed25519 (key-pair signing) |
| Python SDK | py-clob-client | Separate CFTC-compliant SDKs |
| KYC required | No | Yes (mandatory) |
| Regulation | Unregulated (non-US) | CFTC-regulated DCM |
If you need to integrate with the US platform, you’ll need to use the SDKs provided at polymarketexchange.com/developers.html. Pointing ClobClient at api.polymarket.us will fail — the authentication schemes are fundamentally incompatible.
The Polymarket US developer portal now notes that the Trading Gateway is being deprecated in favor of the Exchange Gateway — new integrations should target the Exchange Gateway. No official Python SDK has been announced for Polymarket US as of April 2026.
For more on the differences between global and US Polymarket APIs, see the Polymarket API Guide.
py-clob-client vs polymarket-apis
Two Python packages exist for Polymarket development. Here’s when to use each.
| py-clob-client | polymarket-apis | |
|---|---|---|
| Maintainer | Polymarket (official) | Community (qualiaenjoyer) |
| Version | v0.34.6 | v0.5.7 |
| PyPI downloads | 1.1M/month | Lower volume |
| GitHub stars | 1,058 | 171 |
| Python version | 3.9+ | 3.12+ |
| API coverage | CLOB only | CLOB, Gamma, Data, Web3, WebSockets, GraphQL |
| Type safety | Basic types | Pydantic validation |
| Install | pip install py-clob-client | pip install polymarket-apis |
Use py-clob-client when:
- You’re building a trading bot focused on order placement and management
- You need the widest Python version compatibility (3.9+)
- You want the official SDK that Polymarket maintains and tests against their API
- Your project only interacts with the CLOB (order book, orders, positions)
Use polymarket-apis when:
- You need market discovery (Gamma), portfolio analytics (Data API), and real-time streaming (WebSockets) in one package
- You want Pydantic-validated response models with type safety
- You’re on Python 3.12+ and don’t need backward compatibility
- You want a single unified client instead of juggling
py_clob_client+requestsfor Gamma + custom WebSocket code
Can you use both? Yes — some projects use polymarket-apis for market discovery and data, then switch to py-clob-client for order execution where they want the official signing logic. The two packages don’t conflict.
See Also
- get_order_book() Deep Dive — OrderBookSummary, batch retrieval, spread and depth analysis
- get_balance_allowance() Deep Dive — BalanceAllowanceParams, wei conversion, pre-trade validation
- get_positions() Deep Dive — P&L calculation, Data API alternative, position management
- create_order() Deep Dive — OrderArgs, MarketOrderArgs, tick_size, neg_risk, batch orders
- Polymarket API: The Complete Developer Guide — Conceptual overview of all Polymarket APIs
- Polymarket WebSocket & Orderbook Guide — Real-time streaming and orderbook reconstruction
- Polymarket Auth Troubleshooting — POLY headers, signatureType, common errors
- Polymarket TypeScript SDK Reference — @polymarket/clob-client equivalent
- Polymarket Rust SDK Reference — High-performance Rust SDK
- Polymarket Gamma API Deep Dive — Market discovery and price history
- Polymarket Subgraph Guide — On-chain data via GraphQL
- Prediction Market API Reference — Side-by-side Polymarket vs Kalshi comparison
- Polymarket Rate Limits Guide — Concrete per-endpoint limits and retry strategies
- py-clob-client on GitHub — Official source code and issues
- API Playground — Test endpoints live in your browser
- Sports Betting vs Prediction Markets — How py_clob_client fits in the broader betting API landscape
- Offshore Sportsbook API Guide — Data access patterns for traditional sportsbooks (no SDK equivalent)
- Polymarket Changelog — Official changelog for API updates, fee changes, and new endpoints
This reference is maintained by AgentBets.ai. Found an error or SDK change we missed? Let us know on Twitter.
Not financial advice. Built for builders.
