> ## Documentation Index
> Fetch the complete documentation index at: https://docs.polynode.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Positions (All)

> Query every position on Polymarket. Filter by wallet, market, status, or minimum size. Full history with cursor pagination.

Search and filter all Polymarket positions across every wallet. Each position is enriched with market metadata and includes realized P\&L, average entry price, and current size. Sourced directly from onchain settlement data.

## Request

```
GET /v2/onchain/positions
```

### Query parameters

| Parameter        | Type    | Required | Description                                                                                                                                                                                            |
| ---------------- | ------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `wallet`         | string  | No       | Filter by wallet address                                                                                                                                                                               |
| `market_slug`    | string  | No       | Filter by market slug (e.g. `will-zohran-mamdani-win-the-2025-nyc-mayoral-election`)                                                                                                                   |
| `condition_id`   | string  | No       | Filter by condition ID (0x-prefixed, 64 hex chars)                                                                                                                                                     |
| `token_id`       | string  | No       | Filter by outcome token ID                                                                                                                                                                             |
| `status`         | string  | No       | `open` (size > 0), `closed` (size = 0), or `all` (default)                                                                                                                                             |
| `min_size`       | number  | No       | Minimum position size in shares                                                                                                                                                                        |
| `limit`          | integer | No       | Results per page (1-500, default 100)                                                                                                                                                                  |
| `order`          | string  | No       | Sort direction: `desc` (default, most recent first) or `asc`. When `wallet` is set, results are ordered by the wallet's most recent on-chain activity per position. Otherwise, ordered by position ID. |
| `pagination_key` | string  | No       | Cursor from a previous response to fetch the next page. Not used for wallet queries (see Pagination).                                                                                                  |

### Identifying markets

* **`market_slug`** — human-readable URL slug from Polymarket. Returns positions for all outcomes.
* **`condition_id`** — unique condition identifier. Returns positions for all outcomes.
* **`token_id`** — specific outcome token. Returns positions for only that outcome.

## Response

```json theme={null}
{
  "count": 2,
  "pagination": {
    "limit": 2,
    "has_more": true,
    "pagination_key": "0xa973ae12882a1c24a75da7a3b52cb01500b2f639-99881503238784655670984673100655147508393351942279611133016622634826369070119"
  },
  "positions": [
    {
      "wallet": "0xa973ae12882a1c24a75da7a3b52cb01500b2f639",
      "token_id": "99949662063403569895321097192043694236016212986254553767097648841549016857591",
      "size": 0.012785,
      "avg_price": 0.859999,
      "realized_pnl": 0.16,
      "unrealized_pnl": 0.02,
      "current_price": 0.88,
      "market_status": "live",
      "total_bought": 1.16,
      "market": "Will FC Bayern Munchen win on 2026-04-07?",
      "market_slug": "ucl-rma1-bay1-2026-04-07-bay1",
      "outcome": "Yes",
      "condition_id": "0x5f5c8d8fa28d77b5552562f32393ac199fb6b92ed1c1e2239e29c09f7e4eb3f5",
      "image": "https://polymarket-upload.s3.us-east-2.amazonaws.com/champions-league-pic-QIUFsL8vaDdq.png"
    },
    {
      "wallet": "0xa973ae12882a1c24a75da7a3b52cb01500b2f639",
      "token_id": "99881503238784655670984673100655147508393351942279611133016622634826369070119",
      "size": 0,
      "avg_price": 0.98,
      "realized_pnl": 0.1,
      "unrealized_pnl": 0,
      "current_price": null,
      "market_status": "closed",
      "total_bought": 5,
      "market": "Solana Up or Down - December 30, 10:30PM-10:45PM ET",
      "market_slug": "sol-updown-15m-1767151800",
      "outcome": "Down",
      "condition_id": "0xa7f50f1bd65e8fd1790117b3b162dc03489df7b89cd35f4b1093792691b8a327",
      "image": "https://polymarket-upload.s3.us-east-2.amazonaws.com/SOL+fullsize.png"
    }
  ]
}
```

