Developers

Market data API

Read-only. Every Polymarket market RELVAL tracks, with live quotes and settlement rules.

Base URL
/api/agent
Auth
None
Format
JSON, NDJSON
Rate limit
120 / min per IP

Quick start

  1. 1

    Load everything once

    All markets with rules, plus all events. Keep the first call's headers.

    curl --compressed -D markets.headers \
      "https://<host>/api/agent/snapshot?status=all&include=rules" > markets.ndjson
    curl --compressed "https://<host>/api/agent/snapshot?type=events" > events.ndjson
  2. 2

    Poll what changed

    Start from the snapshot's x-next-since header, then pass each response's next_since back as since.

    curl --compressed "https://<host>/api/agent/changes?since=<next_since>"
  3. 3

    Reload when metadata moves

    /changes reports price changes only. If its meta_version differs from the loaded one (markets opened, closed or delisted), repeat step 1.

/changes is at-least-once: no row is skipped, but a market can appear on two consecutive pages. De-duplicate by id, keeping the larger changed_at_ms.

Coverage

SegmentWhat is in it
coreEvery open event except Sports, Crypto and Esports.
sportsTop 100 Sports events by 24h volume that end within 7 days.
top_listMarkets on the web board outside the two sets above, such as Crypto.

Multi-outcome and neg-risk events are always complete, closed options included. Options never deployed on-chain have no token and appear by name under undeployed_options. Live counts: /api/agent.

Endpoints

All endpoints are GET.

EndpointReturns
/api/agent/snapshotEverything as NDJSON, one row per line
/api/agent/changesMarkets whose price changed since a time
/api/agent/marketsSearch, filter, sort and page markets
/api/agent/markets/:idOne market in full, with book levels and rules
/api/agent/markets/:id/historyRecorded mid prices
/api/agent/eventsSearch events
/api/agent/events/:idA whole event with every option
/api/agent/categoriesOpen market and event counts per category
/api/agentMachine-readable version of this page with live coverage

GET/api/agent/snapshot

Rebuilt at most every 15 s. Allowed 6 times per minute per IP.

ParamValuesDefault
typemarkets, eventsmarkets
statusopen, closed, tradable, allopen
includerulesnone
levels1 adds 8 book levels per side0

Response headers: x-market-count (rows), x-generated-at-ms, x-next-since. fields is not supported here; project the rows yourself.

GET/api/agent/changes

Oldest change first. A page never splits markets fetched at the same instant.

ParamValuesDefault
sinceepoch msrequired
limit500 to 50002000
include, levels, fieldssame as /markets

GET/api/agent/markets

Filters combine with AND.

ParamValuesDefault
qWords that must all appear in question, event, option or slug
idsComma list, up to 1000. Unknown ids return in missing
categoryComma list, matches any
segmentcore, sports, top_list
event_idOne event
statusopen, closed, tradable, allopen (all with ids)
min_volume_24hNumber
min_liquidityNumber
end_before, end_afterEpoch ms or ISO 8601
changed_sinceEpoch ms
sortvolume_24h, liquidity, end_date, change_24h, abs_change_24h, spread, changed_at. Prefix - for descending-volume_24h
limit1 to 1000100
cursornext_cursor from the previous page
includerulesnone
levels1 adds book levels0
fieldsComma list of row keys to keepall

GET/api/agent/markets/:id

Adds book levels, rules, description, tick size, min order size, the parent event with its rules, and refresh_tier.

GET/api/agent/markets/:id/history

Only markets on the 10-second tier have history.

ParamValuesDefault
minutes1 to 1440120
sideyes, noyes

GET/api/agent/events

Takes the same q, category, segment, min_volume_24h, end_before and end_after as /markets.

ParamValuesDefault
sortvolume_24h, end_date, market_count. Prefix - for descending-volume_24h
limit, cursorUp to 1000100

GET/api/agent/events/:id

Every option, each with its rules. For neg-risk events the yes mids add up to about 1.

ParamValuesDefault
statusall, open, closed, tradableall
levels, fieldssame as /markets

