# polymarket-apis [![PyPI version](https://img.shields.io/pypi/v/polymarket-apis.svg)](https://pypi.org/project/polymarket-apis/) Unified Polymarket v2 APIs with Pydantic data validation - Clob, Gamma, Data, Web3, Websockets, GraphQL clients. ## Polymarket Mental Models ### Events, Markets and Outcomes The Polymarket ecosystem is organized hierarchically: ```mermaid flowchart LR A([Event]) --- B([Market A]) A --- C([Market B]) A ~~~ Dot1@{ shape: sm-circ} A ~~~ Dot2@{ shape: sm-circ} A ~~~ Dot3@{ shape: sm-circ} A -.- D([Market n]) B --- E([Outcome 1]) B --- F([Outcome 2]) C --- G([Outcome 1]) C --- H([Outcome 2]) ``` - **Event** — represents a proposition or question such as “How many Fed rate cuts in 2025?”. - Identified by a human-readable **`slug`** (e.g. `how-many-fed-rate-cuts-in-2025`) and an **event `id`** (e.g. `16085`). - **Market** — represents a specific option for the related event (e.g. 1 rate cut in 2025). An Event has 1 or more corresponding Markets. (e.g. 9 options in this case - {0, 1, 2, ..., 7, 8 or more} rate cuts in 2025) - Identified by a **`condition id`** (e.g. `0x8e9b6942b4dac3117dadfacac2edb390b6d62d59c14152774bb5fcd983fc134e` for 1 rate cut in 2025), a human-readable **`slug`** (e.g. `'will-1-fed-rate-cut-happen-in-2025'`) and a **market `id`** (e.g. `516724`). - **Outcome** — represents a binary option related to a market. (most commonly `Yes`/`No`, but can be e.g. `Paris Saint-Germain`/`Inter Milan` in the case of a match where draws are not possible) - Identified by a **`token id`** (e.g. `15353185604353847122370324954202969073036867278400776447048296624042585335546` for the `Yes` outcome in the 1 rate cut in 2025 market) - The different APIs represent Events/Markets differently (e.g. Event, QueryEvent / ClobMarket, GammaMarket, RewardsMarket) but they all use to the same underlying identifiers. ### Tokens - **Tokens** are the blockchain implementation of **Outcomes** - tradable digital assets on the Polygon blockchain that users buy, hold and sell on Polygon. - This helps ensure the logic of binary outcome prediction markets through smart contracts (e.g. collateralization, token prices going to $1.00 or $0.00 after resolution, splits/merges). ### Splits and Merges - Holding 1 `Yes` share + 1 `No` share in a **Market** (e.g. `'will-1-fed-rate-cut-happen-in-2025'`) covers the entire universe of possibilities and guarantees a $1.00 payout regardless of outcome. This mathematical relationship enables Polymarket's core mechanisms: splitting (1 pUSD → 1 `Yes` + 1 `No`) and merging (1 `Yes` + 1 `No` → 1 pUSD) at any point before resolution. - Splits are the only way tokens are created. Either a user splits pUSD into equal shares of the complementary tokens or Polymarket automatically splits pUSD when it matches an `Yes` buy order at e.g. 30¢ with a `No` buy order at 70¢. ### Unified Order Book - Polymarket uses traditional exchange mechanics - a Central Limit Order Book (CLOB), where users place buy and sell orders that get matched by price and time priority. - However, because the `Yes` and `No` outcomes form a complete probability universe, certain orders become mathematically equivalent - allowing the matching engine to find trades as exemplified above. - This unified structure means every **BUY** order for `Outcome 1` at price **X** is simultaneously visible as a **SELL** order for `Outcome 2` at price **(100¢ - X)**, creating deeper liquidity and tighter spreads than separate order books would allow. ### Negative Risk and Conversions - If the **Markets** in and **Event** collectively cover a complete universe of possibilities (e.g. {0, 1, 2, ..., 7, 8 or more} rate cuts in 2025) and only one winner is possible, two collections of positions (made up of tokens and pUSD) become mathematically equivalent and the **Event** is said to support negative risk. - e.g. Hold 1 `No` token in the 0 rate cuts in 2025. This is equivalent to holding 1 `Yes` token in each of the other **Markets** {1, 2, ..., 7, 8 or more}. - An interesting consequence is that holding `No` tokens in more than one **Market** is equivalent to `Yes` tokens ***and*** some pUSD. - e.g. Hold 1 `No` token on each of {0, 1, 2, ..., 7, 8 or more} rate cuts in 2025. Because only one winner is possible, this guarantees that 8 out of the 9 **Markets** resolve to `No`. This is equivalent to a position of 8 pUSD. - e.g. Hold 1 `No` token on each of {0, 1} rate cuts in 2025. This is equivalent to 1 `Yes` token in {2, ..., 7, 8 or more} and 1 pUSD. - Polymarket allows for the one way (for capital efficiency) conversion from `No` tokens to a collection of `Yes` tokens and pUSD before resolution through a smart contract. ## Clients overview ### PolymarketReadOnlyClobClient Read-only order book related operations. - **Order book** - get one or more order books, best price, spread, midpoint, and last trade price by `token_id` - **Miscellaneous** - get market microstructure parameters by `condition_id` (`get_clob_market_info`) - tokens, tick size, minimum order size, fees, rewards, RFQ flags, and order-age settings - get `condition_id` and its corresponding `token_id`s for a market by `token_id` - get crypto outcomes by `slug` for up/down markets - get recent price history by `token_id` in the last 1h, 6h, 1d, 1w, 1m - get price history by `token_id` in a start/end interval - get all price history by `token_id` in 2-minute increments - get `ClobMarket` by `condition_id` - get all `ClobMarkets` ### PolymarketClobClient Order book related operations. - **Authentication** - `private_key` - `address` - `signature_type` is optional and can be derived from the address - signature_type=0 for EOAs (Externally Owned Accounts) - signature_type=1 for Email/Magic wallets - signature_type=2 for Safe/Gnosis wallets - signature_type=3 for Deposit wallets - All operations from `PolymarketReadOnlyClobClient` - **Orders** - create and post limit or market orders - cancel one or more orders by `order_id` - cancel all orders for a `condition_id`/`token_id` - cancel all orders - get active orders - send heartbeat to keep orders alive - **Trades** - get trade history for a user with filtering by `condition_id`, `token_id`, `trade_id`, and time window - **Rewards** - check if one or more orders are scoring for liquidity rewards by `order_id` - get daily earned rewards - check if a market offers rewards by `condition_id` with `get_market_rewards()` - get all active markets that offer rewards, sorted and filtered by multiple criteria, with `get_reward_markets()` - **Miscellaneous** - get pUSD balance - get token balance by `token_id` - detect wallet signature type ### PolymarketGammaClient Market and event related operations. - **Market** - get `GammaMarket` by `market_id` - get `GammaMarket` by `slug` - get `GammaMarkets` with pagination and filtering by `slug`, `market_id`, `token_id`, `condition_id`, `tag_id`, active/closed/archived status, liquidity window, volume window, start date window, end date window, and ordering - get tags for a market by `market_id` - **Event** - get `Event` by `event_id` - get `Event` by `slug` - get `Events` with pagination and filtering by `slug`, `event_id`, `tag_id`, active/closed/archived status, liquidity window, volume window, start date window, end date window, and ordering - get all `Events` given some filtration - search `Events`, `Tags`, and `Profiles` by text query, tags, status, recurrence, and multiple sort modes - grok an event summary by event `slug` - grok an election market explanation by candidate name and election title - get tags for an event by `event_id` - **Tag** - get `Tags` with pagination, ordered by any tag field - get all `Tags` - get `Tag` by `tag_id` - get `Tag` relations by `tag_id` or `slug` - get tags related to a tag by `tag_id` or `slug` - **Sport** - get `Teams` with pagination, filtered by `league`, `name`, and `abbreviation` - get all `Teams` given some filtration - get `Sports` with pagination, filtered by `name` - get sports metadata - **Series** - get `Series` with pagination, filtered by `slug` and closed status, and ordered by any series field - get all `Series` given some filtration - **Comments** - get comments by `parent_entity_type` and `parent_entity_id` with pagination, ordered by any comment field - get comments by `comment_id`, returning all comments in a thread - get comments by user base address with pagination, ordered by any comment field - **Miscellaneous** - get public profile by user address ### PolymarketDataClient Portfolio related operations. - **Positions** - get positions with pagination by user address, filtered by `condition_id`, position size, redeemability, mergeability, and title - **Trades** - get trades with pagination, filtered by `condition_id`, user address, side, taker-only status, cash amount, and token amount - **Activity** - get activity with pagination by user address, filtered by type, `condition_id`, time window, side, and sort order - **Holders** - get top holders by `condition_id` - **Value** - get positions value by user address and `condition_ids` - `condition_ids is None` means total value of positions - `condition_ids is str` means value of positions on one market - `condition_ids is list[str]` means sum of values of positions on multiple markets - **Closed positions** - get closed positions, filtered by `condition_id` - **Miscellaneous** - get total number of markets traded by user address - get open interest for a list of `condition_id`s - get live volume for an event by `event_id` - get pnl timeseries by user address for a period (`1d`, `1w`, `1m`, `all`) with frequency (`1h`, `3h`, `12h`, `1d`) - get overall pnl/volume by user address for a recent window (`1d`, `7d`, `30d`, `all`) - get user rank on the profit/volume leaderboards by user address for a recent window (`1d`, `7d`, `30d`, `all`) - get top users on the profit/volume leaderboards for a recent window (`1d`, `7d`, `30d`, `all`) ### `PolymarketWeb3Client` / `PolymarketGaslessWeb3Client` Blockchain clients for blockchain based Polymarket operations. Both clients support the same core operations detailed below - `PolymarketWeb3Client` submits transactions directly on-chain, so the base EOA for the provided `private_key` must hold POL for gas, regardless of `signature_type`. - `PolymarketGaslessWeb3Client` submits transactions through the relayer, and Polymarket pays for gas - **Authentication** - `PolymarketWeb3Client` - `private_key` - `signature_type` - `PolymarketGaslessWeb3Client` - `private_key` - `signature_type` - either `relayer_api_key` ([create here](https://polymarket.com/settings?tab=api-keys)) - unlimited rate limit - the client derives `RELAYER_API_KEY_ADDRESS` from the wallet automatically - or `builder_creds` ([create here](https://polymarket.com/settings?tab=builder)) as `ApiCreds` - **Supported wallet types** - `PolymarketWeb3Client` - EOA wallets (`signature_type=0`) - Email/Magic proxy wallets (`signature_type=1`) - Safe/Gnosis wallets (`signature_type=2`) - `PolymarketGaslessWeb3Client` - EOA wallets (`signature_type=0`) are NOT supported for gasless transactions - Email/Magic proxy wallets (`signature_type=1`) - Safe/Gnosis wallets (`signature_type=2`) - Deposit wallets (`signature_type=3`) - **Setup and deployment** - set approvals/disapprovals for all needed pUSD and conditional token spenders - Safe/Gnosis and Deposit wallet holders need to run `deploy_safe_wallet()` or `deploy_deposit_wallet()` respectively before using Safe transactions if the Safe has not been deployed yet - **Balances** - get POL balance by user address - get pUSD balance by user address - get token balance by `token_id` and user address - **Transfers** - transfer pUSD to another address with recipient address and amount - transfer token to another address with `token_id`, recipient address, and amount - **Token/pUSD conversions** - split pUSD into complementary tokens with `condition_id`, amount, and `neg_risk` - merge complementary tokens into pUSD with `condition_id`, amount, and `neg_risk` - redeem tokens into pUSD with `condition_id`, amounts array of [`Yes` shares, `No` shares], and `neg_risk` - convert one or more `No` tokens in a negative-risk event into pUSD plus `Yes` tokens on the other markets in the event - enable/disable auto-redeem - **Miscellaneous** - detect wallet signature type - get base/poly proxy/safe proxy/deposit wallet address from `private_key` ### AsyncPolymarketWebsocketsClient / PolymarketWebsocketsClient Market data, authenticated user events, crypto/equity prices, comments/reactions, activity, sports Use `AsyncPolymarketWebsocketsClient` for production services, bots, and multi-stream workflows. Use `PolymarketWebsocketsClient` when you need a blocking wrapper around the same async implementation. - **Connection model** - `open_*_connection()` starts a stream and returns a handle for health checks, dynamic subscription management, and explicit shutdown - `run_*_stream()` starts a stream and waits until it closes - callbacks may be synchronous or async - default callback payloads are parsed Pydantic events - set `parse_messages=False` to receive raw websocket text - `connection.close()` is the default shutdown path: market, real-time-data, and sports sockets stop quickly and may drop queued callback events; user sockets close gracefully - `connection.graceful_close()` stops reading new messages and drains queued callback events before returning, useful for recorders/audit pipelines that need to process pending events - **Market socket** - subscribe by `token_ids` - receive book snapshots, price changes, tick-size changes, last-trade prices, best bid/ask updates, new-market events, and market-resolution events - tracks whether the local market book state is synchronized after a full snapshot - pass `local_order_books=LocalOrderBookStore()` to update local order books directly in the socket reader before callback queue processing - **User socket** - subscribe with `ApiCreds`, optionally restricted by `condition_ids` - receive order events (`live`, `canceled`, `matched`) - receive trade events (`matched`, `mined`, `confirmed`, `retrying`, `failed`) - defaults to disconnecting on queue overflow to avoid silently dropping user activity - **Real-time data socket** - subscribe with RTDS subscription objects or dictionaries - dynamically `subscribe()` and `unsubscribe()` without reconnecting - receive `trades` / `orders_matched` events filtered by event `slug` or market `slug` - receive crypto/equity prices - receive comment/reaction events (`created`, `removed`) - **Sports socket** - receive sports game state updates (game start, score change, period change, game end) - **Reliability** - automatic reconnect with configurable backoff - heartbeats and pong tracking - structured lifecycle, parse, subscription, stale-feed, and queue-overflow logs - bounded message queues with configurable overflow policy - health snapshots via `connection.health` / `get_health()` Notebook quick start: ```python from polymarket_apis import AsyncPolymarketWebsocketsClient async def on_event(event: object) -> None: print(event) client = AsyncPolymarketWebsocketsClient() connection = await client.open_market_connection( token_ids=["...", "..."], process_event=on_event, ) ``` check health at: ```python connection.health() # or get bool at connection.is_healthy() ``` close: ```python await connection.close() await client.close() ``` Use `graceful_close()` when queued callback delivery matters more than fast shutdown: ```python await connection.graceful_close() await client.close() ``` ### PolymarketGraphQLClient / AsyncPolymarketGraphQLClient Goldsky-hosted subgraph queries. - **Endpoints** - `activity_subgraph` - `fpmm_subgraph` - `open_interest_subgraph` - `orderbook_subgraph` - `pnl_subgraph` - `positions_subgraph` - `sports_oracle_subgraph` - `wallet_subgraph` - **Queries** - `query()` takes a GraphQL query string and returns the raw JSON