### Position fields

| Field                   | Type                 | Description                                                                                                                                                                                                                                                                                                                                                          |
| ----------------------- | -------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `wallet`                | string               | Wallet address holding the position                                                                                                                                                                                                                                                                                                                                  |
| `token_id`              | string               | Outcome token ID                                                                                                                                                                                                                                                                                                                                                     |
| `size`                  | number               | Current position size in shares. `0` means the position is fully exited.                                                                                                                                                                                                                                                                                             |
| `avg_price`             | number               | Average entry price (0 to 1)                                                                                                                                                                                                                                                                                                                                         |
| `realized_pnl`          | number               | Realized profit/loss in USD. Reflects gains/losses from closed portions of the position and from onchain redemptions of resolved markets. Stays `0` for resolved-but-not-yet-redeemed positions; check `redeemable` to detect those.                                                                                                                                 |
| `unrealized_pnl`        | number               | Unrealized profit/loss in USD on the remaining open shares. For resolved markets, uses the final settlement price (`1.0` for winners, `0.0` for losers). For live markets, uses the current market price. Returns `0` when `size = 0`. Byte-identical to Polymarket's `cashPnl` field for any position covered by both.                                              |
| `current_price`         | number \| null       | Price used to compute `unrealized_pnl`. `1.0` or `0.0` for resolved markets (derived deterministically from on-chain `payoutNumerators`), the live market price for active markets, and `null` when `size = 0` or no price is available.                                                                                                                             |
| `market_status`         | string               | One of: `"live"` (market still trading), `"resolved-win"` (market resolved, this outcome won), `"resolved-loss"` (market resolved, this outcome lost), or `"closed"` (position fully exited, `size = 0`). A fifth value `"resolved-unknown"` may appear briefly for very old markets while resolution data is catching up. Never `"live"` when `resolved_at` is set. |
| `won`                   | boolean \| undefined | Present only on `resolved-win` / `resolved-loss` rows. `true` when this outcome won, `false` when it lost. Derived from on-chain payouts.                                                                                                                                                                                                                            |
| `winning_outcome_index` | number \| undefined  | Present only on resolved rows. Numeric index of the outcome that won (0 or 1 for binary markets). Pair with `outcome_index` to know whether this row is the winning side.                                                                                                                                                                                            |
| `outcome_index`         | number \| undefined  | Numeric index of this row's outcome within the market's outcomes array (0 or 1 for binary markets). Stable across the API regardless of how the UI labels the outcome (`Yes`/`No`, team names, etc.). Use this for cross-row joins instead of parsing `outcome` strings.                                                                                             |
| `total_bought`          | number               | Total amount bought in USD over the lifetime of the position.                                                                                                                                                                                                                                                                                                        |
| `initial_value`         | number \| undefined  | Cost basis in USD of the currently held shares (`size × cost_per_share`). Use this if you need the basis number that matches Polymarket `cashPnl` and what users see in the Polymarket UI.                                                                                                                                                                           |
| `redeemable`            | boolean \| undefined | `true` when the market has resolved and the user can call redeem on the CTF contract to claim payout (or accept loss). Useful for detecting "resolved-but-not-redeemed" positions: filter `market_status = "resolved-loss"` AND `redeemable = true` for unclaimed losses, or `market_status = "resolved-win"` AND `redeemable = true` for unclaimed wins.            |
| `opposite_asset`        | string \| undefined  | Token ID of the OPPOSITE outcome on the same market (the binary counterpart). Useful for fetching the matching position on the other side without re-resolving the condition.                                                                                                                                                                                        |
| `market`                | string               | Market question text                                                                                                                                                                                                                                                                                                                                                 |
| `market_slug`           | string               | Market URL slug                                                                                                                                                                                                                                                                                                                                                      |
| `outcome`               | string               | Outcome label (e.g. "Yes", "No")                                                                                                                                                                                                                                                                                                                                     |
| `condition_id`          | string               | Market condition ID                                                                                                                                                                                                                                                                                                                                                  |
| `image`                 | string \| null       | Market image URL. `null` for some delisted or niche markets.                                                                                                                                                                                                                                                                                                         |
| `event_slug`            | string \| null       | Parent **event** slug, distinct from the per-row market `slug`. For multi-market events (NBA games with several lines, election markets with several candidates, FIFA World Cup with one market per team), this is the parent the markets share. For single-market events, equals the market slug. `null` when event metadata is not yet known.                      |
| `last_activity`         | number \| undefined  | Unix timestamp of the wallet's most recent fill on this position. Present only when querying by `wallet`. Positions with no fill history (e.g. acquired purely via split/merge/redemption) omit this field and sort to the end.                                                                                                                                      |
| `last_trade_at`         | number \| null       | Unix seconds. Latest fill on this token across V1 + V2 exchanges. Same data as `last_activity` but always present (`null` instead of omitted) for consistent shape. Use either; `last_trade_at` is preferred for new integrations.                                                                                                                                   |
| `closed_at`             | number \| null       | Unix seconds. Latest moment this wallet redeemed any outcome of the market for collateral. `null` when the wallet has not redeemed (open positions, positions sold to zero pre-resolution, or wins held but not yet redeemed).                                                                                                                                       |
| `resolved_at`           | number \| null       | Unix seconds. Moment the market was resolved on-chain (when payouts became redeemable). `null` for markets that resolved before polynode began tracking, or for markets that have not yet resolved. Recent markets are fully covered.                                                                                                                                |

