Who Holds a Polymarket Market?

Pull open Polymarket positions across every wallet in a market with one Data API call. Shares from on-chain balances, live marks, honest nulls, cursor pagination up to 1,000 rows.

You used to answer "who holds this market?" one wallet at a time. Now one signed call returns every open position Bravado indexes for that market. GET /v2/polymarket/positions pages up to 1,000 rows per request. Shares come from on-chain balances. Marks are live. Unknown values stay null.

This post shows what the endpoint returns, where the numbers come from, and a real snapshot of the 2026 House control market as of 12:15 PM ET on Nov 2, 2026.

What does market-wide positions return?

The same position model that powers GET /trader-analytics/positions is available across every indexed wallet. On the Polymarket surface the venue is fixed, so you do not pass a venue parameter.

One request can answer three questions:

  • What does this wallet hold? Pass wallet.

  • Who holds this market or token? Pass market_id, condition_id, or token_id.

  • What changed since I last looked? Pass updated_after (unix ms) and follow the cursor.

A request must be bounded. You need at least one of those filters. A bare status=open returns 400 with "code": "unbounded_scan".

Each row includes the wallet, market and token ids, outcome label, shares, average entry, cost basis, live price and value, realized and unrealized PnL, status, and mark metadata. Money and share fields are exact decimal strings with 6 places. Parse them with a decimal type.

You can narrow further with status, time or block ranges, and min_position_value. Page size is 1 to 1,000. Follow next_cursor until it is null.

Where do the numbers come from?

Four rules matter when you read a row.

Shares. On Polymarket, shares come from on-chain balanceOf state on Polygon. The row marks that with open_value_source: "book". Amounts are computed from integer micro-units, then returned as 6-decimal strings.

Money fields. Prices, values, cost basis, and PnL are exact decimal strings. They are not floating-point guesses.

Cost basis. Cost basis comes only from recorded lots. If there is no lot record, cost_basis and avg_entry_price are null. Bravado does not estimate them. Unknown is never written as "0.000000".

Live marks. Marks are computed on every request. They are not cached on the position. The order is:

  1. Resolution payout, if the market has resolved.

  2. A fresh quote.

  3. The last real trade.

  4. Unknown.

If none of those exists, mark_source is unknown and current_price, current_value, and unrealized_pnl are null. Unknown is never reported as 0.

One more honesty rule: when open_value_source is unknown_book_unseeded, Bravado has not indexed that wallet's on-chain balances yet. In that case shares, cost fields, current_value, and unrealized_pnl are all null. The row is a placeholder, not a guess.

Mark precedence: resolution, then quote, then last trade, then unknown. Cost basis only from lots. Unseeded books return null shares and value.

Text version: mark order is resolution → quote → trade → unknown. Cost basis is lot-only or null. Unseeded books return null shares and value, never estimates.

What did the House market look like?

Here is one real call against the 2026 House control market. The numbers below are from signed GET /v2/polymarket/positions responses served at about 12:15 PM ET on Nov 2, 2026. They are one snapshot. They are not a typical day and not live as of this post.

  • Market id: 562802

  • Condition id: 0xd5d9fc47718bd553592d126b1fa5e87183d27f3936975b0c04cc0f2dec1f1bb4

  • Filter: status=open, six pages at limit=1000

At that moment the market had:

Metric

Value

Open positions

5,253

Unique wallets

5,167

Live marked value

$12.49M

Yes open positions

4,584 at 0.945

No open positions

669 at 0.055

Top 10 wallets

37.7% of marked value

The largest single position was 1.25M Yes shares. Average entry was 0.9396. Marked value was $1.18M. Four Yes positions were not yet seeded on our side. Those rows returned null shares and value instead of a guessed number.

House control snapshot tiles: 5,253 open positions, 5,167 wallets, $12.49M marked, top 10 wallets 37.7%.

Text version: 5,253 open positions across 5,167 wallets; $12.49M marked live; Yes 4,584 at 0.945; No 669 at 0.055; top 10 wallets 37.7%.

Top open positions by live value in the House market snapshot.

Text version: largest Yes position 0xfea3…d227, 1.25M shares, avg 0.9396, value $1.18M. Next Yes rows: $945K, $760K, $409K, $306K. Two No positions appear in the top 10, including one with null average entry because no lot was recorded.

Yes versus No open position counts and marks in the same snapshot.

Text version: 4,584 Yes positions marked at 0.945; 669 No positions marked at 0.055. Seeded Yes shares 12.485M; seeded No shares 12.507M.

Client-side ranking matters. The API order for a market query is by token then wallet, not by value. Sort locally if you need the largest holders.

