Data Format Reference
Complete reference for all ImpliedData files — column definitions, data types, interpretation guide, and working code examples. Read this before using the data.
Overview
ImpliedData provides historical prediction market data from Polymarket, Manifold, and Myriad in a unified schema. All prices are normalized to win probability (0 to 1) — not dollars, not percentages. All timestamps are UTC. All platforms use the same column names.
There are three types of files you can download:
OHLCV aggregated price data. One row per time bucket per market. Best starting point for most analysis.
Raw tick-level trade history. Every individual transaction. Use when you need exact execution times or microstructure analysis.
Market metadata — titles, categories, resolution outcomes, open/close dates. Join this against candles or trades to get market context.
File Formats
Apache Parquet is a columnar binary format. It is 5–10× smaller than CSV and preserves exact data types (timestamps stay as datetime, floats stay as float64). Python, R, DuckDB, Spark, and most modern data tools read it natively. Always prefer Parquet unless you need to open the file in Excel.
Plain text, comma-separated. Opens in Excel, Google Sheets, Numbers, and any language. Timestamps are ISO 8601 strings ("2024-06-01T14:00:00+00:00"). Floats are written with full precision. Larger file sizes than Parquet (no compression, no columnar storage).
0x1a2b3c stays fine, but a pure numeric ID like 123456789012 may be corrupted). Import via Data → From Text/CSV and set the column type to Text to prevent this.Understanding Prices (0–1)
All price fields (open, high, low, close, price) are win probability expressed as a decimal between 0 and 1, not dollars or percentages.
To convert to percentage: price × 100. To convert to implied odds: 1 / price.
Multi-outcome markets (e.g. “Who wins the 2024 election?” with 10 candidates): each candidate is a separate market_id/outcome row. Prices across all outcomes in an event should sum to approximately 1.0 (the difference is the platform’s margin).
Gaps & Sparse Markets
Candle files do not insert rows for empty periods. If no trades occurred during a 1h bucket, that bucket simply does not exist in the file. This is by design — inserting NULL rows would inflate file sizes by 10–100× for low-volume markets.
Manifold markets are especially sparse — many have only a handful of trades over their entire lifetime. A market open for 6 months may have only 20–30 candles at 1d resolution if trading was infrequent.
How to handle gaps in your analysis:
import pandas as pd
df = pd.read_parquet("implieddata_polymarket_1h.parquet")
# Forward-fill missing hours (last known price)
df = df.set_index("timestamp").sort_index()
full_range = pd.date_range(df.index.min(), df.index.max(), freq="1h", tz="UTC")
df = df.reindex(full_range)
df["close"] = df["close"].ffill() # carry last close forward
df["volume"] = df["volume"].fillna(0) # no trades = zero volumeCandles Schema
OHLCV candle data. Files are named implieddata_{platform}_{resolution}.parquet (e.g. implieddata_polymarket_1h.parquet). One row per time bucket per market. All resolutions (1m, 5m, 15m, 1h, 1d) are available to every signed-in account.
Note: Bulk 1m/5m/15m downloads for Polymarket markets before December 2022 may return no data for markets not yet backfilled into the continuous aggregate. Per-market downloads (selecting a specific market) always fall back to on-demand computation from raw trades and are not affected by this gap.
| Column | Type | Nullable | Description |
|---|---|---|---|
| timestamp | datetime (UTC) | NO | Start of the candle bucket. UTC timezone. 1h candles start at the top of the hour (e.g. 2024-06-01 14:00:00+00). |
| platform | string | NO | Source platform slug: polymarket, manifold, or myriad. |
| market_id | string | NO | Platform-specific market identifier. For Polymarket this is the condition_id (hex string). For Manifold the slug/contract ID. |
| resolution | string | NO | Candle resolution: 1m, 5m, 15m, 1h, or 1d. Matches the file you downloaded. |
| open | float64 [0–1] | NO | Win probability at the start of the candle, derived from the first trade in the bucket. |
| high | float64 [0–1] | NO | Highest win probability traded during the candle period. |
| low | float64 [0–1] | NO | Lowest win probability traded during the candle period. |
| close | float64 [0–1] | NO | Win probability at the end of the candle, derived from the last trade in the bucket. |
| volume | float64 | NO | Total notional traded during the candle period. Currency depends on the platform — see volume_currency. Computed as Σ(price × size) across all trades in the bucket. |
| volume_currency | string | NO | Currency of the volume field: USD for Polymarket, MANA for Manifold. Manifold uses play-money (Mana) — volumes are not directly comparable across platforms. |
| trade_count | int64 | NO | Number of individual trades that occurred during the candle period. |
| market_title | string | YES | Human-readable title of the market (e.g. 'Will Trump win the 2024 election?'). Joined from market metadata. |
| market_open_time | datetime (UTC) | YES | When the market was created / opened for trading. |
| market_close_time | datetime (UTC) | YES | When the market resolved or closed. NULL for markets still open. |
Trades Schema
Raw tick-level trades. Files are named implieddata_{platform}_trades.parquet. One row per individual transaction. Use this for microstructure analysis, custom aggregations, or building your own candle resolution.
| Column | Type | Nullable | Description |
|---|---|---|---|
| trade_id | string | NO | Platform-assigned unique trade identifier. Used for deduplication. |
| timestamp | datetime (UTC) | NO | Exact trade execution time in UTC. Precision varies by platform (ms or µs). |
| platform | string | NO | Source platform: polymarket, manifold, or myriad. |
| market_id | string | NO | Platform-specific market identifier, same as in the candles file. |
| price | float64 [0–1] | NO | Execution price expressed as win probability. A trade at 0.62 means the taker paid 0.62 for a contract settling at 1.00 if YES resolves. |
| size | float64 | NO | Notional size of the trade. USD-denominated for Polymarket (shares × 1.00). For Manifold, this is raw Mana — Manifold uses play-money and sizes are not USD-equivalent. |
| side | string | YES | Trade side: buy or sell from the taker's perspective. NULL when platform does not report this field. |
| outcome | string | YES | Which outcome token was traded (YES, NO, or candidate name for multi-outcome markets). |
Multi-Outcome Markets
Most markets are binary (Yes/No) — one number matters: P(YES), the close value in the candle schema above. But some markets have several mutually-exclusive outcomes — a football match (1 / X / 2), a multi-candidate election, a “which option wins” market. These ship as a separate dataset with one extra column, outcome — they are never mixed into the clean binary 0–1 files.
The grain becomes (market_id, outcome, timestamp) — one row per outcome per time bucket — and close is now the implied probability of that specific outcome. So for “Arsenal vs Chelsea” you get three rows at each timestamp:
| timestamp | market_id | outcome | close | meaning |
|---|---|---|---|---|
| 14:00 | 123 | 1 | 0.55 | P(Arsenal win) = 55% |
| 14:00 | 123 | X | 0.27 | P(draw) = 27% |
| 14:00 | 123 | 2 | 0.18 | P(Chelsea win) = 18% |
Every other column is identical to the binary candle schema (open/high/low/close/volume/trade_count). What to know:
- One outcome’s price path: filter
market_id = 123 AND outcome = '1'. - The whole market: filter
market_id = 123→ every outcome at every timestamp. - Prices ≈ sum to 1 across a market’s outcomes (they’re mutually exclusive) — but not exactly: prices are sampled from an order book with a spread, so the sum drifts a few percent around 1.0. We report observed prices and do not renormalize (divide by the row-sum yourself if you want a clean distribution).
- Outcome labels are the raw traded labels — for Azuro football, betting convention:
1= home win,X= draw,2= away win. Joinmarket_id→ the markets file (outcomescolumn) for the full human meaning. - At resolution the winning outcome converges to ~1, the losers to ~0 — same convergence guarantee as the binary set.
Raw trades for these markets follow the same rule: the trades schema gains the same outcome column, with price = that outcome’s probability at the fill. Coverage at launch: Azuro (sports, 2023–2025 historical archive), multi-candidate Polymarket, and multi-option Myriad.
Markets Schema
Market metadata catalog. Files named implieddata_{platform}_markets.parquet. Join against candles or trades on platform_market_id = market_id.
| Column | Type | Nullable | Description |
|---|---|---|---|
| id | int64 | NO | ImpliedData internal market ID. Stable across updates. |
| platform_market_id | string | NO | Platform-assigned market identifier. Use this to join against candle/trade files. |
| platform | string | NO | Source platform slug. |
| title | string | NO | Full market question as listed on the platform. |
| category | string | YES | Topic category (politics, sports, crypto, science, …). Normalized across platforms. |
| status | string | NO | Market status: open, resolved, or closed. Resolved markets have a resolution_result. |
| resolution_result | string | YES | Resolution outcome: YES, NO, or a candidate name for multi-outcome markets. NULL if not yet resolved. |
| open_time | datetime (UTC) | YES | Market creation timestamp. |
| close_time | datetime (UTC) | YES | Market resolution timestamp. NULL for open markets. |
| trade_count | int64 | NO | Total number of trades ever recorded for this market in ImpliedData. |
neg_risk flag, Manifold’s resolution_probability. Parse with json.loads(row["raw_metadata"]).Code Examples
Load candles with pandas (Python)
import pandas as pd
# Load 1h candles for Polymarket
df = pd.read_parquet("implieddata_polymarket_1h.parquet")
# Basic info
print(df.dtypes)
print(df.head())
# Filter to a single market
market = df[df["market_id"] == "0x1234abcd..."]
# Plot the close probability over time
import matplotlib.pyplot as plt
market.set_index("timestamp")["close"].plot(title=market["market_title"].iloc[0])Load with polars (Python) — faster for large files
import polars as pl
df = pl.read_parquet("implieddata_polymarket_1h.parquet")
# Filter markets that resolved YES and had high volume
winners = (
df.filter(pl.col("resolution_result") == "YES")
.group_by("market_id")
.agg([
pl.col("close").last().alias("final_price"),
pl.col("volume").sum().alias("total_volume"),
pl.col("market_title").first(),
])
.filter(pl.col("total_volume") > 10_000)
.sort("total_volume", descending=True)
)
print(winners.head(20))Query with DuckDB — SQL on Parquet without loading into memory
-- Install: pip install duckdb
-- Then in Python:
import duckdb
# Query directly from file — DuckDB reads only the columns/rows it needs
result = duckdb.query("""
SELECT
market_id,
market_title,
MIN(low) AS min_prob,
MAX(high) AS max_prob,
LAST(close ORDER BY timestamp) AS final_price,
SUM(volume) AS total_volume_usd
FROM 'implieddata_polymarket_1h.parquet'
WHERE market_title ILIKE '%election%'
AND timestamp >= '2024-01-01'
GROUP BY market_id, market_title
ORDER BY total_volume_usd DESC
LIMIT 20
""").df()
print(result)Load in R
# install.packages("arrow")
library(arrow)
library(dplyr)
df <- read_parquet("implieddata_polymarket_1h.parquet")
# Summary by market
summary <- df |>
group_by(market_id, market_title) |>
summarise(
first_price = first(open),
last_price = last(close),
total_volume = sum(volume),
n_candles = n()
) |>
arrange(desc(total_volume))
head(summary, 20)Join candles with market metadata
import pandas as pd
candles = pd.read_parquet("implieddata_polymarket_1h.parquet")
markets = pd.read_parquet("implieddata_polymarket_markets.parquet")
# Join on the platform market identifier
merged = candles.merge(
markets[["platform_market_id", "title", "category", "resolution_result"]],
left_on="market_id",
right_on="platform_market_id",
how="left"
)
# Filter to politics markets only
politics = merged[merged["category"] == "politics"]Convert to percentage / implied odds
import pandas as pd
df = pd.read_parquet("implieddata_polymarket_1h.parquet")
# Prices are 0–1 probabilities. Convert:
df["close_pct"] = df["close"] * 100 # e.g. 0.62 → 62.0%
df["close_implied_odds"]= 1 / df["close"] # e.g. 0.62 → 1.61x
df["close_decimal_odds"]= 1 / df["close"] # same as implied odds for binary markets
# For a YES/NO market, the NO probability is:
df["no_prob"] = 1 - df["close"]API Reference
Programmatic access is free and open to every signed-in account. Generate a key in your account (shown once — store it securely), then authenticate every request with a Bearer header. API downloads count towards the same fair-use export ceiling as website downloads.
| Endpoint | Description | Rate limit |
|---|---|---|
| GET /v1/markets | Market catalog — filter by platform, category, status, search | 120 / min |
| GET /v1/candles | OHLCV candles as JSON (max 10,000 rows per call) | 60 / min |
| GET /v1/download | Bulk Parquet/CSV export (counts against monthly volume) | 20 / 10 min |
| GET /v1/outcome-candles | Per-outcome candles for multi-outcome markets, incl. the Azuro archive — market_id required | 60 / min |
| GET /v1/outcome-download | Parquet/CSV export of multi-outcome / Azuro candles — market_id required | 20 / 10 min |
| GET /v1/event-links | Confirmed cross-platform links for a market, with polarity | 120 / min |
| GET /v1/event-clusters | One event across every platform, aligned to a canonical YES | 120 / min |
| GET /v1/onchain-trades | Raw tick-level Polymarket fills — market_id required | 20 / 10 min |
| GET /v1/usage | Your current month's row usage and remaining volume | 60 / min |
Quick start (curl)
# Catalog: newest open Polymarket politics markets
curl -H "Authorization: Bearer idk_live_YOUR_KEY" \
"https://api.implieddata.com/v1/markets?platform=polymarket&category=politics&status=open&limit=50"
# Candles: 1-minute resolution for one market
curl -H "Authorization: Bearer idk_live_YOUR_KEY" \
"https://api.implieddata.com/v1/candles?platform=polymarket&market_id=12345&resolution=1m&start=2026-05-01&end=2026-06-01"
# Bulk export: all Manifold 1h candles for May as Parquet
curl -OJ -H "Authorization: Bearer idk_live_YOUR_KEY" \
"https://api.implieddata.com/v1/download?platform=manifold&resolution=1h&start=2026-05-01&end=2026-06-01&fmt=parquet"
# Multi-outcome (e.g. the Azuro sports archive): per-outcome candles for one market
curl -OJ -H "Authorization: Bearer idk_live_YOUR_KEY" \
"https://api.implieddata.com/v1/outcome-download?market_id=49021136&resolution=1h&fmt=csv"
# Check your remaining monthly volume
curl -H "Authorization: Bearer idk_live_YOUR_KEY" "https://api.implieddata.com/v1/usage"Python
import io, requests, pandas as pd
KEY = "idk_live_YOUR_KEY"
H = {"Authorization": f"Bearer {KEY}"}
BASE = "https://api.implieddata.com"
# Download straight into a DataFrame
r = requests.get(f"{BASE}/v1/download", headers=H, params={
"platform": "polymarket", "resolution": "1h",
"start": "2026-01-01", "end": "2026-06-01", "fmt": "parquet",
})
r.raise_for_status()
df = pd.read_parquet(io.BytesIO(r.content))
print(df.head())Errors: 401 missing/invalid key · 403 fair-use export limit exceeded · 429 rate limit — retry after the Retry-After header · 404 no data for the given filters. Keys can be revoked any time from your account page.
Common Gotchas
Should not happen in cleaned data. If you see them, they are anomalies from erroneous platform data that we have flagged but not deleted. Filter with df[df['close'].between(0, 1)] to exclude them.
An election market with 10 candidates generates 10× the rows of a binary market. Each candidate is a separate market_id. When computing platform-level volume, be aware that an event's total volume is the sum across all its outcome market_ids.
Manifold uses play-money (Mana), not real money. The volume and size columns for Manifold files contain raw Mana amounts — they are NOT converted to USD. The volume_currency column tells you the currency: 'USD' for Polymarket, 'MANA' for Manifold. Filter with df[df['volume_currency'] == 'USD'] if you want real-money platforms only. Price/probability columns (open, high, low, close) ARE unified across all platforms — only volume differs.
The 2024 US Presidential Election is on both platforms as separate market_ids. There is no cross-platform event ID — you need to match by title substring or by manually mapping condition_ids.
A 1d candle timestamped 2024-11-05 00:00:00+00 covers trades from midnight to midnight UTC. US market events (debates, election results) often occur in the US evening = UTC next day, so a result announced at 11pm ET lands in the next day's 1d candle.
In CSV files, timestamp is written as an ISO 8601 string: 2024-06-01T14:00:00+00:00. Always parse with timezone awareness: pd.read_csv(..., parse_dates=['timestamp']). With Parquet, timestamps are already datetime64[ns, UTC] — no parsing needed.
The resolution column in candle files repeats the resolution label on every row (e.g. '1h'). This is useful when you concatenate files from multiple resolutions into one DataFrame — it lets you filter without checking filename.