### Pagination fields

| Field                       | Type    | Description                                                            |
| --------------------------- | ------- | ---------------------------------------------------------------------- |
| `count`                     | number  | Number of positions in this response                                   |
| `pagination.limit`          | number  | Requested page size                                                    |
| `pagination.has_more`       | boolean | `true` if more results exist beyond this page                          |
| `pagination.pagination_key` | string  | Pass this as `pagination_key` in the next request to get the next page |

## Examples

### All positions for a wallet

<CodeGroup>
  ```bash cURL theme={null}
  curl "https://api.polynode.dev/v2/onchain/positions?wallet=0xa973ae12882a1c24a75da7a3b52cb01500b2f639&limit=100" \
    -H "x-api-key: YOUR_KEY"
  ```

  ```javascript Node.js theme={null}
  const resp = await fetch(
    "https://api.polynode.dev/v2/onchain/positions?wallet=0xa973ae12882a1c24a75da7a3b52cb01500b2f639&limit=100",
    { headers: { "x-api-key": "YOUR_KEY" } }
  );
  const data = await resp.json();
  console.log(data.positions);
  ```

  ```python Python theme={null}
  import requests

  resp = requests.get(
      "https://api.polynode.dev/v2/onchain/positions",
      params={"wallet": "0xa973ae12882a1c24a75da7a3b52cb01500b2f639", "limit": 100},
      headers={"x-api-key": "YOUR_KEY"},
  )
  data = resp.json()
  print(data["positions"])
  ```
</CodeGroup>

### Open positions only

Use `status=open` to get only positions with a non-zero size.

```bash theme={null}
curl "https://api.polynode.dev/v2/onchain/positions?wallet=0xa973ae12882a1c24a75da7a3b52cb01500b2f639&status=open&limit=100" \
  -H "x-api-key: YOUR_KEY"
```

