> Fetch the complete documentation index at: https://docs.polymarket.com/llms.txt
> Use this file to discover all available pages before exploring further.
# Python SDK
> Build with the unified Polymarket Python SDK.
The unified Python SDK gives you a consistent surface across Polymarket discovery, market data, trading, account data, and realtime streams.
<Note>
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.
</Note>
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](#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.
## Quickstart
<Steps>
<Step title="Install the Package">
Install the SDK from PyPI.
<CodeGroup>
```bash uv theme={null}
uv add polymarket-client
```
```bash pip theme={null}
pip install polymarket-client
```
```bash poetry theme={null}
poetry add polymarket-client
```
</CodeGroup>
</Step>
<Step title="Create a Public Client">
Create an instance of the `AsyncPublicClient` inside an `async with` block so its network transports are released when you are done.
```python theme={null}
from polymarket import AsyncPublicClient
async with AsyncPublicClient() as client:
...
```
</Step>
<Step title="Fetch Markets">
Fetch a page of markets to discover active trading opportunities.
The SDK uses consistent patterns for pagination, Python-native model values, and structured SDK exceptions across public and authenticated workflows.
### Typed Primitives
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`.
```python theme={null}
from datetime import datetime
from decimal import Decimal
from polymarket import ConditionId, EvmAddress, MarketId, TokenId
class Market:
id: MarketId
condition_id: ConditionId | None
state: MarketState
outcomes: MarketOutcomes
resolution: MarketResolution
# …
class MarketState:
start_date: datetime | None
end_date: datetime | None
# …
class MarketOutcome:
label: str
token_id: TokenId | None
price: Decimal | None
class MarketResolution:
resolved_by: EvmAddress | None
# …
```
### Market and Event Data
Market and event responses are returned as SDK models with snake\_case fields and nested submodels.
<CodeGroup>
```python Market theme={null}
class Market:
id: MarketId
slug: str | None
condition_id: ConditionId | None
question: str | None
description: str | None
category: str | None
image: str | None
icon: str | None
state: MarketState
outcomes: MarketOutcomes
metrics: MarketMetrics
prices: MarketPrices
trading: MarketTrading
resolution: MarketResolution
rewards: MarketRewards
sports: MarketSportsMetadata
tags: tuple[MarketTag, ...]
# …
```
```python Event theme={null}
class Event:
id: EventId
slug: str | None
title: str | None
subtitle: str | None
description: str | None
category: str | None
subcategory: str | None
image: str | None
icon: str | None
created_at: datetime | None
updated_at: datetime | None
published_at: datetime | None
state: EventState
schedule: EventSchedule
metrics: EventMetrics
trading: EventTrading
estimation: EventEstimation
sports: EventSportsMetadata
partners: tuple[EventPartner, ...]
markets: tuple[Market, ...]
series: tuple[EventSeries, ...]
# …
```
</CodeGroup>
### Environment Configuration
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.
```python theme={null}
from polymarket import AsyncPublicClient, PRODUCTION
async with AsyncPublicClient(environment=PRODUCTION) as client:
...
```
### Pagination
With async clients, list methods return an `AsyncPaginator` across paginated endpoints. Use `async for` to iterate through pages.
You can also fetch the first page directly and resume later from a cursor.
```python theme={null}
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.
```python theme={null}
async for market in markets.items():
# market: Market
...
```
### Error Handling
All SDK exceptions inherit from `PolymarketError`. Catch specific subclasses to handle known cases, and catch `PolymarketError` as the final SDK fallback.
<Note>
Catching `PolymarketError` last ensures error subclasses added in future SDK
Use discovery methods to browse events, markets, teams, tags, comments, sports metadata, and search results. The examples below show a few common entry points.
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`.
```python theme={null}
from polymarket import AsyncPublicClient
from polymarket.streams import CryptoPricesSpec, MarketSpec
yes_token_id = market.outcomes.yes.token_id
if 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.
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.
#### Place Orders
<Tabs>
<Tab title="Limit Order">
```python theme={null}
yes_token_id = market.outcomes.yes.token_id
if yes_token_id is None:
raise RuntimeError("Market does not have a YES token id")
response = await secure_client.place_limit_order(
token_id=yes_token_id,
side="BUY",
price="0.52",
size="10",
)
if response.ok:
# response.order_id: str
...
else:
# response.code: OrderResponseErrorCode
# response.message: str
...
```
</Tab>
<Tab title="Expiring Limit Order">
```python theme={null}
import time
yes_token_id = market.outcomes.yes.token_id
if yes_token_id is None:
raise RuntimeError("Market does not have a YES token id")
response = await secure_client.place_limit_order(
token_id=yes_token_id,
side="SELL",
price="0.52",
size="10",
expiration=int(time.time()) + 60 * 60,
)
if response.ok:
# response.order_id: str
...
else:
# response.code: OrderResponseErrorCode
# response.message: str
...
```
</Tab>
<Tab title="Partial-Fill Market Order">
```python theme={null}
yes_token_id = market.outcomes.yes.token_id
if yes_token_id is None:
raise RuntimeError("Market does not have a YES token id")
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.
<Tabs>
<Tab title="Current Rewards">
```python theme={null}
rewards = secure_client.list_current_rewards()
async for page in rewards:
# page.items: tuple[CurrentReward, ...]
...
```
</Tab>
<Tab title="Market Rewards">
```python theme={null}
condition_id = market.condition_id
if condition_id is None:
raise RuntimeError("Market does not have a condition id")
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.
<Tabs>
<Tab title="Positions">
```python theme={null}
positions = secure_client.list_positions(
market=[market.id],
page_size=10,
)
async for page in positions:
# page.items: tuple[Position, ...]
...
```
</Tab>
<Tab title="Portfolio Value">
```python theme={null}
value = await secure_client.get_portfolio_values(market=[market.id])
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.
* Point Combos RFQ endpoints at the production domains: `combos-rfq-api.polymarket.com` (REST) and `combos-rfq-gateway-quoter.polymarket.com` (quoter WebSocket).
### `0.1.0b6`
* Added `list_combo_markets` for fetching the Combo market catalog with SDK pagination. See [Combos](/market-makers/combos).
* Parse RFQ quote rejections that use the `SUBMISSION_WINDOW_CLOSED` gateway error code.
### `0.1.0b5`
* Added Combos support for multi-leg RFQ positions. See [Combos](/market-makers/combos).
* Added notebook-friendly model display for Jupyter workflows.
* `ConditionId` is now deprecated in favor of `CtfConditionId`; existing
`ConditionId` exports remain available as deprecated aliases.
### `0.1.0b4`
* Added dataframe conversion support for SDK models and response collections.
**Secure client setup now defaults to the Deposit Wallet flow**
`AsyncSecureClient.create` can now derive and use the signer's deterministic
Deposit Wallet when you omit `wallet`. If you already know which Polymarket
wallet you want to use, keep passing `wallet`.
```diff theme={null}
secure_client = await AsyncSecureClient.create(
private_key=os.environ["POLYMARKET_PRIVATE_KEY"],
- wallet=os.environ["POLYMARKET_WALLET_ADDRESS"],
)
```
If you want to keep account selection explicit, no change is required:
```python theme={null}
secure_client = await AsyncSecureClient.create(
private_key=os.environ["POLYMARKET_PRIVATE_KEY"],
wallet=os.environ["POLYMARKET_WALLET_ADDRESS"],
)
```
**`setup_trading_approvals()` now waits internally**
You no longer need to wait on the returned handle. Call the method once before
trading; it is safe to call again if approvals are already set.