Skip to main content
The Data API answers what happened and who holds what: wallet portfolios and PnL, trade and activity feeds, per-market state such as holders and open interest, and ranked boards. Version 2 serves every read behind one contract, a shared response envelope, cursor pagination, and a unified identifier vocabulary, so a client written against one endpoint already knows how to consume the rest. All v2 routes are served from:
No API key or authentication is required.

Make a First Request

Fetch a wallet’s current positions:
Every response wraps its payload in data; paginated routes add a pagination object (row fields trimmed here for brevity):
A documented miss is data: null or an empty list, never an error. Each endpoint’s reference page describes its full row shape and every filter.

Paginate With Cursors

Pagination is cursor-only; there is no offset query parameter. To walk a result set:
  1. Send the first request with an optional limit (each endpoint documents its default and maximum).
  2. Read pagination.next_cursor from the response and re-send the request with cursor=<next_cursor>.
  3. Stop when next_cursor is null. has_more is exact, so an empty or short page does not mean the walk is over.
Cursors are opaque, signed, and typed per endpoint. Two rules keep a walk consistent:
  • On the feeds (/v2/trades, /v2/activity, /v2/activity/combos), re-send the same filters on every page. The cursor carries only its seek anchor, so changing a filter mid-walk silently re-anchors the feed.
  • Where a cursor binds its query (positions, boards, combo positions), restating the same values is fine, but contradicting them returns a 400. On /v2/positions/combos, keep sending user alongside the cursor. On /v2/holders, keep sending condition. Other routes that bind the query can resume with the bare cursor.
The limit parameter only applies to the first page; once a cursor is supplied, the cursor’s own page size wins. The offset field in pagination is display metadata for numbering rows across pages, not a request parameter.

Shared Conventions

Every v2 endpoint follows the same vocabulary and encoding rules.

Identifiers

Query parameters accept both snake_case and camelCase spellings.

Units and Sentinels

  • Bare volume and size values are outcome shares; fields suffixed _usdc are USD amounts; volumes prefixed taker_ count one side of each trade.
  • All amounts are JSON numbers.
  • outcome_index: 999 means the outcome could not be labeled.
  • A missing or null numeric field means unavailable, never zero.

Time Windows

Windowed routes take start and end as epoch seconds. Treatment of an omitted or 0 bound differs by route (for example, /v2/activity floors an omitted start to three years back, while /v2/prices-history rejects a 0 bound with a 400), so check the parameter documentation on each endpoint page before relying on a default.

Price History Resolution and Availability

/v2/prices-history serves each token from a graded store: a rolling raw window (one-minute serving) plus five-minute, thirty-minute, three-hour, and twelve-hour series. The coarse pair is permanent, so every token’s full history is always available at twelve-hour grain (and at three-hour grain from July 2026 onward); the fine grains are windowed. The availability minimums are: raw at least 7 days, five-minute at least 60 days, thirty-minute at least 90 days. These are floors, not exact horizons: data expires in multi-day chunks, so a grain usually reaches a few days further back than its minimum. bucket_seconds=60 over a window starting a month ago is therefore an incompatible pair: both values are valid, and there is no data at their intersection. The two ways of asking behave differently on purpose:
  • Send bucket_seconds and the request means exactly that grid: the response holds every stored observation snapped to it and nothing else. Over a range where the store has nothing at or below that grain the page is empty, and where the stored history is coarser than the grid the page is sparse. Both are correct answers, never a silent substitution at a different width.
  • Omit it and the server measures, per request, which series actually cover this token and window, and serves the densest one: sized to the span, then coarsened only as far as coverage requires. Clients should not build their own availability tables; coverage is discovered from the data on every request, and it extends as the store grows.
Two consequences worth internalizing:
  • On a pinned request, resolution_seconds echoes the requested grid, not the density of what filled it. Counting rows is the only density measure.
  • Deep history predates the fine grains: before July 2026 the store’s finest observations are three-hour or twelve-hour points, so a pinned fine bucket over old ranges returns those observations on your grid, sparsely.
For a multi-day window, use bucket_seconds of 300, 1800, 10800, or 43200, or omit it. A 60-second width over several days is both slow and, past the raw window, empty.

Errors and Rate Limits

Request errors return a 400 with a message envelope:
Heavy load answers with 429 and a Retry-After header; retry after the given delay. The IP-based request limits for /v2 routes are listed on the Rate Limits page.

Endpoints

The complete machine-readable contract is published at https://data-api.polymarket.com/v2/openapi.json, with an interactive explorer at https://data-api.polymarket.com/v2/docs.

Next Steps

  • Coming from the v1 Data API routes? See Migrating to Data API v2.
  • Browse the endpoint pages in the sidebar for full parameter and response documentation.