```json theme={null}
{
  "count": 2,
  "pagination": {
    "limit": 2,
    "has_more": true,
    "pagination_key": "0xa973ae12882a1c24a75da7a3b52cb01500b2f639-99504617..."
  },
  "positions": [
    {
      "wallet": "0xa973ae12882a1c24a75da7a3b52cb01500b2f639",
      "token_id": "99949662063403569895321097192043694236016212986254553767097648841549016857591",
      "size": 0.012785,
      "avg_price": 0.859999,
      "realized_pnl": 0.16,
      "unrealized_pnl": 0.02,
      "current_price": 0.88,
      "market_status": "live",
      "total_bought": 1.16,
      "market": "Will FC Bayern Munchen win on 2026-04-07?",
      "market_slug": "ucl-rma1-bay1-2026-04-07-bay1",
      "outcome": "Yes",
      "condition_id": "0x5f5c8d8fa28d77b5552562f32393ac199fb6b92ed1c1e2239e29c09f7e4eb3f5",
      "image": "https://polymarket-upload.s3.us-east-2.amazonaws.com/champions-league-pic-QIUFsL8vaDdq.png"
    }
  ]
}
```

### Who holds a market

Use `market_slug` to see all wallets with positions on a specific market.

```bash theme={null}
curl "https://api.polynode.dev/v2/onchain/positions?market_slug=will-zohran-mamdani-win-the-2025-nyc-mayoral-election&status=open&limit=50" \
  -H "x-api-key: YOUR_KEY"
```

```json theme={null}
{
  "count": 2,
  "pagination": {
    "limit": 2,
    "has_more": true,
    "pagination_key": "0xfffe254008792df0c325325a75a3c6e7aaed436a-10583236..."
  },
  "positions": [
    {
      "wallet": "0xffff3840fbf40fd2e193c01cc299cdf1262cffaf",
      "token_id": "33945469250963963541781051637999677727672635213493648594066577298999471399137",
      "size": 0.009534,
      "avg_price": 0.947999,
      "realized_pnl": -0.54,
      "unrealized_pnl": -5.07,
      "current_price": 0.416,
      "market_status": "live",
      "total_bought": 539.03,
      "market": "Will Zohran Mamdani win the 2025 NYC mayoral election?",
      "market_slug": "will-zohran-mamdani-win-the-2025-nyc-mayoral-election",
      "outcome": "Yes",
      "condition_id": "0xebddfcf7b4401dade8b4031770a1ab942b01854f3bed453d5df9425cd9f211a9",
      "image": "https://polymarket-upload.s3.us-east-2.amazonaws.com/will-zohran-mamdani-win-the-2025-nyc-mayoral-election-EscSJQTT6hWg.jpg"
    }
  ]
}
```

### Large positions

Use `min_size` to find positions above a threshold.

```bash theme={null}
curl "https://api.polynode.dev/v2/onchain/positions?min_size=100&limit=20" \
  -H "x-api-key: YOUR_KEY"
```

```json theme={null}
{
  "count": 2,
  "pagination": {
    "limit": 2,
    "has_more": true,
    "pagination_key": "0xffffffe1e093aacd21e4e281e66d543fb0b23455-98495912..."
  },
  "positions": [
    {
      "wallet": "0xffffffe1e093aacd21e4e281e66d543fb0b23455",
      "token_id": "98813479054803844837498343855179110721772056323529222930302029314504656450267",
      "size": 1100,
      "avg_price": 0.003636,
      "realized_pnl": 0,
      "unrealized_pnl": -3.96,
      "current_price": 0,
      "market_status": "resolved-loss",
      "total_bought": 1100,
      "market": "Bitcoin Up or Down - February 5, 9:45AM-10:00AM ET",
      "market_slug": "btc-updown-15m-1770302700",
      "outcome": "Up",
      "condition_id": "0x3ec09263f6fb247e65635d52c2787dc0f46806f153124e138f42ad03411198b4",
      "image": "https://polymarket-upload.s3.us-east-2.amazonaws.com/BTC+fullsize.png"
    }
  ]
}
```

### Combined filters

Filter by wallet, market, and status at once.

