TL;DR: Polymarket now has two completely separate APIs — the Global crypto-native API on Polygon and the new US CFTC-regulated API launched February 16, 2026. They use different authentication (EIP-712 vs. Ed25519), different SDKs (
py-clob-clientvs.polymarket-us), different endpoint structures, and different account models. This guide maps every difference side by side, shows equivalent code for both, and provides a pattern for building agents that trade across both platforms.
Why This Guide Exists
On February 16, 2026, Polymarket launched public APIs and SDKs for its US-regulated platform. This created a problem nobody had before: two Polymarket APIs that look superficially similar but are architecturally incompatible.
If you built a bot on the Global Polymarket API using py-clob-client, none of that code works on Polymarket US. The authentication is different. The SDKs are different. The order format is different. The account structure is different. Even the way you identify markets is different.
As of March 2026, no official migration guide exists. The Polymarket docs cover each API independently, but never compare them. Developers are left reading two separate doc sites, two separate GitHub repos, and piecing together the differences through trial and error.
This guide fixes that. Every section maps Global ↔ US equivalents so you can port your bot, choose the right platform, or build an agent that trades on both.
Platform Overview: Two Polymarkets
Before diving into the API layer, understand what you’re building on.
Polymarket Global is the original platform. It runs on the Polygon blockchain. Your account is an Ethereum wallet. Trades settle on-chain as conditional token transfers. Collateral is USDC on Polygon. There is no KYC for users outside restricted jurisdictions. It is the highest-volume prediction market in the world.
Polymarket US is the regulated counterpart. It operates as a CFTC-designated contract market (DCM) after Polymarket acquired QCX LLC in 2025. Your account requires full KYC (government ID, SSN, proof of address via the iOS app). It uses fiat rails with USDC.e. Positions are held within the regulated exchange, not as on-chain tokens you control in your wallet.
| Dimension | Polymarket Global | Polymarket US |
|---|---|---|
| Regulatory status | Crypto-native, offshore | CFTC-regulated DCM |
| KYC required | No (outside restricted jurisdictions) | Yes — ID, SSN, proof of address |
| Collateral | USDC on Polygon | USDC.e + POL for gas (EOA) |
| Settlement | On-chain conditional tokens (ERC-1155) | Exchange-internal |
| Account model | Ethereum wallet + proxy address | KYC-verified account + developer portal |
| Geographic access | Global minus 33 restricted countries | US only (all 50 states) |
| Launched | 2020 (API matured 2023–2025) | Feb 16, 2026 (public API) |
| Liquidity | Shared global pool | Separate US liquidity pool |
The critical implication for agent builders: these are separate liquidity pools with separate order books. The same event may trade at different prices on Global vs. US. This creates arbitrage opportunities — but also means you cannot move positions between them.
For a full comparison of Polymarket against Kalshi and other platforms, see the Prediction Market API Reference.
Authentication: The Biggest Difference
Authentication is where most developers get stuck. The two platforms use fundamentally different cryptographic models.
Global API: Two-Level EIP-712 + HMAC
The Global Polymarket CLOB API uses a two-level authentication system rooted in Ethereum wallet cryptography.
Level 1 (L1) — Wallet Signature: You sign an EIP-712 typed data message with your Ethereum private key. This proves wallet ownership and is used to create or derive API credentials.
Level 2 (L2) — HMAC-SHA256: The L1 step generates three values: apiKey, secret, and passphrase. These are used as HMAC-SHA256 credentials for all subsequent trading requests. Five HTTP headers (POLY_ADDRESS, POLY_SIGNATURE, POLY_TIMESTAMP, POLY_NONCE, POLY_API_KEY) must accompany every authenticated request.
Even with L2 headers, order placement still requires signing the order payload with your private key. The auth is non-custodial — Polymarket never holds your key.
# Global API — Authentication with py-clob-client
from py_clob_client.client import ClobClient
import os
client = ClobClient(
host="https://clob.polymarket.com",
chain_id=137, # Polygon mainnet
key=os.getenv("PRIVATE_KEY"), # Ethereum private key (0x...)
)
# L1 → L2: Create or derive API credentials
creds = client.create_or_derive_api_creds()
client.set_api_creds(creds)
# creds = { "apiKey": "...", "secret": "...", "passphrase": "..." }
Common failure modes on Global auth: proxy wallet not deployed (user never logged into polymarket.com), nonce mismatch when re-deriving credentials, and timestamp drift causing intermittent 401 errors. See the Security Best Practices guide for key management patterns.
US API: Single-Level Ed25519
Polymarket US uses Ed25519 keypair authentication — the same cryptographic primitive used by Solana, SSH, and many modern APIs. No blockchain wallet involved.
You generate an Ed25519 keypair from the Polymarket US developer portal (polymarket.us/developer) after completing KYC on the iOS app. The SDK signs every request automatically with your private key. There is no two-level system and no separate order signing step.
# US API — Authentication with polymarket-us
from polymarket_us import PolymarketUS
import os
client = PolymarketUS(
key_id=os.getenv("POLYMARKET_US_KEY_ID"), # UUID
secret_key=os.getenv("POLYMARKET_US_SECRET"), # Base64-encoded Ed25519 private key
)
The private key is shown exactly once at generation. If you lose it, you generate a new keypair. There is no derivation or recovery mechanism.
Auth Comparison Table
| Aspect | Global API | US API |
|---|---|---|
| Cryptographic scheme | EIP-712 (Ethereum typed data) + HMAC-SHA256 | Ed25519 |
| Key source | Ethereum private key (wallet) | Developer portal keypair |
| Levels | L1 (wallet sig) → L2 (API creds) | Single level |
| Headers required | 5 (POLY_*) for trading | SDK handles signing automatically |
| Order signing | Separate step — private key signs order payload | SDK signs requests; no separate order signing |
| Key recovery | Derive from same private key + nonce | Not recoverable — regenerate |
| Credential format | apiKey + secret + passphrase | key_id (UUID) + secret_key (Base64 Ed25519) |
SDKs: Which Library to Use
Each platform has its own official SDKs. They are not interchangeable.
Global API SDKs
- Python:
py-clob-client— the mature, battle-tested library. Handles L1/L2 auth, order building, signing, and submission. Requireseth-accountfor wallet operations. - TypeScript:
@polymarket/clob-client— equivalent TypeScript client usingethers.js(v5). - Rust:
polymarket-rs— newer Rust client for performance-sensitive use cases. - Community:
polymarket-apis— third-party unified client wrapping Gamma, CLOB, Data, WebSocket, and GraphQL endpoints with Pydantic validation. Updated March 6, 2026.
For a full breakdown of py-clob-client methods, see the py_clob_client Reference.
US API SDKs
- Python:
polymarket-us— official SDK. Requires Python 3.10+. Supports sync and async (AsyncPolymarketUS). Released February 2026. - TypeScript:
polymarket-us— official TypeScript/Node SDK. Requires Node 18+. Same resource-oriented design as the Python SDK.
SDK Design Philosophy Comparison
The SDKs reflect fundamentally different design choices.
py-clob-client is low-level and crypto-native. You manage wallet keys, sign orders explicitly, construct order args with token IDs, and interact with the Polygon blockchain. It exposes the raw CLOB mechanics.
polymarket-us is high-level and REST-native. It uses a resource-oriented design (client.orders.create(...), client.events.list(...)) similar to Stripe’s SDK pattern. No blockchain concepts. Markets are referenced by slug, not token ID. The SDK handles all cryptographic signing internally.
# Equivalent: Fetch markets
# Global (py-clob-client) — uses Gamma API for market data
import requests
markets = requests.get(
"https://gamma-api.polymarket.com/markets",
params={"active": "true", "limit": 10, "order": "volume24hr", "ascending": "false"}
).json()
# US (polymarket-us) — integrated market endpoint
from polymarket_us import PolymarketUS
client = PolymarketUS()
markets = client.markets.list({"limit": 10})
API Architecture: Split vs. Unified
Global: Three APIs + WebSocket
The Global Polymarket API is split across three distinct services, each with its own base URL:
- Gamma API (
gamma-api.polymarket.com) — Market metadata, event listings, search, categories. Read-only. No auth required. - CLOB API (
clob.polymarket.com) — Order book, pricing, order placement, cancellation. L2 auth required for trading endpoints. - Data API (
data-api.polymarket.com) — User positions, trade history, portfolio data. Auth required. - WebSocket (
ws-subscriptions-clob.polymarket.com) — Real-time price, order book, and user channel streams.
This separation means building a Global bot requires calling at least two different API services. You query Gamma for market discovery, then switch to CLOB for pricing and execution.
US: Unified REST + WebSocket
The Polymarket US API is a single unified service with 23 REST endpoints and 2 WebSocket endpoints, organized into five resource groups: Markets, Orders, Events, Portfolio, and Account.
Everything routes through one base URL. Market data and trading live in the same API. You reference markets by slug ("btc-100k-2025") rather than by Polygon token ID.
| Feature | Global | US |
|---|---|---|
| Base URLs | 3 separate services | 1 unified service |
| Market identifier | Token ID (condition ID hash) | Market slug |
| Price format | Decimal 0.00–1.00 | String USD value ("0.55") |
| Order format | OrderArgs(price, size, side, token_id) | {"marketSlug", "intent", "type", "price", "quantity", "tif"} |
| REST endpoints | 50+ across Gamma/CLOB/Data | 23 |
| WebSocket channels | Market + User channels | Market + Private channels |
| Public rate limit | Varies by endpoint | 60 req/min |
| WebSocket instrument limit | Not explicitly capped | 10 instruments |
| Institutional access | Builder Program (higher limits) | Exchange Gateway (FIX 4.4 support) |
Placing Orders: Side-by-Side Code
This is where the rubber meets the road. Here is the same operation — buy 100 YES shares at $0.55 — on each platform.
Global API: Order Placement
from py_clob_client.client import ClobClient
from py_clob_client.clob_types import OrderArgs, OrderType
from py_clob_client.order_builder.constants import BUY
import os
client = ClobClient(
host="https://clob.polymarket.com",
chain_id=137,
key=os.getenv("PRIVATE_KEY"),
)
client.set_api_creds(client.create_or_derive_api_creds())
# You need the token_id — a long condition ID hash
# Typically fetched from the Gamma API first
TOKEN_ID = "71321045863171548570638401..." # YES token for this market
order_args = OrderArgs(
price=0.55,
size=100.0,
side=BUY,
token_id=TOKEN_ID,
)
# Sign the order (separate step — uses your private key)
signed_order = client.create_order(order_args)
# Submit to CLOB
resp = client.post_order(signed_order, OrderType.GTC)
print(resp)
US API: Order Placement
from polymarket_us import PolymarketUS
import os
client = PolymarketUS(
key_id=os.getenv("POLYMARKET_US_KEY_ID"),
secret_key=os.getenv("POLYMARKET_US_SECRET"),
)
# Reference market by slug — no token ID lookup needed
order = client.orders.create({
"marketSlug": "btc-100k-2025",
"intent": "ORDER_INTENT_BUY_LONG", # BUY YES
"type": "ORDER_TYPE_LIMIT",
"price": {"value": "0.55", "currency": "USD"},
"quantity": 100,
"tif": "TIME_IN_FORCE_GOOD_TILL_CANCEL",
})
print(order)
Key differences in the order flow:
- Market reference: Global uses a token ID (hash). US uses a human-readable slug.
- Side/intent: Global uses
BUY/SELLwith token-specific sides. US uses explicit intents likeORDER_INTENT_BUY_LONG(buy YES) andORDER_INTENT_BUY_SHORT(buy NO). - Price format: Global takes a float (
0.55). US takes a structured object ({"value": "0.55", "currency": "USD"}). - Signing: Global requires explicit
create_order()signing before submission. US handles signing inside the SDK automatically. - Time in force: US uses explicit
TIME_IN_FORCE_*enums. Global passesOrderType.GTC/OrderType.FOKas a separate parameter topost_order().
WebSocket Streaming: Real-Time Data
Both platforms offer WebSocket streaming for real-time market data and private order updates. The connection patterns differ.
Global WebSocket
import asyncio
import websockets
import json
async def stream_global():
uri = "wss://ws-subscriptions-clob.polymarket.com/ws/market"
async with websockets.connect(uri) as ws:
subscribe = {
"type": "subscribe",
"market": "71321045...", # Token ID
"channel": "price"
}
await ws.send(json.dumps(subscribe))
async for message in ws:
data = json.loads(message)
print(f"Global price: {data}")
asyncio.run(stream_global())
US WebSocket
import asyncio
from polymarket_us import AsyncPolymarketUS
async def stream_us():
async with AsyncPolymarketUS(
key_id="your-key-id",
secret_key="your-secret-key",
) as client:
# WebSocket connections are async-only
# Subscribe to market updates via /v1/ws/markets
# Subscribe to private order updates via /v1/ws/private
# Up to 10 instruments simultaneously
events = await client.events.list({"limit": 5, "active": True})
print(events)
asyncio.run(stream_us())
The US API WebSocket endpoints (/v1/ws/markets and /v1/ws/private) handle up to 10 instruments per connection. For the Global API, WebSocket subscriptions are per-token-ID with no explicitly documented cap, but practical limits apply.
Both platforms strongly recommend WebSocket over REST polling. The US API’s 60 requests-per-minute REST limit makes polling impractical for real-time strategies. The Global API has higher REST limits but WebSocket is still preferred for latency-sensitive agents.
Account Funding: How Agents Pay
This is a critical infrastructure difference that affects your wallet layer architecture.
Global: Fund your Polygon wallet with USDC. The agent’s Ethereum private key controls the wallet. You can use any wallet provider — raw EOA, Safe multisig, Coinbase Agentic Wallet, Lit Protocol session keys. See the Best Agent Wallet for Prediction Markets guide for recommendations.
US: Fund through the Polymarket US platform. USDC.e for purchasing outcome tokens. POL for gas fees if using an EOA wallet. Funding requires a verified account. Your agent cannot hold its own wallet independent of the KYC-verified identity.
This has significant implications for autonomous agents. On Global, an agent can have its own wallet with programmatic spending controls, session keys, and kill switches — the full agent wallet security stack. On US, the agent operates within a KYC-verified account, which raises legal and liability questions about who the “customer” is when an autonomous agent trades.
Decision Framework: Which API Should You Use?
| Your Situation | Use This API | Why |
|---|---|---|
| US-based developer building for US users | Polymarket US | Legal clarity, CFTC compliance, simpler SDK |
| International developer, global users | Polymarket Global | No KYC, more markets, higher liquidity, on-chain composability |
| Building an arbitrage agent | Both | Price discrepancies between US and Global pools create arb opportunities |
| Prototyping / learning | Polymarket Global | No KYC friction, Gamma API needs zero auth for reading |
| Institutional / enterprise deployment | Polymarket US | Exchange Gateway with FIX 4.4, regulatory audit trail |
| Need on-chain composability (DeFi integration) | Polymarket Global | Conditional tokens are ERC-1155s on Polygon — composable with DeFi |
| Copy-trading platform for US customers | Polymarket US | Regulatory requirement for US-facing consumer products |
For agents that need cross-platform capabilities, see the Cross-Market Arbitrage Guide for patterns on normalizing data across platforms.
Building a Dual-Stack Agent: Abstraction Pattern
If your agent needs to trade on both platforms (for arbitrage, redundancy, or serving both US and international users), you need an abstraction layer. Here is a minimal pattern:
from abc import ABC, abstractmethod
from dataclasses import dataclass
from typing import Optional
import os
@dataclass
class NormalizedMarket:
"""Platform-agnostic market representation."""
platform: str # "global" or "us"
slug: str # Human-readable identifier
token_id: Optional[str] # Global only — condition ID hash
question: str
yes_price: float # Normalized to 0.00–1.00
no_price: float
volume_24h: float
@dataclass
class NormalizedOrder:
"""Platform-agnostic order."""
market_slug: str
side: str # "YES" or "NO"
action: str # "BUY" or "SELL"
price: float # 0.00–1.00
quantity: float
time_in_force: str # "GTC" or "FOK"
class PredictionMarketClient(ABC):
"""Interface both platforms implement."""
@abstractmethod
def get_markets(self, limit: int = 10) -> list[NormalizedMarket]:
...
@abstractmethod
def place_order(self, order: NormalizedOrder) -> dict:
...
@abstractmethod
def cancel_all(self) -> dict:
...
@abstractmethod
def get_positions(self) -> list[dict]:
...
class PolymarketGlobalClient(PredictionMarketClient):
"""Wraps py-clob-client for the Global API."""
def __init__(self):
from py_clob_client.client import ClobClient
self.client = ClobClient(
host="https://clob.polymarket.com",
chain_id=137,
key=os.getenv("PM_GLOBAL_PRIVATE_KEY"),
)
self.client.set_api_creds(
self.client.create_or_derive_api_creds()
)
def get_markets(self, limit: int = 10) -> list[NormalizedMarket]:
import requests
raw = requests.get(
"https://gamma-api.polymarket.com/markets",
params={"active": "true", "limit": limit}
).json()
return [
NormalizedMarket(
platform="global",
slug=m.get("slug", ""),
token_id=m.get("clobTokenIds", [None])[0],
question=m.get("question", ""),
yes_price=float(m.get("outcomePrices", "[0.5,0.5]").strip("[]").split(",")[0]),
no_price=float(m.get("outcomePrices", "[0.5,0.5]").strip("[]").split(",")[1]),
volume_24h=float(m.get("volume24hr", 0)),
)
for m in raw
]
def place_order(self, order: NormalizedOrder) -> dict:
from py_clob_client.clob_types import OrderArgs, OrderType
from py_clob_client.order_builder.constants import BUY, SELL
# Requires token_id — look up from market data
# This is a simplified example; production code caches token IDs
markets = self.get_markets(limit=100)
market = next((m for m in markets if m.slug == order.market_slug), None)
if not market or not market.token_id:
raise ValueError(f"Market not found: {order.market_slug}")
side = BUY if order.action == "BUY" else SELL
order_args = OrderArgs(
price=order.price,
size=order.quantity,
side=side,
token_id=market.token_id,
)
signed = self.client.create_order(order_args)
tif = OrderType.GTC if order.time_in_force == "GTC" else OrderType.FOK
return self.client.post_order(signed, tif)
def cancel_all(self) -> dict:
return self.client.cancel_all()
def get_positions(self) -> list[dict]:
# Use the Data API for positions
import requests
# Simplified — production code handles pagination
return []
class PolymarketUSClient(PredictionMarketClient):
"""Wraps polymarket-us for the US API."""
def __init__(self):
from polymarket_us import PolymarketUS
self.client = PolymarketUS(
key_id=os.getenv("PM_US_KEY_ID"),
secret_key=os.getenv("PM_US_SECRET_KEY"),
)
def get_markets(self, limit: int = 10) -> list[NormalizedMarket]:
raw = self.client.markets.list({"limit": limit})
# Normalize US market format to common structure
return [
NormalizedMarket(
platform="us",
slug=m.get("slug", ""),
token_id=None, # US has no token IDs
question=m.get("question", ""),
yes_price=float(m.get("yesPrice", 0.5)),
no_price=float(m.get("noPrice", 0.5)),
volume_24h=float(m.get("volume24h", 0)),
)
for m in raw.get("markets", [])
]
def place_order(self, order: NormalizedOrder) -> dict:
intent_map = {
("BUY", "YES"): "ORDER_INTENT_BUY_LONG",
("BUY", "NO"): "ORDER_INTENT_BUY_SHORT",
("SELL", "YES"): "ORDER_INTENT_SELL_LONG",
("SELL", "NO"): "ORDER_INTENT_SELL_SHORT",
}
tif_map = {
"GTC": "TIME_IN_FORCE_GOOD_TILL_CANCEL",
"FOK": "TIME_IN_FORCE_FILL_OR_KILL",
}
return self.client.orders.create({
"marketSlug": order.market_slug,
"intent": intent_map[(order.action, order.side)],
"type": "ORDER_TYPE_LIMIT",
"price": {"value": str(order.price), "currency": "USD"},
"quantity": int(order.quantity),
"tif": tif_map.get(order.time_in_force, "TIME_IN_FORCE_GOOD_TILL_CANCEL"),
})
def cancel_all(self) -> dict:
return self.client.orders.cancel_all()
def get_positions(self) -> list[dict]:
return self.client.portfolio.positions()
This pattern lets your agent logic operate on NormalizedMarket and NormalizedOrder objects without caring which platform executes the trade. An arbitrage agent, for example, can compare yes_price across both clients and route to whichever has the better price.
For agents using unified APIs like Dome, note that these tools currently target the Global API only. Polymarket US SDK support is not yet available in any third-party unified API as of March 2026.
Troubleshooting: Common Issues on Each Platform
Global API
401 “Invalid api key” on post_order() but cancel_all() works: This is a known issue. The order signing step requires the correct funder (proxy wallet) address. If your proxy wallet was never deployed (you never logged into polymarket.com with this wallet), order submission fails even though cancellation works. Solution: log into polymarket.com once with your wallet to deploy the proxy, or deploy it programmatically.
Intermittent 401 errors on create_or_derive_api_creds(): Timestamp drift. The CLOB API validates request timestamps and rejects requests with excessive skew. Ensure your server’s clock is NTP-synced. Even a few seconds of drift causes intermittent failures.
signature_type confusion: If using a Gnosis Safe or smart contract wallet, you must set signature_type=1 when initializing ClobClient. The default (0) is for EOA wallets. Mismatched signature types cause opaque auth errors.
US API
“Private key displayed only once” — key lost: Generate a new keypair from the developer portal. There is no recovery or derivation mechanism.
Auth errors from timestamp mismatch: The US API uses Ed25519 signatures that include timestamps to prevent replay attacks. If your server clock is off, requests fail silently or return 401. Use NTP synchronization.
Rate limited on REST polling: The 60 requests-per-minute limit on public endpoints is strict. Switch to WebSocket for any real-time data needs. REST should only be used for one-off queries and order management.
KYC required for API access: You cannot generate API keys without completing KYC through the iOS app first. There is no way around this. Use the same authentication method (Apple, Google, or email) across both the app and the developer portal to avoid account mismatch issues.
Migration Checklist: Global → US
If you are porting an existing Global bot to Polymarket US, follow this sequence:
Complete KYC on the Polymarket US iOS app. Generate Ed25519 API keys at polymarket.us/developer. Store the private key immediately — it is shown once.
Install the US SDK:
pip install polymarket-us(Python 3.10+) ornpm install polymarket-us(Node 18+).Replace your client initialization. Swap
ClobClientforPolymarketUS. Replace Ethereum private key env vars with Ed25519 key ID and secret.Replace market identifiers. Token IDs → market slugs. You will need to map your existing token ID references to the corresponding US slugs. Some markets may only exist on one platform.
Replace order construction.
OrderArgs(price, size, side, token_id)→client.orders.create({marketSlug, intent, type, price, quantity, tif}). Side/intent mapping:BUY+ YES token →ORDER_INTENT_BUY_LONG.Remove explicit order signing. The US SDK handles signing internally. Delete any
create_order()/sign_order()calls.Update WebSocket connections. Replace raw
websocketsconnections towss://ws-subscriptions-clob.polymarket.comwith the US SDK’s async WebSocket interface.Update error handling. The US SDK throws typed exceptions:
AuthenticationError,BadRequestError,RateLimitError,NotFoundError,APITimeoutError,APIConnectionError. Map these to your existing error handling logic.Fund your US account. Load USDC.e through the Polymarket US platform. Your Polygon USDC balance is not usable on US.
Test with small orders. There is no testnet/sandbox for the public Retail API. Start with minimal position sizes. For institutional-scale testing, contact [email protected] for Exchange Gateway sandbox access.
What’s Next
This guide covers the current state as of March 2026. The Polymarket US API is three weeks old and actively evolving. Expect changes to endpoints, rate limits, and SDK capabilities.
For the complete endpoint reference for the Global API, see the Prediction Market API Reference. For wallet infrastructure to power your agent’s fund management, see the Agent Wallet Comparison and Agent Betting Stack framework.
If you are building cross-platform agents that span Polymarket, Kalshi, and sportsbooks, the Unified API Comparison covers the unified API layer — though as noted, these tools do not yet support Polymarket US.
Browse the AgentBets marketplace for agents already trading on these platforms, or list your own agent if you have built something others can use.
