Skip to main content
Sign each request with a fresh HMAC signature. The cURL and HTTP header values below are placeholders, not reusable credentials. For runnable signing code, use request signing examples.
Lists the wallets holding an open position in one market, ranked by the metric you choose, with each holder’s trade count and first/last trade time in this market. The route takes your Data API credential (analytics.read). Each successful response costs 1 API credit.
  • Venue: Polymarket only. There is no venue parameter.
  • Exchange scope: CTF Exchange V1 and V2 (exchange_scope: "ctf_v1_v2" on every response). Markets created on ExchangeV3 (combo markets, live since about 2026-11-02) are not covered yet.
  • Predexon-compatible names. entries[], rank, user, position_shares, position_value_usd, avg_price, realized_pnl, unrealized_pnl, trade_count, first_trade_at, last_trade_at, pagination and total_count use Predexon’s names, so you can switch base URLs. The other fields are additions.
  • Same numbers as positions. Each row has the same shares, mark, cost, realized and unrealized PnL as the matching row of GET /v2/polymarket/positions for that wallet and token.

GET /v2/polymarket/market/{condition_id}/top-holders

string
required
The market’s CTF condition id: 0x followed by 64 hex characters.
string
yes returns holders of the first outcome (outcome_index 0) only. no returns holders of the second outcome (outcome_index 1) only. Omit it to get both outcomes.
string
Keep positions holding at least this many shares. A positive decimal with at most 6 decimal places.
integer
Keep holders with at least this many trades in this market. Must be 1 or more. Holders with no trade record in this market are excluded.
boolean
default:"false"
true also fills total_count, total_holders and unseeded_holders. Otherwise those fields are null.
string
default:"shares"
Rank by shares, value (position value at the current mark), pnl (realized + unrealized) or trades (trade count in this market). Always descending. A holder whose figure is unknown sorts last in every sort.
integer
default:"100"
Rows per page, 1–100.
string
The pagination.pagination_key of the previous page. It only works with the same side, min_shares, min_trades and sort. A key from a different query is refused with a 400.
Unknown query parameters, and parameters given twice, are refused with 400. They are never ignored.

Response

array
required
One object per (holder, outcome token), in rank order. The fields are in the table below.
object
required
limit, count, has_more (boolean) and pagination_key (string, null on the last page).
integer
required
Number of positions in the filtered set. null unless include_count=true.
integer
required
Distinct holder wallets in the filtered set. null unless include_count=true.
integer
required
Wallets holding this market whose balance is not yet known. They are counted here but not ranked. null unless include_count=true.
string
required
The requested condition id.
string
required
The Polymarket market id, or null if it is not known.
string
required
Always polymarket.
string
required
Always ctf_v1_v2: the CTF Exchange V1/V2 facts. ExchangeV3 (combo) fills are not included yet.
string
required
Always market: trade_count, first_trade_at and last_trade_at count this market only, not the wallet’s lifetime.
string
required
The sort that was applied.
integer
required
Unix seconds when the response was built.
Entry fields. Money and share counts are plain JSON numbers built from exact integers. Prices are 0–1. Timestamps are unix seconds.
Unknown is null, never 0. For many open holders, the cost of their shares cannot yet be attributed. Those rows have cost_source: "unknown", with avg_price, cost_basis, unrealized_pnl and total_pnl all null. Do not read null as a zero cost or a breakeven position.

Ordering and paging

Ties are broken by wallet, then token id. Paging is keyset-based, so a page never repeats or skips a row while the data does not change. With sort=value or sort=pnl, rows are ordered by live marks, so a page boundary is exact only while the marks hold between your two requests. shares and trades are exact.

Errors

Two layers answer a 400, and their envelopes differ. The API edge sends {"error": "<message>", "code": "unknown_param" | "invalid_param", "param": "<name>"}. The analytics service sends {"error": "BAD_REQUEST" | "NOT_FOUND" | "UNAVAILABLE", "message": "<message>"}. Read the typed code from code when it is present, otherwise from error.