```bash theme={null}
curl "https://api.polynode.dev/v2/onchain/positions?wallet=0xa973ae12882a1c24a75da7a3b52cb01500b2f639&market_slug=ucl-rma1-bay1-2026-04-07-bay1&status=open&limit=10" \
  -H "x-api-key: YOUR_KEY"
```

### Pagination

**Wallet queries** return every position for the wallet in a single response (up to 500), sorted by most recent on-chain activity. No pagination needed — request `limit=500` and read all results. `has_more` is always `false` and no cursor is returned. Wallets with more than 500 lifetime positions are capped at 500.

**Non-wallet queries** (filtering by `market_slug`, `condition_id`, `token_id`, or `min_size` alone) use cursor-based pagination for iterating through large result sets.

```bash theme={null}
# First page
curl "https://api.polynode.dev/v2/onchain/positions?min_size=100&limit=100" \
  -H "x-api-key: YOUR_KEY"

# Next page
curl "https://api.polynode.dev/v2/onchain/positions?min_size=100&limit=100&pagination_key=CURSOR_FROM_PREVIOUS" \
  -H "x-api-key: YOUR_KEY"
```

## Error responses

| Status | Response                                                                               | Condition                         |
| ------ | -------------------------------------------------------------------------------------- | --------------------------------- |
| 400    | `{"error": "market_slug not found"}`                                                   | Invalid or unknown `market_slug`  |
| 400    | `{"error": "condition_id not found"}`                                                  | Invalid or unknown `condition_id` |
| 401    | `{"error": "API key required. Pass via ?key= or x-api-key header."}`                   | Missing API key                   |
| 403    | `{"error": "V2 endpoints require a paid plan. See polynode.dev/pricing for details."}` | Free tier key                     |
| 429    | `{"error": "Rate limited. N req/s for your tier.", "retryAfterMs": ...}`               | Rate limited                      |

## Notes

* Position data is sourced from onchain settlement records. Every position that was ever opened on Polymarket is included.
* The `realized_pnl` field reflects actual profit/loss from closed portions of the position, including onchain redemptions of resolved markets. For open positions, it reflects any partial closes.
* The `unrealized_pnl` field shows the paper profit/loss on the remaining open shares. For resolved markets, this uses the final settlement price ($1 for winners, $0 for losers). For active markets, the current market price is used.
* `market_status` lets clients distinguish live-tradable positions from positions on resolved markets that the wallet never redeemed. Resolved-and-never-redeemed positions still have `size > 0` and will show the correct terminal `unrealized_pnl`.
* Market metadata (`market`, `market_slug`, `outcome`, `condition_id`, `image`) is enriched from our index. Very old or delisted markets may not have metadata.
* When querying by `wallet`, the sort key is the timestamp of the most recent fill for each position, not the position's close date. A position bought months ago and held to resolution (closed only via redemption) will sort by its original buy date.

## Verifying parity with Polymarket

Per-position fields on this endpoint match Polymarket's `data-api.polymarket.com/positions` byte-for-byte. Anyone can verify directly with the standalone Node script below — no internal access required.

### Field map

| polynode field   | Polymarket field |
| ---------------- | ---------------- |
| `token_id`       | `asset`          |
| `condition_id`   | `conditionId`    |
| `size`           | `size`           |
| `avg_price`      | `avgPrice`       |
| `realized_pnl`   | `realizedPnl`    |
| `unrealized_pnl` | `cashPnl`        |
| `current_price`  | `curPrice`       |
| `outcome`        | `outcome`        |

### Self-test script (Node 18+, no dependencies)

```javascript theme={null}
// Save as parity_demo.mjs and run with:
//   POLYNODE_KEY=pn_live_xxx node parity_demo.mjs <wallet>

const KEY = process.env.POLYNODE_KEY;
const WALLET = process.argv[2];

const [pm, us] = await Promise.all([
  fetch(`https://data-api.polymarket.com/positions?user=${WALLET}&sizeThreshold=0&limit=2000`,
    { headers: { 'User-Agent': 'Mozilla/5.0' } }).then(r => r.json()),
  fetch(`https://api.polynode.dev/v2/onchain/positions?wallet=${WALLET}&status=all&limit=2000`,
    { headers: { 'x-api-key': KEY } }).then(r => r.json()).then(j => Array.isArray(j) ? j : (j.positions ?? [])),
]);

