Skip to main content
supagamma
DataPolymarket

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.

ColumnMeaning
trade_idUnique id: the transaction hash and log index, joined by a hyphen.
market_idThe outcome TOKEN that traded, a long decimal string. It is not the numeric markets.id you request with.
timestampPolygon block time, UTC, as an ISO-8601 string.
block_numberPolygon block the fill landed in.
transaction_hashTransaction hash.
log_indexPosition of the event within the transaction.
order_hashHash of the order this fill belongs to.
sideThe maker's side: buy or sell.
pricePrice per share, 0 to 1. For a token, this is the probability it implies.
sizeCollateral value of the fill. Shares = size / price.
makerMaker wallet address, lower-case.
takerTaker wallet address, lower-case.
feeFee, in collateral.
collateralpUSD 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.

EndpointReturnsNote
GET /v1/tradesUp 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/tradesA CSV, Parquet or JSON file for a market and window.Both outcome tokens arrive in one file, identified only by market_id.
POST /v1/exportsThe 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.