Trade History
Every fill on Polymarket's CTF Exchange, indexed straight from the Polygon blockchain: on-chain data, not API-reported trades. February 2024 onward, more than 1.2 billion order fills.
What a row is
Each on-chain match emits one OrderFilled event per order, and we keep every one. A trade between two orders therefore appears twice, once from each order's side. Count rows as order fills, not as distinct trades.
| Column | Meaning |
|---|---|
| trade_id | Unique id: the transaction hash and log index, joined by a hyphen. |
| market_id | The outcome TOKEN that traded, a long decimal string. It is not the numeric markets.id you request with. |
| timestamp | Polygon block time, UTC, as an ISO-8601 string. |
| block_number | Polygon block the fill landed in. |
| transaction_hash | Transaction hash. |
| log_index | Position of the event within the transaction. |
| order_hash | Hash of the order this fill belongs to. |
| side | The maker's side: buy or sell. |
| price | Price per share, 0 to 1. For a token, this is the probability it implies. |
| size | Collateral value of the fill. Shares = size / price. |
| maker | Maker wallet address, lower-case. |
| taker | Taker wallet address, lower-case. |
| fee | Fee, in collateral. |
| collateral | pUSD from 2026-04-28 (Polymarket's V2 exchange), USDC before. |
Two tokens per market
A binary market trades as two tokens, for example Yes and No, or Up and Down. A row names the token only by its id in market_id, and a downloaded file has no outcome column. The two tokens price each other: if Yes trades at 0.80, No trades near 0.20. Averaging prices across both tokens therefore drifts toward 0.5 whatever the market thinks.
To attribute a row, take the market's outcome_token_ids from GET /v1/markets/{id}. They come in the same order as outcomes and outcome_prices, so index 0 is the first outcome. Then either keep one token's rows, or convert the other token's prices to the same scale as below.
import pandas as pd
import requests
BASE = "https://api.supagamma.com/v1"
H = {"X-API-Key": "sg_your_key_here"}
market = requests.get(f"{BASE}/markets/1254468", headers=H).json()
yes_token = market["outcome_token_ids"][0] # same order as market["outcomes"]
trades = pd.read_parquet("trades_1254468.parquet")
# Put every row on the YES scale: a NO-token price p is 1 - p in YES terms.
is_yes = trades["market_id"] == yes_token
trades["yes_price"] = trades["price"].where(is_yes, 1 - trades["price"])
vwap = (trades["yes_price"] * trades["size"]).sum() / trades["size"].sum()Pulling it
A market is addressed by its numeric markets.id, such as 1254468. A hex condition id is not accepted. An outcome token id is accepted where it narrows a query to that one outcome, on /v1/trades and /v1/download/trades.
| Endpoint | Returns | Note |
|---|---|---|
| GET /v1/trades | Up to 24 hours per call, newest first, as JSON. | Each row also carries outcome and outcome_label, so you can tell YES from NO without a join. |
| GET /v1/download/trades | A CSV, Parquet or JSON file for a market and window. | Both outcome tokens arrive in one file, identified only by market_id. |
| POST /v1/exports | The same file, built in the background for large or wide windows. | Submit, poll, then fetch a signed link. Use this for anything that could outrun a request timeout. |
Freshness and access
- The archive is appended once a day at 01:00 UTC, so it trails the chain by up to about 25 hours. A market that ended today may not have its trades yet.
- Trade history is included with the Professional plan, within its 50 GB monthly fair-use allowance. The Free plan covers delayed daily candles and top-of-book. See pricing.
- Parameters, limits and errors are in the API reference.