Documentation Index
Fetch the complete documentation index at: https://pmxt.dev/docs/llms.txt
Use this file to discover all available pages before exploring further.
The /v0/sql endpoint exposes a read-only SQL interface over PMXT’s
historical orderbook data. Currently this covers Polymarket tick data
(BBO updates, trades, and full book snapshots) stored in ClickHouse.
SQL access requires an Enterprise plan.
Connecting IDs
PMXT markets carry two fields that map directly to the orderbook history:
| PMXT field | SQL column | What it is |
|---|
market.contractAddress | market | Polymarket condition ID (hex) |
outcome.outcomeId | JSON: asset_id | Polymarket CLOB token ID |
Grab them from any PMXT market response:
import pmxt
poly = pmxt.Polymarket(pmxt_api_key="pmxt_live_...")
markets = poly.fetch_markets(query="bitcoin", limit=1)
m = markets[0]
condition_id = m.contract_address # "0xa0f4c492..."
yes_token = m.yes.outcome_id # "1391568931..."
curl -s "https://api.pmxt.dev/api/polymarket/fetchMarkets" \
-H "Authorization: Bearer pmxt_live_..." \
-H "Content-Type: application/json" \
-d '{"args": [{"query": "bitcoin", "limit": 1}]}'
In the response, read data[0].contractAddress and
data[0].yes.outcomeId.
Table schema
polymarket_orderbook_rust
├── timestamp DateTime64(3) — event timestamp
├── timestamp_received DateTime64(3) — ingestion timestamp
├── market String — condition ID (primary key)
├── event_type String — "price_change" | "book" | "last_trade_price"
└── data String (JSON) — full event payload
Always filter by market — it is the primary key. Queries without a
market filter will time out.
1. Candlestick (OHLCV)
Build price candles at any interval from price_change events. Filter by
asset_id to get a specific outcome (Yes or No).
SELECT
toStartOfHour(timestamp) AS hour,
argMin(toFloat64OrZero(JSONExtractString(data, 'best_bid')), timestamp) AS open,
argMax(toFloat64OrZero(JSONExtractString(data, 'best_bid')), timestamp) AS close,
min(toFloat64OrZero(JSONExtractString(data, 'best_bid'))) AS low,
max(toFloat64OrZero(JSONExtractString(data, 'best_bid'))) AS high,
count() AS ticks
FROM polymarket_orderbook_rust
WHERE market = '{contractAddress}'
AND event_type = 'price_change'
AND JSONExtractString(data, 'asset_id') = '{outcomeId}'
AND timestamp >= now() - INTERVAL 7 DAY
GROUP BY hour
ORDER BY hour
Change toStartOfHour to toStartOfMinute, toStartOfFiveMinutes,
or toStartOfDay for different granularities.
import requests
resp = requests.post(
"https://api.pmxt.dev/v0/sql",
headers={"Authorization": "Bearer pmxt_live_..."},
json={"query": f"""
SELECT
toStartOfHour(timestamp) AS hour,
argMin(toFloat64OrZero(JSONExtractString(data, 'best_bid')), timestamp) AS open,
argMax(toFloat64OrZero(JSONExtractString(data, 'best_bid')), timestamp) AS close,
min(toFloat64OrZero(JSONExtractString(data, 'best_bid'))) AS low,
max(toFloat64OrZero(JSONExtractString(data, 'best_bid'))) AS high,
count() AS ticks
FROM polymarket_orderbook_rust
WHERE market = '{condition_id}'
AND event_type = 'price_change'
AND JSONExtractString(data, 'asset_id') = '{yes_token}'
AND timestamp >= now() - INTERVAL 7 DAY
GROUP BY hour ORDER BY hour
"""},
)
candles = resp.json()["data"]
curl "https://api.pmxt.dev/v0/sql" \
-H "Authorization: Bearer pmxt_live_..." \
-H "Content-Type: application/json" \
-d '{"query": "SELECT toStartOfHour(timestamp) AS hour, argMin(...) AS open, argMax(...) AS close, min(...) AS low, max(...) AS high, count() AS ticks FROM polymarket_orderbook_rust WHERE market = '\''YOUR_CONDITION_ID'\'' AND event_type = '\''price_change'\'' AND JSONExtractString(data, '\''asset_id'\'') = '\''YOUR_OUTCOME_ID'\'' AND timestamp >= now() - INTERVAL 7 DAY GROUP BY hour ORDER BY hour"}'
2. Historical orderbook snapshots
Full book snapshots are stored as book events. Each contains the
complete bids and asks arrays at that point in time.
SELECT
timestamp,
JSONExtractString(data, 'asset_id') AS token,
JSONExtractRaw(data, 'bids') AS bids,
JSONExtractRaw(data, 'asks') AS asks
FROM polymarket_orderbook_rust
WHERE market = '{contractAddress}'
AND event_type = 'book'
AND JSONExtractString(data, 'asset_id') = '{outcomeId}'
ORDER BY timestamp DESC
LIMIT 10
Each level in bids / asks is a [price, size] pair.
To get just the depth count over time:
SELECT
timestamp,
JSONLength(JSONExtractRaw(data, 'bids')) AS bid_levels,
JSONLength(JSONExtractRaw(data, 'asks')) AS ask_levels
FROM polymarket_orderbook_rust
WHERE market = '{contractAddress}'
AND event_type = 'book'
ORDER BY timestamp DESC
LIMIT 50
Response shape
Every query returns:
{
"data": [
{ "hour": "2026-04-24 17:00:00", "open": 0.37, "close": 0.39, "low": 0.34, "high": 0.39, "ticks": 3788 },
{ "hour": "2026-04-24 18:00:00", "open": 0.39, "close": 0.36, "low": 0.33, "high": 0.40, "ticks": 4386 },
{ "hour": "2026-04-24 19:00:00", "open": 0.36, "close": 0.39, "low": 0.33, "high": 0.40, "ticks": 4776 }
],
"meta": {
"columns": [
{ "name": "hour", "type": "DateTime" },
{ "name": "open", "type": "Float64" },
{ "name": "close", "type": "Float64" },
{ "name": "low", "type": "Float64" },
{ "name": "high", "type": "Float64" },
{ "name": "ticks", "type": "UInt64" }
],
"rows": 168,
"statistics": { "elapsed": 0.44, "rows_read": 18192766 }
}
}
Limits
- 5-second query timeout — always scope queries to a single market.
- 10,000 row cap — use
LIMIT or time filters to stay under.
- Read-only — only
SELECT, WITH, SHOW, DESCRIBE, and EXPLAIN.