How do you query it?

Three common patterns:

Question

Filters

What does this wallet hold?

wallet=0x…

Who holds this market?

condition_id=, market_id=, or token_id=

What changed in a market?

updated_after= (unix ms) with a market filter

Docs also allow updated_after on its own for a global state sync. For day-to-day market monitoring, pair it with condition_id, market_id, or token_id.

Three query modes: wallet, market or token, and updated_after with a market filter.

Text version: wallet → holdings for one address. market_id / condition_id / token_id → every holder. updated_after + market filter → incremental changes. Page with cursor up to 1,000 rows.

Example: the three largest open positions above $250,000 in the House market. Replace the signature headers with a fresh HMAC for each request. See the request signing examples.

curl "https://partner-api.bravadotrade.com/v2/polymarket/positions\
?condition_id=0xd5d9fc47718bd553592d126b1fa5e87183d27f3936975b0c04cc0f2dec1f1bb4\
&status=open\
&min_position_value=250000\
&limit=3" \
  -H "X-BRAVADO-API-KEY: $BRAVADO_API_KEY" \
  -H "X-BRAVADO-TIMESTAMP: <unix-ms>" \
  -H "X-BRAVADO-SIGNATURE: <signature-for-this-request>"
curl "https://partner-api.bravadotrade.com/v2/polymarket/positions\
?condition_id=0xd5d9fc47718bd553592d126b1fa5e87183d27f3936975b0c04cc0f2dec1f1bb4\
&status=open\
&min_position_value=250000\
&limit=3" \
  -H "X-BRAVADO-API-KEY: $BRAVADO_API_KEY" \
  -H "X-BRAVADO-TIMESTAMP: <unix-ms>" \
  -H "X-BRAVADO-SIGNATURE: <signature-for-this-request>"
curl "https://partner-api.bravadotrade.com/v2/polymarket/positions\
?condition_id=0xd5d9fc47718bd553592d126b1fa5e87183d27f3936975b0c04cc0f2dec1f1bb4\
&status=open\
&min_position_value=250000\
&limit=3" \
  -H "X-BRAVADO-API-KEY: $BRAVADO_API_KEY" \
  -H "X-BRAVADO-TIMESTAMP: <unix-ms>" \
  -H "X-BRAVADO-SIGNATURE: <signature-for-this-request>"
curl "https://partner-api.bravadotrade.com/v2/polymarket/positions\
?condition_id=0xd5d9fc47718bd553592d126b1fa5e87183d27f3936975b0c04cc0f2dec1f1bb4\
&status=open\
&min_position_value=250000\
&limit=3" \
  -H "X-BRAVADO-API-KEY: $BRAVADO_API_KEY" \
  -H "X-BRAVADO-TIMESTAMP: <unix-ms>" \
  -H "X-BRAVADO-SIGNATURE: <signature-for-this-request>"

Use the documented parameter min_position_value, not min_value. Follow next_cursor for more pages. The response includes served_at (when the live marks were read) and unpriced_positions (open rows with an unknown mark in the span this page covered).

The Polymarket /v2/polymarket/* surface uses a Data API credential with the analytics.read scope. You can try the call in Console. Full field reference: GET /positions.

Why use Bravado?

Bravado is prediction market infrastructure. The Data API gives you licensed Polymarket analytics from on-chain settlement, not a scraped UI.

For positions today:

  • Market-wide holders. One call for every indexed wallet in a market, token, or change window.

  • Honest unknowns. Null when cost or mark is unknown. No silent zeros.

  • Live marks. Resolution, quote, then trade, computed on each request.

  • Same model, two mounts. /v2/polymarket/positions and /trader-analytics/positions share filters, fields, and pagination. The positions page is the integer-exact source of record.

  • Console. Sign a request and inspect the JSON without wiring your app first.

Related reading:

Conclusion

If you need the holders of a market, query the market once. Do not loop wallets.

  • Use condition_id, market_id, or token_id for the full open book of holders.

  • Use wallet when the question is about one address.

  • Use updated_after with a market filter to keep a local copy current.

  • Trust nulls. A missing cost or mark is better than a made-up zero.

Start at the positions docs or try it in Console.

Read more blogs

Trading insights, market analysis, and strategies for Polymarket prediction markets.

© 2026 Bravado. All rights reserved.

Not financial advice. Trade responsibly.

© 2026 Bravado. All rights reserved.

Not financial advice. Trade responsibly.

© 2026 Bravado. All rights reserved.

Not financial advice. Trade responsibly.

© 2026 Bravado. All rights reserved.

Not financial advice. Trade responsibly.