const pmByPid = new Map(pm.map(p => [String(p.asset), p]));
const usByPid = new Map(us.map(p => [String(p.token_id), p]));
const shared = [...pmByPid.keys()].filter(k => usByPid.has(k));

let exact = 0, sub_dollar = 0;
for (const pid of shared) {
  const p = pmByPid.get(pid), u = usByPid.get(pid);
  const avgDiff = Math.abs(Number(p.avgPrice) - Number(u.avg_price));
  const pnlDiff = Math.abs(Number(p.realizedPnl) - Number(u.realized_pnl));
  if (avgDiff < 0.005 && pnlDiff < 0.01) exact++;
  if (pnlDiff < 1.0) sub_dollar++;
  console.log(`  ${pid.slice(0,8)}…  PM realPnl=${Number(p.realizedPnl).toFixed(2).padStart(10)}  Us realPnl=${Number(u.realized_pnl).toFixed(2).padStart(10)}  Δ=${pnlDiff.toFixed(4)}`);
}
console.log(`\n  byte-perfect: ${exact}/${shared.length}    sub-$1: ${sub_dollar}/${shared.length}`);
```

Run against any wallet to confirm. Validated 2026-04-30 across diverse wallets at 100 % byte-perfect match on every shared open position.

### What this proves

If you trust Polymarket's positions page, you can trust ours — they're computing the same thing from the same on-chain events. Use this script as ongoing regression coverage in your own integration if PnL accuracy is critical to your product.


## OpenAPI

````yaml GET /v2/onchain/positions
openapi: 3.1.0
info:
  title: PolyNode API
  description: >-
    Real-time Polymarket data API with decoded mempool settlements, OHLCV
    candles, and full Polygon JSON-RPC proxy.
  contact:
    name: PolyNode
    url: https://polynode.dev
  license:
    name: ''
  version: 2.0.0
servers:
  - url: https://api.polynode.dev
    description: Production
security:
  - api_key: []
paths:
  /v2/onchain/positions:
    get:
      tags:
        - Onchain
      summary: Positions
      description: >-
        Query every position on Polymarket. Filter by wallet, market, status, or
        minimum size.
      operationId: onchain_positions
      parameters:
        - name: wallet
          in: query
          schema:
            type: string
          description: Filter by wallet address
        - name: market_slug
          in: query
          schema:
            type: string
          description: Filter by market slug
        - name: condition_id
          in: query
          schema:
            type: string
          description: Filter by condition ID
        - name: token_id
          in: query
          schema:
            type: string
          description: Filter by outcome token ID
        - name: status
          in: query
          schema:
            type: string
            enum:
              - open
              - closed
              - all
            default: all
          description: Filter by position status
        - name: min_size
          in: query
          schema:
            type: number
          description: Minimum position size in shares
        - name: limit
          in: query
          schema:
            type: integer
            default: 100
            minimum: 1
            maximum: 500
          description: Results per page (1-500, default 100)
        - name: order
          in: query
          schema:
            type: string
            enum:
              - desc
              - asc
            default: desc
          description: Sort direction
        - name: pagination_key
          in: query
          schema:
            type: string
          description: Cursor from a previous response to fetch the next page
      responses:
        '200':
          description: Position results with pagination
        '400':
          description: Invalid filter or market not found
        '401':
          description: Unauthorized
        '403':
          description: Invalid API key or free tier
        '429':
          description: Rate limited
      security:
        - apiKey: []
components:
  securitySchemes:
    api_key:
      type: apiKey
      in: header
      name: x-api-key

````