This guide is for builders who still have a bot on py-clob-client-v2 / @polymarket/clob-client-v2 (or older V1 packages) and need the current recommended client path. Polymarket docs now point to the unified SDK (polymarket-client / @polymarket/client). The April 28, 2026 CLOB V2 protocol cutover is still the historical backdrop — this page keeps that context — but the actionable install and method map today is the unified cutover.
For agent builders: automate package uninstall → install, credential resume, and limit / post-only / paced batch placement behind a secure client. What you should not automate is shipping the owner private key into an agent runtime, inventing fee or tick values the SDK no longer asks for, or treating a superseded create_and_post_order call site as current.
Official sources: SDK Migration, Python SDK, Place Orders. Order-placement deep dive (GTC/GTD, FAK/FOK, batches, responses): polymarket-client place_limit_order — slug still says py-clob-client-create-order; that is intentional and unchanged. Auth debugging: Polymarket Auth Troubleshooting. Post-cutover V2 SDK error table (still useful for leftover V2 imports): py-clob-client-v2 Errors.
Last verified against Polymarket unified SDK docs + PyPI polymarket-client==0.9.0: September 2026.
Update (September 2026): Uninstall
py-clob-client-v2(and related builder packages); installpolymarket-client(0.9.0 on PyPI as of 2026-09-04, Python >=3.11). Trading clients areAsyncSecureClient/SecureClient. Order methods:place_limit_order,create_limit_order,place_market_order,create_market_order,post_order,post_orders. TypeScript twin: uninstall@polymarket/clob-client-v2(+ builder packages) → install@polymarket/client+viem; usecreateSecureClient/placeLimitOrder.
Old py-clob-client-v2 | Current polymarket-client |
|---|---|
create_and_post_order | place_limit_order |
create_order | create_limit_order |
create_and_post_market_order | place_market_order |
create_market_order | create_market_order (still exists) |
post_order | post_order |
ClobClient + set_api_creds / two-step create_or_derive_api_key | AsyncSecureClient.create / SecureClient.create |
OrderArgs / Side enum | keyword args; side="BUY" / "SELL" strings in official Python examples |
PartialCreateOrderOptions(tick_size, neg_risk) | resolved automatically by SDK |
Two migrations, one bot
Treat these as stacked, not interchangeable:
- CLOB V2 protocol cutover (April 28, 2026) — Exchange contracts, EIP-712 Exchange domain version
"2", pUSD collateral, redesigned order struct, resting orders wiped. V1 packages cannot sign valid V2 orders. Covered below as prior migration context. - Unified SDK cutover (current recommended path) — one client covers CLOB + Gamma + Data + wallet/relayer flows that previously required separate CLOB, relayer, and builder-signing packages. Official migration doc: Migrate from previous SDKs.
If you never left V1, you still need both. If you already run on py-clob-client-v2 against production CLOB V2, you mainly need the unified package swap and method renames.
Current path: py-clob-client-v2 → polymarket-client (Python)
Install
pip uninstall -y py-clob-client-v2 py-builder-relayer-client py-builder-signing-sdk
pip install polymarket-client
uv remove py-clob-client-v2 py-builder-relayer-client py-builder-signing-sdk
uv add polymarket-client
poetry remove py-clob-client-v2 py-builder-relayer-client py-builder-signing-sdk
poetry add polymarket-client
Requires Python >=3.11. Interfaces:
| Interface | Public data | Trading and account |
|---|---|---|
| Async | AsyncPublicClient | AsyncSecureClient |
| Sync | PublicClient | SecureClient |
Client construction
Previous V2 pattern (two-step ClobClient + create_or_derive_api_key):
# Historical — py-clob-client-v2
from py_clob_client_v2 import ClobClient
l1 = ClobClient(host="https://clob.polymarket.com", chain_id=137, key="<pk>")
creds = l1.create_or_derive_api_key()
client = ClobClient(
host="https://clob.polymarket.com",
chain_id=137,
key="<pk>",
creds=creds,
signature_type=3,
funder="<deposit-wallet>",
)
Unified pattern — create derives or creates CLOB credentials, resolves the account wallet, and configures signing:
import os
from polymarket import AsyncSecureClient
async with await AsyncSecureClient.create(
private_key=os.environ["POLYMARKET_PRIVATE_KEY"],
) as client:
...
Omit wallet for the default Deposit Wallet flow. Pass wallet=... when reconnecting an existing account wallet. Sync twin: SecureClient.create(...).
Resume with stored credentials (same API key) when you already persisted them:
resumed = await AsyncSecureClient.create(
private_key=os.environ["POLYMARKET_PRIVATE_KEY"],
wallet=os.environ["POLYMARKET_WALLET_ADDRESS"],
credentials=credentials,
)
Revoke the current API key with end_authentication() (returns a public client). Builder / Relayer API keys for gasless wallet actions move onto api_key= (BuilderApiKey / RelayerApiKey) at create time — see the official migration page for local vs remote builder signing.
Orders (limit, market, sign-then-post, batch)
One-step limit (omit expiration for GTC; Unix seconds for GTD):
response = await client.place_limit_order(
token_id=yes_token_id,
side="BUY",
price="0.52",
size="10",
)
Two-step sign then submit:
signed = await client.create_limit_order(
token_id=yes_token_id,
side="BUY",
price="0.52",
size="10",
)
response = await client.post_order(signed)
Market (FAK/FOK); user_usdc_balance becomes max_spend:
response = await client.place_market_order(
token_id=yes_token_id,
side="BUY",
amount=10,
max_spend=10,
order_type="FOK",
)
Batch signed orders with post_orders([...]) (order type / post-only live on each signed order). Official Place Orders docs allow 1–15 signed orders per batch call.
Tick size, neg-risk routing, fees, and signing details are resolved by the SDK when you place — you do not pass PartialCreateOrderOptions anymore. For inspection after get_market:
tick_size = market.trading.minimum_tick_size
neg_risk = market.state.neg_risk
Builder attribution: pass builder_code= on place_limit_order (32-byte hex from Settings → Builders). Full GTC/GTD rules, post-only, estimate_market_price, response statuses, and settlement wait: place_limit_order guide.
Market data and account reads (rename highlights)
Previous (py-clob-client-v2) | Unified (polymarket-client) |
|---|---|
get_market("CONDITION_ID") | await client.get_market(slug="MARKET_SLUG") (also id / URL forms in docs) |
get_markets() page dict | client.list_markets(...) async paginator → .first_page() |
get_tick_size / get_neg_risk / get_fee_rate_bps | read market.trading.minimum_tick_size, market.state.neg_risk, market.trading.fee_schedule |
get_order_book(token_id) | await client.get_order_book(token_id="...") |
calculate_market_price(...) | await client.estimate_market_price(...) |
get_open_orders(...) | client.list_open_orders(...) paginator |
get_trades / get_trades_paginated | client.list_account_trades(...) |
get_balance_allowance(BalanceAllowanceParams(...)) | await client.get_balance_allowance(asset_type="COLLATERAL") |
Separate relayer execute for split/merge/redeem | split_position / merge_positions / redeem_positions on the secure client |
Public-only bots can use AsyncPublicClient() without a private key. The unified clients also cover Gamma / Data flows that many bots previously called with raw requests.
Prior migration: CLOB V1 → CLOB V2 (April 28, 2026)
Still accurate as protocol history. Use this if you are auditing why an old bot broke, or if you are reading May 2026 field notes. Prefer the unified SDK section above for new edits.
What changed on the wire (and what did not)
Polymarket shipped, in one coordinated release: new on-chain Exchange contracts (CTF Exchange V2 + Neg Risk CTF Exchange V2), a rewritten CLOB matching backend, EIP-712 Exchange domain version "1" → "2", collateral pUSD (replacing USDC.e), a redesigned order struct, builder attribution on the order, and language-specific V2 SDK packages. Old V1 packages cannot sign valid V2 orders; resting orders were wiped at cutover.
What did not change then (and still holds): host URL clob.polymarket.com, Gamma (gamma-api.polymarket.com), Data API (data-api.polymarket.com), WebSocket hosts, and the L1/L2 API-key model (ClobAuthDomain stays at version "1" — you do not need to regenerate API key/secret/passphrase solely for the protocol bump). Read-only public CLOB paths (/book, /price, /midpoint, /spread) continued to work.
V1 → V2 package / import map (historical)
| Area | V1 | V2 (py-clob-client-v2) |
|---|---|---|
| Install | pip install py-clob-client | pip install py-clob-client-v2 |
| Client | from py_clob_client.client import ClobClient | from py_clob_client_v2 import ClobClient |
| Order types | OrderArgs, MarketOrderArgs, OrderType from clob_types | same names from py_clob_client_v2 + PartialCreateOrderOptions |
| Side | BUY / SELL constants | Side.BUY / Side.SELL |
| Creds helper | create_or_derive_api_creds() | create_or_derive_api_key() |
Order-struct field changes (still on the wire)
| Field | V1 | V2+ |
|---|---|---|
fee_rate_bps | User-settable; signed into the order | Removed from the signed struct. Fees set by the protocol at match time. |
nonce | User-settable uniqueness | Removed. Replaced by timestamp (ms). |
taker | User-settable | Removed. |
timestamp / metadata / builder | n/a | Added (builder set via builder code). |
Raw signers still need Exchange domain version "2" and V2 verifying contracts — see Contracts. Canonical addresses documented for standard / Neg Risk exchanges include 0xE111180000d2663C0091e4f400237545B87B996B and 0xe2222d279d744050d28e00520010520000310F59.
Signature types (wallet still matters)
| Value | Name | Notes |
|---|---|---|
0 | EOA | Default if omitted on older clients. |
1 | POLY_PROXY | Magic Link / Google login proxy wallets. |
2 | GNOSIS_SAFE | Existing Safe-based browser wallet flow. |
3 | POLY_1271 | Deposit wallets / EIP-1271 path emphasized for new API users after V2. |
Unified AsyncSecureClient.create resolves wallet + signing for you when you omit wallet (Deposit Wallet flow) or pass an existing wallet=. Details: POLY_1271 & Smart-Contract Wallets, Wallets and Authentication.
Collateral: pUSD
Trading collateral is pUSD, not USDC.e. UI users get a wrap path on first use; API-only wallets that never wrapped still need to move collateral into pUSD before allowance checks pass. See What Is pUSD? and Polymarket USD.
Builder attribution
Order attribution uses a public builder code on the signed order (not the old per-request HMAC POLY_BUILDER_* header set for attribution). Builder API keys remain relevant for gasless / relayer authorization on the unified client (BuilderApiKey / remote signing helpers) — that is separate from per-order builder_code.
Cutover day (historical)
Pre-cutover test host: https://clob-v2.polymarket.com, parallel with production V1. At ~11:00 UTC on April 28, 2026, V2 took over clob.polymarket.com, V1 stopped accepting orders, and resting orders were wiped (about an hour of downtime in contemporary reports). Production today is the standard CLOB host. Do not point new integrations at the old clob-v2 hostname.
Some V2-era clients exposed get_pre_migration_orders() for read-only reconciliation of wiped pre-cutover orders. Treat that as a historical audit helper, not a live trading API.
TypeScript: previous CLOB client-v2 to unified client
Official migration removes the previous CLOB client-v2, builder-relayer, and builder-signing packages, then adds the unified client package plus viem (see official SDK Migration for exact install commands per package manager).
Packages involved: @polymarket/clob-client-v2, @polymarket/builder-relayer-client, @polymarket/builder-signing-sdk → @polymarket/client.
| Previous | Unified |
|---|---|
new ClobClient({ host, chain, ... }) read-only | createPublicClient() |
Two-step createOrDeriveApiKey + secured ClobClient | await createSecureClient({ wallet, signer }) |
createAndPostOrder | placeLimitOrder |
createOrder + postOrder | createLimitOrder + postOrder |
createAndPostMarketOrder | placeMarketOrder |
postOrders([{ order, orderType }, ...]) | postOrders([signed, ...]) |
createSecureClient derives credentials and configures signing. Omit wallet for Deposit Wallet. Signer adapters include Viem (privateKey from @polymarket/client/viem), Privy, and Ethers v5. The earlier V2 ethers to viem rewrite on the old client-v2 package is superseded for new work — see ethers to viem migration only if you are still on that stack.
Rust: unified SDK in progress
Official docs: a unified Rust SDK is in progress. Until it ships, use polymarket_client_sdk_v2 0.7.0 (enable the clob feature for auth/trading). Deposit Wallet trading uses funder + SignatureType::Poly1271. Deploying deposit wallets / submitting wallet batches is not in that crate — use Direct API workflows for those. See SDK Migration — Rust and Polymarket Rust SDK Reference.
Before / after: minimal Python place path
On py-clob-client-v2 (superseded client)
from py_clob_client_v2 import (
ClobClient, OrderArgs, OrderType, PartialCreateOrderOptions, Side,
)
l1 = ClobClient(host="https://clob.polymarket.com", chain_id=137, key=os.environ["PK"])
creds = l1.create_or_derive_api_key()
client = ClobClient(
host="https://clob.polymarket.com", chain_id=137, key=os.environ["PK"],
creds=creds, signature_type=3, funder=os.environ["FUNDER"],
)
resp = client.create_and_post_order(
order_args=OrderArgs(token_id=token_id, price=0.52, size=10.0, side=Side.BUY),
options=PartialCreateOrderOptions(tick_size="0.01"),
order_type=OrderType.GTC,
)
On polymarket-client (current)
import asyncio
import os
from polymarket import AsyncSecureClient
async def main() -> None:
async with await AsyncSecureClient.create(
private_key=os.environ["POLYMARKET_PRIVATE_KEY"],
) as client:
market = await client.get_market(slug="MARKET_SLUG")
yes_token_id = market.outcomes.yes.token_id
if yes_token_id is None:
raise RuntimeError("YES token ID missing")
response = await client.place_limit_order(
token_id=yes_token_id,
side="BUY",
price="0.52",
size="10",
)
if not response.ok:
raise RuntimeError(f"{response.code}: {response.message}")
print(response.order_id, response.status)
asyncio.run(main())
Things you can keep as-is
- Host
https://clob.polymarket.com— unchanged through both cutovers. - Gamma / Data API hosts — still valid; unified SDK can replace many direct calls with typed helpers.
- WebSocket hosts — still documented under Polymarket realtime feeds; Python unified realtime is via async
subscribe()(async clients only). - Existing CLOB API credentials — can be resumed into
AsyncSecureClient.create(..., credentials=...)when the signer/wallet match; you are not forced to mint a new key solely because the package name changed. - Order lifetime vocabulary — GTC, GTD, FOK, FAK, post-only (post-only is not combined with FOK/FAK).
- pUSD as collateral — still required for trading inventory.
Common gotchas (unified + leftover V2)
- Still importing
py_clob_client_v2after installingpolymarket-client. Overlapping mental models, wrong methods. Remove the old packages and fix imports tofrom polymarket import .... - Calling
create_and_post_order/PartialCreateOrderOptionson the new client. Those are V2-era names. Useplace_limit_orderand let the SDK resolve tick / neg-risk. - Assuming Python
chain_idrenamed. On the old V2 client it did not (TSchainId→chaindid). Unified Python create helpers do not ask you to pass host/chain for production defaults. - Leaving USDC.e unwrapped. Allowance failures until collateral is pUSD.
- Owner key in the agent container. Prefer scoped session keys for Deposit Wallet trading when your threat model needs a non-owner signer (Session Keys).
- Blind FOK on thin books / ignoring rate limits. Same operational failure modes as before; see rate limits and the create-order sibling.
- Leftover V2 error messages (
order_version_mismatch, dict-vs-attribute order books, removedget_balance/get_positions). Symptom table: py-clob-client-v2 Errors.
Agent compatibility
Safer automation
- Scripted removal of
py-clob-client-v2(+ builder packages) and install ofpolymarket-client/ the unified TypeScript client in CI or bootstrap AsyncSecureClient.create/SecureClient.createwith credentials resumed from a secret store (not committed)- Limit ladders,
post_only=True, andpost_ordersbatches within the documented 1–15 size estimate_market_priceplusmax_price/min_price/max_spendbefore market orders- Scoped session keys for Deposit Wallet trading when you can keep the owner key off the bot host
Unsafe / account-burning patterns
- Shipping the owner private key into an agent runtime, chat tool, or CI log
- Hammering order endpoints past rate limits
- Blind FOK into thin books
- Treating
delayedacceptance as a confirmed fill - Hard-coding fee bps or tick sizes the unified place APIs no longer take from you
Changelog
| Date | Change |
|---|---|
| May 25, 2026 | Initial publication as V1→V2 playbook (py-clob-client-v2, @polymarket/clob-client-v2, polymarket_client_sdk_v2). |
| September 7, 2026 | Targeted refresh: primary path is unified SDK (polymarket-client 0.9.0 / @polymarket/client); V1→V2 cutover retained as prior context; method rename table aligned with create-order sibling; agent compatibility section added. |
Official Resources
- SDK Migration (unified)
- Python SDK
- TypeScript SDK
- Place Orders
- polymarket-client (PyPI)
- SDK Changelog
- Predictions Changelog
- Polymarket Contracts
- Polymarket USD (pUSD)
AgentBets Guides
- polymarket-client place_limit_order — order placement deep dive (slug legacy; content unified)
- Polymarket Auth Troubleshooting
- py-clob-client-v2 Errors — symptom → cause → fix for leftover V2 imports
- Polymarket Trading Bot Quickstart
- Polymarket API Guide
Where This Fits in the Agent Betting Stack
This guide is maintenance work at Layer 3 (Trading) of the Agent Betting Stack. After April 28, 2026 a V1 adapter cannot place; after the unified SDK push, a V2-only package is the wrong long-term dependency. Layer 4 strategies only matter if the execution adapter can still sign and post.
Greenfield bots: start at the Trading Bot Quickstart and the place_limit_order reference rather than replaying the full V1→V2 field list.
This guide is maintained by AgentBets.ai. Found an error or an SDK change we missed? Let us know on Twitter.
Not financial advice. Built for builders.
