API · Reference
Quick answer (updated 2026-10-25)
The Polymarket Data API is public and needs no key. Use version 2 at https://data-api.polymarket.com/v2: /v2/positions?user=<wallet> for a wallet's positions and PnL, /v2/trades for trade history, /v2/activity for trades, splits, merges, redemptions and rewards, /v2/value for portfolio value, /v2/holders?condition= for a market's top holders and /v2/leaderboard for the trader leaderboard. Every v2 response wraps results in data and pages with pagination.next_cursor. Pass the account wallet address (the proxy_wallet), not the signing key. Polymarket's docs mark the old v1 routes retired from 24 October 2026.
The Data API answers "who holds what and what happened": positions, profit and loss, trade and activity feeds, holders, open interest and leaderboards, for any wallet or market, with nothing more than an HTTP GET. This page lists every v2 endpoint with its parameters, shows real responses, gives Python and TypeScript code for the official SDKs, and maps the old v1 routes to v2. Polymarkets.co.il is an independent guide, not Polymarket. Everything was checked on 25 October 2026 against Polymarket's developer documentation and the published v2 OpenAPI contract, and the endpoints were called live to confirm their shape.
What this page answers
Polymarket Data API endpoints (v2)
Base URL https://data-api.polymarket.com; every route below starts with /v2. No API key, no signature, no account. The full machine-readable contract is published at data-api.polymarket.com/v2/openapi.json.
| Endpoint | Returns | Key parameters |
|---|---|---|
GET /v2/positions | A wallet's or a market's positions, with cost basis and PnL | user or condition (one required), status, sort_by, title, limit (max 1000) |
GET /v2/value | Portfolio value of a wallet | user (required), condition |
GET /v2/trades | Trades for a wallet, market, event, or the global feed | user, condition, event_id, side, start, end, taker_only |
GET /v2/activity | Everything a wallet did: trades, splits, merges, redemptions, rewards, rebates, deposits | user, type, condition, start, end, limit (default 100, max 1000) |
GET /v2/holders | A market's top holders per outcome | condition (required), limit, include_pnl |
GET /v2/leaderboard | Trader leaderboard by PnL or volume | time_period, category, sort_by, user |
GET /v2/biggest-winners | Largest single winning positions | time_period, category |
GET /v2/user-pnl | A wallet's cumulative PnL series | user, interval, fidelity |
GET /v2/user-stats | Profile stats, including the number of markets traded | user |
GET /v2/user-volume | A wallet's trading volume over a window | user, start, end |
GET /v2/approvals | A wallet's token and operator approvals on Polygon | user |
GET /v2/oi | Open interest per market | condition |
GET /v2/live-volume | Live volume for an event | event_id |
GET /v2/prices-history | One outcome token's price history | token_id, interval or start/end or as_of, bucket_seconds |
GET /v2/resolutions | Resolution state (proposals, disputes, settlement estimates) | condition, event_id, question_id |
GET /v2/tokens | Token IDs or conditions mapped to markets and outcomes | token_id (up to 50) or condition |
GET /v2/positions/combos, /v2/activity/combos | Combo positions and combo activity | user |
GET /v2/builders/leaderboard, /v2/builders/volume | Builder rankings and volume over time | time_period, interval |
GET /v2/status | Data freshness (how far behind the chain the API is) | None |
/v2/positions: a wallet's positions and PnL
The workhorse. Pass a wallet with user= to get its open positions, each with cost basis and profit and loss already computed; pass condition= instead to list a market's positions.
curl -s "https://data-api.polymarket.com/v2/positions?user=$WALLET&limit=1"
Real response (25 October 2026, trimmed):
{
"data": [
{
"proxy_wallet": "0x983eedfb...6748",
"token_id": "2032407043...6504",
"condition_id": "0xf74a1dd0...feb8",
"current_size": 12000.0,
"avg_price": 0.38,
"entry_cost_usdc": 4560.0,
"entry_fees_usdc": 141.36,
"current_price": 0.0,
"current_value": 0.0,
"realized_pnl": -141.36,
"unrealized_pnl": -4560.0,
"total_pnl": -4701.36,
"status": "REDEEMABLE",
"redeemable": true,
"mergeable": false,
"negative_risk": true,
"title": "Will Genoa CFC win on 2026-09-20?",
"outcome": "Yes",
"outcome_index": 0,
"opposite_token_id": "4853053701...1490",
"first_entry_at": 1789907647,
"last_event_at": 1789917433
}
],
"pagination": { "limit": 1, "offset": 0, "has_more": true, "next_cursor": "eyJkYXRh..." }
}
| Field | Meaning |
|---|---|
token_id | The outcome token: pass it to the CLOB to read the book or trade |
current_size, avg_price | Shares held and average entry price |
entry_cost_usdc, entry_fees_usdc | Entry cost without fees, and the buy fees on top |
realized_pnl, unrealized_pnl, total_pnl | Profit and loss in USD; total = realized + unrealized |
status | OPEN, REDEEMABLE (resolved, can be claimed), MERGEABLE or CLOSED |
Filter with status: OPEN (the default), REDEEMABLE, REDEEMABLE_LOST (held losing positions), MERGEABLE (complete Yes and No pairs) or CLOSED (fully exited positions with their realized PnL). Sort with sort_by: CURRENT_VALUE, PRICE, TOKENS, UNREALIZED_PNL, REALIZED_PNL, TOTAL_PNL or TIMESTAMP.
Which address to pass: the proxy wallet, not the signing key
Polymarket accounts trade from an account wallet (a Deposit Wallet for accounts created since May 2026, or an older Proxy Wallet or Safe) that is different from the key that signs. That account wallet is what holds positions, what polymarket.com shows in the profile menu, and what the Data API returns as proxy_wallet. Pass it as user. Querying the signing key (the EOA) usually returns an empty list. To find a trader's wallet, take proxy_wallet from /v2/trades, /v2/holders or /v2/leaderboard (where it appears as user_id). Wallet types are explained in authentication and wallets.
/v2/trades: trade history
Every fill, newest first. With user= it is one wallet's history; with condition= or event_id= one market or event; with neither, the global feed. By default (taker_only=true) each fill appears once, on the taker's side; taker_only=false adds the maker rows.
curl -s "https://data-api.polymarket.com/v2/trades?user=$WALLET&limit=100"
{
"data": [
{ "proxy_wallet": "0x53805554...4b3", "side": "SELL", "token_id": "5448579069...0081",
"condition_id": "0x2093b156...1be8", "size": 0.35, "price": 0.61, "timestamp": 1792951041,
"title": "Bitcoin Up or Down - October 25, 1:45PM-2:00PM ET", "outcome": "Up",
"transaction_hash": "0xa39fe4d9...f730" }
],
"pagination": { "limit": 1, "offset": 0, "has_more": true, "next_cursor": "eyJkYXRh..." }
}
Timestamps are Unix seconds. transaction_hash lets you verify the trade on a Polygon block explorer.
/v2/activity: the full on-chain action log
Trades are only part of what a wallet does. /v2/activity returns every action, filtered with type (comma-separated): TRADE, SPLIT, MERGE, REDEEM, CONVERSION, MIGRATION, REWARD, YIELD, MAKER_REBATE, TAKER_REBATE, REFERRAL_REWARD, DEPOSIT, WITHDRAWAL or TIP. Leaving type out returns everything except tips.
curl -s "https://data-api.polymarket.com/v2/activity?user=$WALLET&type=SPLIT,MERGE,REDEEM&limit=50"
Two details catch people out. An omitted start only reaches back three years; send start=1 for the full history. And a redemption appears as one row per outcome, so redeeming both sides of a market gives two rows (sum usdc_size for the payout). Split, merge and redeem are explained in the CLOB guide.
/v2/value: portfolio value
curl -s "https://data-api.polymarket.com/v2/value?user=$WALLET"
{ "data": { "proxy_wallet": "0x983eedfb...6748", "value": 0.0 } }
The value marks single-market holdings to market and counts unresolved combo positions at cost. Add condition= (up to 20 IDs) to value only some markets.
/v2/holders: who holds a market
curl -s "https://data-api.polymarket.com/v2/holders?condition=$CONDITION_ID&limit=20"
Returns the top holders for each outcome token, netted per wallet; limit applies per token (default 100, max 1000). With include_pnl=true (one market at a time, up to 100 rows) each holder also gets average price, entry cost and PnL, which is useful for spotting concentrated or underwater positions.
/v2/leaderboard: the trader leaderboard endpoint
curl -s "https://data-api.polymarket.com/v2/leaderboard?time_period=week&sort_by=PNL&limit=10"
{ "data": [ { "rank": 1, "user_id": "0x57a85952...ebc2", "pnl": 1460154.79, "volume": 832550.81,
"user_name": "...", "verified": false } ],
"pagination": { "limit": 2, "offset": 0, "has_more": true, "next_cursor": "eyJkYXRh..." } }
time_period is day (default), week, month or all; sort_by is PNL (default) or VOLUME; category is overall (default), a market category such as sports, or combos / esports. Add user= to look up one wallet's standing.
Python and TypeScript: the official SDKs
The unified SDKs, polymarket-client 0.14.0 (Python) and @polymarket/client 0.14.0 (TypeScript), call Data API v2 since version 0.10.0, handle cursors for you and need no key for these reads.
Python
# pip install polymarket-client
import asyncio
from polymarket import AsyncPublicClient
WALLET = "0x..." # the account (proxy) wallet
async def main():
async with AsyncPublicClient() as client:
page = await client.list_positions(user=WALLET, page_size=50).first_page()
for p in page.items:
print(p.title, p.outcome, p.current_size, "PnL", p.total_pnl)
value = await client.get_portfolio_value(user=WALLET)
print("portfolio value", value)
async for item in client.list_activity(user=WALLET, page_size=100).iter_items():
print(item)
break # remove to walk the full history
asyncio.run(main())
TypeScript
// npm install @polymarket/client@latest
import { createPublicClient } from "@polymarket/client";
const client = createPublicClient();
const wallet = "0x..."; // the account (proxy) wallet
const page = await client.listPositions({ user: wallet, pageSize: 50 }).firstPage();
for (const p of page.items) console.log(p.title, p.outcome, p.currentSize, "PnL", p.totalPnl);
const value = await client.fetchPortfolioValue({ user: wallet });
console.log("portfolio value", value.value);
Plain HTTP with cursor pagination
import requests
def walk(path, **params):
url = "https://data-api.polymarket.com" + path
cursor = None
while True:
q = dict(params, cursor=cursor) if cursor else params
r = requests.get(url, params=q, timeout=20)
r.raise_for_status()
body = r.json()
yield from body["data"] or []
cursor = body["pagination"]["next_cursor"]
if not cursor:
break
for t in walk("/v2/trades", user="0x...", limit=500):
print(t["timestamp"], t["side"], t["size"], t["price"], t["title"])
Rules from the docs: there is no offset parameter (v2 rejects it with a 400); stop when next_cursor is null, not when a page looks short; on /v2/trades and /v2/activity resend the same filters with every cursor; and after the first page the cursor's own page size wins over limit.
Units, pagination and rate limits
- Envelope: every response is
{ "data": ..., "pagination": {...} }. A miss isdata: nullor an empty list, not an error. - Units: plain
sizeandvolumeare shares; fields ending in_usdcare US dollars; all amounts are JSON numbers.outcome_index: 999means the outcome could not be labelled. - IDs: markets are selected with
condition(aliasescondition_id,conditionId), up to 20 per request. Parameters accept snake_case or camelCase. - Rate limits (per IP, 25 Oct 2026): 800 requests per 10 s across
/v2;/v2/trades300;/v2/positionsand/v2/activity200 each. Under load the API answers429with aRetry-Afterheader. - Freshness:
/v2/statusreports how many seconds and blocks the data trails Polygon (a few seconds when we checked).
Data API v1 to v2: route mapping
Polymarket's migration guide states that Data API v1 is retired on 24 October 2026; only GET /v1/accounting/snapshot stays. When we checked on 25 October 2026 the v1 /positions and /value routes still answered, but build on v2: v1 is frozen and can stop at any time.
| v1 route | v2 route |
|---|---|
/positions | /v2/positions (market becomes condition) |
/closed-positions | /v2/positions?status=CLOSED |
/v1/market-positions | /v2/positions?condition= |
/trades, /activity, /value | /v2/trades, /v2/activity, /v2/value |
/holders, /oi, /live-volume | /v2/holders, /v2/oi, /v2/live-volume |
/traded | /v2/user-stats |
/v1/leaderboard | /v2/leaderboard |
CLOB /prices-history | /v2/prices-history on the data host |
What changes in your code: results move inside data; fields switch from camelCase (proxyWallet, conditionId, cashPnl) to snake_case (proxy_wallet, condition_id, unrealized_pnl); limit/offset paging (capped at 10,000 rows in v1) becomes cursors; and v2 returns 400 for the old market and offset parameters instead of ignoring them.
Data API, Gamma API, CLOB or a subgraph?
| You want | Use |
|---|---|
| Positions, PnL, trade history, holders, leaderboards | Data API (this page) |
| Find markets and events, token IDs, active markets sorted by liquidity or closing soon | Gamma API, e.g. /markets/keyset?closed=false&order=liquidity_num&ascending=false with end_date_max for markets ending soon |
| Live order book, prices, placing orders | CLOB API and the WebSocket |
| Raw on-chain history you index yourself | Polygon logs or an indexer; see on-chain data |
docs.polymarket.com and data-api.polymarket.com), the Gamma OpenAPI spec, and live calls to /v2/positions, /v2/trades, /v2/activity, /v2/value, /v2/leaderboard, /v2/status and the v1 /positions and /value routes.Next: CLOB API (read the book, trade) · Gamma API (find markets) · Build a copy-trading bot.



