Use this file to discover all available pages before exploring further.
The unified Python SDK gives you a consistent surface across Polymarket discovery, market data, trading, account data, and realtime streams.
The Python SDK is currently in beta. We are keeping it in this beta phase
while we address issues and harden the SDK before transitioning to a more
stable release.
The SDK ships parallel async and sync clients with matching method names and arguments: AsyncPublicClient / PublicClient for public reads, and AsyncSecureClient / SecureClient for authenticated reads and trading. Prefer the async clients for servers, bots, and any code that already runs inside an event loop. Use the sync clients for scripts, notebooks, and one-off tools where an event loop would just add ceremony. Realtime stream subscriptions are async only and require the async clients.Examples below show the body of an async def main() function; wrap them with asyncio.run(main()) to run as a script, as shown in Quickstart. To switch a snippet to sync, swap AsyncPublicClient / AsyncSecureClient for PublicClient / SecureClient, drop await, replace async with with with, replace async for with for, and remove the asyncio.run(...) wrapper.
Identifiers and EVM addresses are exposed as typing.NewType aliases (MarketId, ConditionId, TokenId, EventId, EvmAddress, …) so static type checkers can keep them distinct from plain strings. Precision-sensitive price, size, and amount fields generally use decimal.Decimal; date and time fields use datetime.date or datetime.datetime.
Production is the default environment. Pass an Environment object when your integration needs to target a different deployment or custom endpoint set. The client owns network transports, so use async with (or call await client.close()) to release them when you are done.
from polymarket import AsyncPublicClient, PRODUCTIONasync with AsyncPublicClient(environment=PRODUCTION) as client: ...
With async clients, list methods return an AsyncPaginator across paginated endpoints. Use async for to iterate through pages.
async with AsyncPublicClient() as client: markets = client.list_markets(closed=False, page_size=10) async for page in markets: # page.items: tuple[Market, ...] ...
You can also fetch the first page directly and resume later from a cursor.
first_page = await markets.first_page()# first_page.items: tuple[Market, ...]async for page in markets.from_cursor(first_page.next_cursor): # page.items: tuple[Market, ...] ...
When you only care about the items and not page boundaries, iterate them directly.
async for market in markets.items(): # market: Market ...
Use discovery methods to browse events, markets, teams, tags, comments, sports metadata, and search results. The examples below show a few common entry points.
Events
Markets
Teams
Tags
Comments
Sports
Search
events = client.list_events(page_size=10)async for page in events: # page.items: tuple[Event, ...] ...
markets = client.list_markets(closed=False, page_size=10)async for page in markets: # page.items: tuple[Market, ...] ...
teams = client.list_teams(league="NBA", page_size=10)async for page in teams: # page.items: tuple[Team, ...] ...
Subscribe through one SDK interface even when events come from different stream families. The SDK routes each subscription spec to the right stream and merges the results into one async iterator. Subscriptions are async only and require AsyncPublicClient or AsyncSecureClient.
from polymarket import AsyncPublicClientfrom polymarket.streams import CryptoPricesSpec, MarketSpecyes_token_id = market.outcomes.yes.token_idif yes_token_id is None: raise RuntimeError("Market does not have a YES token id")async with AsyncPublicClient() as client: stream = await client.subscribe( [ MarketSpec(token_ids=[yes_token_id]), CryptoPricesSpec( topic="prices.crypto.binance", symbols=["btcusdt"], ), ], ) async with stream: async for event in stream: # event: # | MarketBookEvent # | MarketPriceChangeEvent # | MarketLastTradePriceEvent # | MarketTickSizeChangeEvent # | MarketBestBidAskEvent # | NewMarketEvent # | MarketResolvedEvent # | CryptoPricesBinanceEvent print(type(event).__name__) break
AsyncSecureClient.subscribe accepts the same public subscription specs and adds UserSpec for user-scoped order and trade events on the authenticated wallet.
import osfrom polymarket import AsyncSecureClientfrom polymarket.streams import UserSpecasync with await AsyncSecureClient.create( private_key=os.environ["POLYMARKET_PRIVATE_KEY"], wallet=os.environ.get("POLYMARKET_WALLET_ADDRESS"),) as secure_client: user_stream = await secure_client.subscribe(UserSpec()) async with user_stream: async for event in user_stream: # event: # | UserOrderEvent # | UserTradeEvent print(type(event).__name__) break
Create a secure client when you need wallet-scoped reads or trading.
Secure clients own multiple network transports. Wrap them in async with,
or call await secure_client.close() when you are done, to release the
underlying connections. The snippets below show client creation and
subsequent calls as a flat sequence for readability — in real code, keep
the client inside an async with block or close it explicitly.
The Python SDK authenticates with a local private key. Pass wallet when you want to operate on a Polymarket wallet address that differs from the signer address; if omitted, the client uses the signer address as the account wallet.
import osfrom polymarket import AsyncSecureClientasync with await AsyncSecureClient.create( private_key=os.environ["POLYMARKET_PRIVATE_KEY"], wallet=os.environ.get("POLYMARKET_WALLET_ADDRESS"),) as secure_client: ...
Keep private keys and API credentials in your secret manager or local environment. Do not commit them to source control.
Builder API keys are supported for backwards compatibility with builders that
still use them for gasless workflows. They are not used for order attribution.
Use builder_code on orders for attribution.
Before placing orders, make sure the authenticated wallet has the required trading approvals. If you use gasless wallet operations, configure API key authentication first.
From this point forward, snippets in Trading Setup, Trading, Position
Lifecycle, and Wallet Operations submit real on-chain transactions or
live orders against the configured environment when executed. Review
each call before running it against a wallet that holds funds.
1
Check Gasless Readiness
Optionally check whether the deposit wallet for the authenticated EOA is already deployed. The result is informational — call setup_gasless_wallet() in the next step regardless, since it is idempotent on deployment and is the call that binds the client to the deposit wallet.
Always call setup_gasless_wallet() for gasless workflows. It deploys the deposit wallet if needed and returns a new client bound to the deposit wallet address. Close the previous client when you replace it.
Use a secure client to create, sign, and submit orders. Limit orders specify the price and size you want to trade. Market orders execute against resting liquidity immediately.Order placement returns a discriminated response. Check response.ok before reading order details.
Use position lifecycle methods to split collateral into outcome tokens, merge complete sets back into collateral, or redeem resolved positions. These examples assume the secure client is configured with API key authentication as shown in API Key Authentication.
Split Position
Merge Positions
Redeem Positions
condition_id = market.condition_idif condition_id is None: raise RuntimeError("Market does not have a condition id")handle = await secure_client.split_position( condition_id=condition_id, amount=1,)outcome = await handle.wait()# outcome.transaction_hash: TransactionHash
condition_id = market.condition_idif condition_id is None: raise RuntimeError("Market does not have a condition id")handle = await secure_client.merge_positions( condition_id=condition_id, amount="max",)outcome = await handle.wait()# outcome.transaction_hash: TransactionHash
Use wallet operation methods for direct token movements from the authenticated wallet. Amounts are in base units. These examples assume the secure client is configured with API key authentication as shown in API Key Authentication.
Manage open orders for the authenticated wallet after placement. These examples assume order_id comes from an accepted order response.
Get Order
List Open Orders
Cancel Order
Cancel Market Orders
order = await secure_client.get_order(order_id=order_id)# order: OpenOrder
condition_id = market.condition_idif condition_id is None: raise RuntimeError("Market does not have a condition id")open_orders = secure_client.list_open_orders(market=condition_id)async for page in open_orders: # page.items: tuple[OpenOrder, ...] ...
yes_token_id = market.outcomes.yes.token_idif yes_token_id is None: raise RuntimeError("Market does not have a YES token id")response = await secure_client.cancel_market_orders(token_id=yes_token_id)# response.canceled: tuple[str, ...]
Use rewards methods to inspect active reward programs and scoring methods to check whether orders are eligible for scoring. list_current_rewards and list_market_rewards are public reads and are also available on AsyncPublicClient / PublicClient; get_order_scoring and get_orders_scoring require a secure client because they read account-scoped order data.
Current Rewards
Market Rewards
Order Scoring
Batch Order Scoring
rewards = secure_client.list_current_rewards()async for page in rewards: # page.items: tuple[CurrentReward, ...] ...
condition_id = market.condition_idif condition_id is None: raise RuntimeError("Market does not have a condition id")rewards = secure_client.list_market_rewards(condition_id=condition_id)async for page in rewards: # page.items: tuple[MarketReward, ...] ...
Secure clients read account-scoped data for the authenticated wallet by default. Methods that take a user= parameter (positions, portfolio value, activity) accept a different wallet address to read its data instead.
Positions
Portfolio Value
Activity
Trades
Notifications
positions = secure_client.list_positions( market=[market.id], page_size=10,)async for page in positions: # page.items: tuple[Position, ...] ...
value = await secure_client.get_portfolio_values(market=[market.id])# value: tuple[PortfolioValue, ...]
activity = secure_client.list_activity( market=[market.id], page_size=10,)async for page in activity: for item in page.items: match item.type: case "TRADE": # item.token_id: TokenId # item.shares: Decimal ... case "REWARD": # item.amount: Decimal ... case _: # SPLIT / MERGE / REDEEM / CONVERSION / MAKER_REBATE # / REFERRAL_REWARD / YIELD ...
yes_token_id = market.outcomes.yes.token_idif yes_token_id is None: raise RuntimeError("Market does not have a YES token id")trades = secure_client.list_account_trades(token_id=yes_token_id)async for page in trades: # page.items: tuple[ClobTrade, ...] ...
Secure clients expose the API credentials created for the authenticated session. Store them securely if you want to reuse the session later without producing a new authentication signature while the credentials remain valid.