Polymarket API reference / Data API v2
GET/v2/positions

List positions for a user or market

A keyset page of positions in the standard `{ data, pagination }` envelope. One route serves a user's open book, their closed book (`status=CLOSED`), and a market's holders (market anchor). At least one of `user`/`condition` is required. Supplying both anchors on the user and applies `condition` as a narrowing filter.

Base URL
https://data-api.polymarket.com
Auth
Public, no key
Rate limit
200 req / 10s (20 per second)
Limit applies to
/v2/positions, /v2/positions/combos

Things to know

Every v2 response wraps its payload in a data field. Paginated reads return an opaque cursor in pagination and do not accept offset. Under load the API answers 429 with a Retry-After header.

Parameters

NameInTypeRequiredDescription
userquerystringnullyesThe wallet to anchor on. At least one of `user`/`condition` is required.
conditionquerystringnullnoCondition id(s), comma-separated (at most 20 distinct values). With `user`, narrows that user's positions (all ids honoured). Without `user`, anchors on the market's holders; exactly one id is accepted there, and a multi-id list is rejected rather than silently truncated.
limitqueryintegernullnoFirst-page size. Ignored when `cursor` is supplied (the cursor's size wins).
cursorquerystringnullnoOpaque pagination cursor from a prior response's `next_cursor`. It carries the page position, page size, and the status/sort/direction it was minted under.
statusquerystringnullnoOne of `OPEN`, `REDEEMABLE`, `REDEEMABLE_LOST`, `MERGEABLE`, or `CLOSED`; defaults to `OPEN`. `REDEEMABLE_LOST` requires `user`, supports all six sorts, and defaults to `CURRENT_VALUE`; it preserves row status `REDEEMABLE`.
event_idquerystringnullnoEvent id(s), comma-separated (at most 20 distinct values). User-anchored only.
titlequerystringnullnoCase-insensitive market-title substring filter, honoured on every anchor and status. SQL LIKE wildcards (`%`, `_`) keep their usual meaning; empty or whitespace-only is treated as absent; at most 200 characters.
filter_typequerystringnullno`CASH` or `TOKENS`; defaults to `TOKENS` (the /v2/trades-homogenized filter pair, replacing the former `size_threshold`).
filter_amountquerynumbernullnoThe filter floor. `TOKENS`: minimum CURRENT holding in shares (defaults to the 0.1 dust floor; applies to `OPEN`/`REDEEMABLE`; a user's `CLOSED` set is not narrowed by it, and on a market anchor it moves the OPEN/CLOSED boundary).
include_archivedquerybooleannullnoAlso include positions on archived markets; defaults to `false`. `OPEN`/`REDEEMABLE`/`REDEEMABLE_LOST`/`MERGEABLE` only; combining it with `status=CLOSED` is rejected. Inactive markets remain excluded either way.
sort_byquerystringnullnoOne of `CURRENT_VALUE`, `PRICE`, `TOKENS`, `UNREALIZED_PNL`, `REALIZED_PNL`, `TOTAL_PNL`, or `TIMESTAMP` (the row's `last_event_at`). `PRICE` ranks by the response's effective `current_price` on every supported anchor and status.
startqueryintegernullnoInclusive lower bound on `last_event_at`, epoch seconds; omit or `0` for unbounded (the `/v2/activity` + `/v2/trades` vocabulary).
endqueryintegernullnoInclusive upper bound on `last_event_at`, epoch seconds; omit or `0` for unbounded. A position with no native economics carries no `last_event_at` and is therefore **excluded by any bound**, in either direction; a window asks which positions moved inside it, and a row with no clock has no answer.
sort_directionquerystringnullno`ASC` or `DESC`; defaults to `DESC`.

user is listed as optional in Polymarket's OpenAPI spec, but the API returns 400 without it (checked 2026-10-04).

Example

curl -X GET "https://data-api.polymarket.com/v2/positions?user=<user>"

Returns a list of objects with these fields

FieldTypeDescription
archivedbooleanWhether the market is archived; tells a caller using `includeArchived` which rows the flag surfaced.
avg_pricenumberWeighted-average entry price per share, in USDC.
condition_idstringThe on-chain condition id.
current_pricenumberLive mark per share, in USDC.
current_sizenumberThe CURRENT holding, in shares (~0 residual on the CLOSED arm).
current_valuenumber`current_size × current_price`, in USDC.
end_datestringMarket end date, `YYYY-MM-DD`; `1970-01-01` when Gamma has none.
entry_cost_usdcnumberThe fee-EXCLUSIVE entry basis.
entry_fees_usdcnumberAttributed BUY-fee total for the position. Disclosure only: `entry_cost_usdc` is already fee-exclusive, so never re-deduct this from a PnL column.
event_idstringGamma event id of the parent event.
event_slugstringParent event slug.
first_entry_atintegerFirst acquisition of the position (buy, split, mint, transfer in), epoch seconds; 0 when no acquisition is recorded.
iconstringMarket icon URL.
last_event_atintegerThe row's last economics event, epoch seconds; 0 without native state.
mergeablebooleanWhether the wallet also holds the opposite outcome, so the pair can merge back into collateral.
namestringProfile display name of the wallet.
negative_riskbooleanWhether the market belongs to a neg-risk group.
opposite_outcomestringLabel of the market's other outcome; what a merge pairs with.
opposite_token_idstringToken id of the market's other outcome.
outcomestringLabel of the held outcome (e.g. `Yes`).
outcome_indexintegerIndex of the held outcome within the market; `999` means the outcome could not be labeled.
percent_pnlnumber`(current_value - entry_cost_usdc) / entry_cost_usdc`, as a percent. Fee-exclusive basis, and the numerator is `unrealized_pnl`; not `total_pnl / total_cost_usdc`.
percent_realized_pnlnumber`(current_value - total_size × avg_price) / (total_size × avg_price)`, as a percent. A compatibility shape: despite the name, it is not `realized_pnl` over a basis.
profile_imagestringProfile image URL.
proxy_walletstringProxy wallet holding the position.
realized_pnlnumberRealized PnL in USDC, cumulative for the position.
redeemablebooleanWhether the position can be redeemed now: its market resolved and the tokens are still held (losing sides included; redeemable ≠ won).
slugstringMarket slug; the URL segment on polymarket.com.
statusstringThe row's actual state; can be narrower than the requested `status`, since an `OPEN` request also returns `REDEEMABLE` rows.
titlestringMarket question title (Gamma enrichment; empty when unenriched).
token_idstringThe outcome token id.
total_cost_usdcnumberGross (fee-INCLUSIVE) basis. Always exactly `entry_cost_usdc + entry_fees_usdc`; the contract sums the two served columns, so the identity holds on every row of every arm.
total_pnlnumberAlways equals `realized_pnl + unrealized_pnl`.
total_sizenumberLIFETIME bought shares (the WAC denominator), never the current balance; that is `current_size`.
unrealized_pnlnumberUnrealized (mark-to-market) PnL: `current_value - entry_cost_usdc`.
verifiedbooleanProfile verification badge.

Responses

StatusMeaning
200A page of positions
400Missing anchor, invalid query params or cursor, an unknown status/sort/direction, a malformed 'condition' id, an over-long 'title', or a 'user' that is a known protocol contract address
429Service is at heavy-query capacity; retry after `Retry-After`
500Internal server error
503Timed out (the request deadline, the datastore statement timeout, or the connection pool's acquire budget) or a serving dependency is unavailable; retry after `Retry-After`

Related endpoints

Polymarket's API serves current state only, so it cannot show what a market's book looked like after it resolved. PolyTest records Up/Down markets while they run and keeps the snapshots. Try it free, or read the full rate limit reference.

From Polymarket's published OpenAPI spec and rate-limit tables, synced 2026-10-04.