Market row

One row per market, the same shape in every endpoint.

Identity

FieldTypeMeaning
idstringPolymarket market id
questionstringMarket question
optionstring | nullOption label inside a multi-outcome event
event_id, event_titlestringParent event
segmentstringcore, sports or top_list
neg_riskbooleanOptions of the event are mutually exclusive
categoriesstring[]Published Polymarket categories
outcomes, token_idsstring[]Aligned by index: outcomes[0] trades as token_ids[0]
urlstring | nullEvent page on Polymarket

State and activity

FieldTypeMeaning
statusstringopen, not_accepting or closed
end_date_msnumber | nullScheduled end
volume_24h, liquiditystring | nullAs reported by Polymarket
change_1h, change_24h, change_1wstring | nullAbsolute price change: 0.08 is 8 cents
last_trade_pricestring | nullLast traded price
tradable_nowbooleanOpen, both sides quoted, quotes younger than 30 s

Quotes (yes and no)

FieldTypeMeaning
bid, ask, midstring | nullnull when that side of the book is empty
fetched_at_msnumberWhen the book was read
changed_at_msnumber | nullWhen bid, ask or mid last changed
levelsobjectOnly with levels=1: 8 levels per side, best first

yes or no is null when that book has never been fetched.

Example row
{
  "id": "123456",
  "question": "Will it snow in New York City on Christmas Day?",
  "option": null,
  "event_id": "54321",
  "event_title": "…",
  "segment": "core",
  "neg_risk": false,
  "categories": ["Weather"],
  "outcomes": ["Yes", "No"],
  "token_ids": ["…", "…"],
  "status": "open",
  "end_date_ms": 1798779540000,
  "volume_24h": "48213.5",
  "liquidity": "233290.86",
  "change_1h": null,
  "change_24h": "-0.004",
  "change_1w": "0.01",
  "last_trade_price": "0.037",
  "yes": {
    "bid": "0.036", "ask": "0.038", "mid": "0.037",
    "fetched_at_ms": 1789294072890,
    "changed_at_ms": 1789293876978
  },
  "no": { … },
  "tradable_now": true,
  "url": "https://polymarket.com/event/…"
}

Settlement rules

Returned as rules on detail endpoints, and on list endpoints and snapshots with include=rules.

Market rules

FieldTypeMeaning
descriptionstring | nullFull resolution text
resolution_sourcestring | nullSource named by the market, often empty
uma_resolution_statusstring | nullFor example proposed or resolved
uma_end_date_msnumber | nullEnd of the UMA resolution window
resolved_bystring | nullResolver address
outcome_pricesstring[] | nullSettlement prices once resolved
neg_risk_otherbooleanThe catch-all Other option of a neg-risk event

Event rules

FieldTypeMeaning
descriptionstring | nullEvent-level resolution text
resolution_sourcestring | nullSource named by the event

Conventions

TopicRule
PricesDecimal strings exactly as the order book returned them, never floats
Times*_ms fields are epoch ms. Filters accept epoch ms or ISO 8601
Server timeIn the x-generated-at-ms header, not the body
FreshnessBoard markets and markets recently opened on the website refresh about every 10 s; the rest about once a minute
CompressionSend Accept-Encoding: gzip
CachingSend If-None-Match with the last ETag. An unchanged response is an empty 304
TradingOrders are placed on the website. Check tradable_now first

Limits and errors

LimitValue
Requests120 per minute per IP, bursts of 30
Snapshots6 per minute per IP
q200 characters, 8 distinct words
category, segment20 items
fields40 items
ids1000 items
Repeated parameterRejected with 400
CursorsExpire when market metadata refreshes. Paging by spread or changed_at is not stable
HTTPcodeWhat to do
400BAD_REQUESTFix the parameter named in message
404NOT_FOUNDThe market or event is no longer tracked
409CURSOR_EXPIREDStart again from the first page
429RATE_LIMITEDWait retry-after seconds

Error bodies are { ok: false, code, message }.

Markets