Polymarket API Authentication & Wallet Setup in Python
Step-by-step tutorial for generating an API key, signing orders with a Polygon-compatible EOA, and placing your first limit order on Polymarket's CLOB in under 50 lines of Python using the official py-clob-client SDK.
Polymarket API authentication and wallet setup trips up more developers than the trading logic itself. The credential chain is non-standard — you need a Polygon EOA, a separate API key derived from that wallet, and a correct EIP-712 signing flow — and the official docs skip over enough detail that most people spend a day debugging before their first order lands. This guide closes that gap in one sitting.
Prerequisites and What You're Actually Connecting To
Polymarket's order book runs on a custom CLOB (Central Limit Order Book) that settles on Polygon PoS. Orders are signed off-chain and submitted to Polymarket's matching engine; fills are then settled on-chain via the CTF Exchange contract. That distinction matters: you pay no gas for placing or cancelling orders, only for deposits and withdrawals. Funds live in a Polymarket Vault contract at 0x4bFb41d5B3570DeFd03C39a9A4D8dE6Bd8B8982E on Polygon mainnet.
You need:
- Python 3.10+
- A funded Polygon wallet (USDC.e is the collateral token — contract
0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174) py-clob-clientinstalled:pip install py-clob-client- A minimum USDC.e balance of around $1 to place test orders — the CLOB enforces a minimum notional
The SDK wraps the REST API at https://clob.polymarket.com and handles signature construction, so you don't need to touch raw EIP-712 encoding yourself. That said, knowing the signing scheme is important for debugging.
Generating API Credentials from Your EOA
Polymarket uses a two-layer auth model. Your Polygon private key (the EOA key) proves wallet ownership. From that you derive a separate API key, secret, and passphrase — a triplet that authenticates REST calls. You generate this triplet once and store it; the private key is only needed to derive credentials and sign orders.
import os
from py_clob_client.client import ClobClient
from py_clob_client.constants import POLYGON
HOST = "https://clob.polymarket.com"
KEY = os.environ["POLYGON_PRIVATE_KEY"] # 0x-prefixed hex
# Step 1: Derive API credentials (one-time setup)
client = ClobClient(HOST, key=KEY, chain_id=POLYGON)
creds = client.create_api_key()
print("api_key: ", creds.api_key)
print("api_secret: ", creds.api_secret)
print("passphrase: ", creds.api_passphrase)
Never commit these values to source control. Store them in environment variables or a secrets manager and load them at runtime. The API key has no expiry, but you can revoke and regenerate credentials at any time by calling create_api_key() again — each call creates a new triplet and invalidates older ones, so do this once and store the result.
Authenticating Subsequent Requests
Once you have the triplet, instantiate a fully-authenticated client:
from py_clob_client.clob_types import ApiCreds
creds = ApiCreds(
api_key=os.environ["POLY_API_KEY"],
api_secret=os.environ["POLY_API_SECRET"],
api_passphrase=os.environ["POLY_API_PASSPHRASE"],
)
client = ClobClient(HOST, key=KEY, chain_id=POLYGON, creds=creds)
Every request the SDK makes includes an L2-Authorization header computed from an HMAC-SHA256 signature over timestamp + method + requestPath + body using the secret. The passphrase is passed as a separate header. If you ever implement raw HTTP calls instead of using the SDK, matching this header format exactly is where people get stuck — a single extra whitespace character in the body breaks the HMAC.
Placing Your First Limit Order
Orders reference Polymarket's binary outcome tokens using a tokenId — a 256-bit integer that identifies the YES or NO token for a specific market. You can look these up via client.get_markets() or the Gamma Markets API.
from py_clob_client.clob_types import OrderArgs, OrderType
# Fetch a market to get a real tokenId
markets = client.get_markets()
market = markets["data"][0] # first available market
token_id = market["tokens"][0]["token_id"] # YES token
# Build and sign a limit order: buy YES at 0.42 USDC for 10 USDC notional
order_args = OrderArgs(
token_id=token_id,
price=0.42, # probability / price (0.01 – 0.99)
size=10, # USDC notional
side="BUY",
)
signed_order = client.create_order(order_args)
response = client.post_order(signed_order, OrderType.GTC)
print("order_id:", response["orderID"])
print("status: ", response["status"])
Total runtime: under 50 lines from import to a live resting order. The create_order call constructs and signs the EIP-712 typed data locally using your private key; post_order ships the pre-signed payload to the CLOB. A GTC (Good-Till-Cancelled) order rests in the book until filled or explicitly cancelled with client.cancel(order_id).
Key Trade-offs and Production Considerations
A few things that matter once you move beyond toy orders:
Price precision. Polymarket enforces 2 decimal places on price (e.g., 0.42, not 0.4231) and 0.01 minimum tick. Submitting a non-conformant price silently maps to the nearest valid tick in some SDK versions and rejects in others — validate before posting.
Size limits. Minimum order size is 5 USDC notional. Maximum single-order notional depends on market liquidity; there is no hard cap, but large orders will partially fill if the book is thin. Check client.get_order_book(token_id) for depth before sizing in.
Rate limits. The CLOB enforces a per-IP limit of roughly 10 requests per second on order endpoints and 100/s on read endpoints. In a market-making or arbitrage bot that's scanning dozens of markets concurrently, you'll need to budget your request budget carefully — stagger polls and batch order submissions where the SDK allows.
Allowances. Before your first order can fill, your wallet must approve the CTF Exchange contract to spend your USDC.e. The SDK exposes client.set_allowances() which sends the on-chain approval transactions — this is the only step that costs gas and requires your wallet to hold a small amount of MATIC for fees (0.1 MATIC is plenty).
L1 vs L2 auth. The KEY passed to ClobClient is used for two things: deriving API credentials (L1 auth) and signing individual orders (also L1). The API key triplet is L2 auth for REST calls. Confusing these layers is the most common source of 401 errors — make sure you pass both the private key and the creds object to the client constructor, not just one.
Structuring This for Production
The pattern that holds up in production is a thin credentials module that loads creds from environment variables, a singleton ClobClient, and a queue-based order manager that rate-limits submissions and retries on transient 5xx errors. Polymarket's infrastructure does go down occasionally — especially around high-volume political events — so reconnect logic and idempotency on order IDs matter.
If you want to see how this fits into a full bot — two-sided quoting, inventory tracking, resolution-aware exits — the Polymarket Spread Bot and Polymarket Market-Making Bot we've shipped in production use exactly this auth foundation, with considerably more logic on top. The Polymarket Arb Bot case study shows what the scanning layer looks like over 100+ markets.
If you want to skip the plumbing and get a production-ready Polymarket bot built for you, get in touch — we scope, build and hand over fully working systems, not boilerplate.
Need a bot like this built?
We design, build and run trading bots on Solana, Hyperliquid and Polymarket.
Start a projectMore from the blog
Public vs Paid RPC: How to Run a Fair Benchmark
A fair comparison of public and paid RPC endpoints must account for quotas, workload, freshness and support guarantees.
Read articleRPC Benchmark Percentiles Explained: p50, p95 and p99
Why averages hide the latency spikes that break trading bots, wallets and production blockchain applications.
Read articleHyperliquid REST vs WebSocket Benchmark: What to Measure
A benchmark plan for Hyperliquid market data that separates request latency from streaming freshness and recovery.
Read article