GET
/v2/positionsList 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
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| user | query | stringnull | yes | The wallet to anchor on. At least one of `user`/`condition` is required. |
| condition | query | stringnull | no | Condition 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. |
| limit | query | integernull | no | First-page size. Ignored when `cursor` is supplied (the cursor's size wins). |
| cursor | query | stringnull | no | Opaque 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. |
| status | query | stringnull | no | One 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_id | query | stringnull | no | Event id(s), comma-separated (at most 20 distinct values). User-anchored only. |
| title | query | stringnull | no | Case-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_type | query | stringnull | no | `CASH` or `TOKENS`; defaults to `TOKENS` (the /v2/trades-homogenized filter pair, replacing the former `size_threshold`). |
| filter_amount | query | numbernull | no | The 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_archived | query | booleannull | no | Also 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_by | query | stringnull | no | One 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. |
| start | query | integernull | no | Inclusive lower bound on `last_event_at`, epoch seconds; omit or `0` for unbounded (the `/v2/activity` + `/v2/trades` vocabulary). |
| end | query | integernull | no | Inclusive 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_direction | query | stringnull | no | `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
| Field | Type | Description |
|---|---|---|
| archived | boolean | Whether the market is archived; tells a caller using `includeArchived` which rows the flag surfaced. |
| avg_price | number | Weighted-average entry price per share, in USDC. |
| condition_id | string | The on-chain condition id. |
| current_price | number | Live mark per share, in USDC. |
| current_size | number | The CURRENT holding, in shares (~0 residual on the CLOSED arm). |
| current_value | number | `current_size × current_price`, in USDC. |
| end_date | string | Market end date, `YYYY-MM-DD`; `1970-01-01` when Gamma has none. |
| entry_cost_usdc | number | The fee-EXCLUSIVE entry basis. |
| entry_fees_usdc | number | Attributed 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_id | string | Gamma event id of the parent event. |
| event_slug | string | Parent event slug. |
| first_entry_at | integer | First acquisition of the position (buy, split, mint, transfer in), epoch seconds; 0 when no acquisition is recorded. |
| icon | string | Market icon URL. |
| last_event_at | integer | The row's last economics event, epoch seconds; 0 without native state. |
| mergeable | boolean | Whether the wallet also holds the opposite outcome, so the pair can merge back into collateral. |
| name | string | Profile display name of the wallet. |
| negative_risk | boolean | Whether the market belongs to a neg-risk group. |
| opposite_outcome | string | Label of the market's other outcome; what a merge pairs with. |
| opposite_token_id | string | Token id of the market's other outcome. |
| outcome | string | Label of the held outcome (e.g. `Yes`). |
| outcome_index | integer | Index of the held outcome within the market; `999` means the outcome could not be labeled. |
| percent_pnl | number | `(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_pnl | number | `(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_image | string | Profile image URL. |
| proxy_wallet | string | Proxy wallet holding the position. |
| realized_pnl | number | Realized PnL in USDC, cumulative for the position. |
| redeemable | boolean | Whether the position can be redeemed now: its market resolved and the tokens are still held (losing sides included; redeemable ≠ won). |
| slug | string | Market slug; the URL segment on polymarket.com. |
| status | string | The row's actual state; can be narrower than the requested `status`, since an `OPEN` request also returns `REDEEMABLE` rows. |
| title | string | Market question title (Gamma enrichment; empty when unenriched). |
| token_id | string | The outcome token id. |
| total_cost_usdc | number | Gross (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_pnl | number | Always equals `realized_pnl + unrealized_pnl`. |
| total_size | number | LIFETIME bought shares (the WAC denominator), never the current balance; that is `current_size`. |
| unrealized_pnl | number | Unrealized (mark-to-market) PnL: `current_value - entry_cost_usdc`. |
| verified | boolean | Profile verification badge. |
Responses
| Status | Meaning |
|---|---|
| 200 | A page of positions |
| 400 | Missing 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 |
| 429 | Service is at heavy-query capacity; retry after `Retry-After` |
| 500 | Internal server error |
| 503 | Timed 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
- GET /positions Data API v1
- GET /v2/positions/combos Data API v2
From Polymarket's published OpenAPI spec and rate-limit tables, synced 2026-10-04.