Documentation -- download OHLCV Parquet via Python & API
Quick Start
MarketParquet serves daily-partitioned OHLCV data as Apache Parquet files. Each file contains all symbols for a single trading day.
- No account? Grab the newest daily file (
curl -LO https://marketparquet.com/api/data/download/stock_daily/latest.parquet) or the intraday sample pack (zip) - Sign up for a free account
- Browse available data by asset type and timeframe
- Download the parquet files you need (web or API)
- Load into pandas, polars, DuckDB, or any tool that reads Parquet
File Format
Files are named by trading date and stored under by_date/{asset}_{timeframe}/YYYY/YYYY-MM-DD.parquet.
Intraday schema (1min, 5min, 30min, 1hour)
timestamp TIMESTAMP[us] -- bar open time (US/Eastern) symbol STRING -- ticker symbol asset_type STRING -- "Stock", "ETF", or contract code open FLOAT64 high FLOAT64 low FLOAT64 close FLOAT64 volume FLOAT64
Daily (EOD) schema
date DATE -- trading date symbol STRING -- ticker symbol asset_type STRING open FLOAT64 high FLOAT64 low FLOAT64 close FLOAT64 volume FLOAT64
Note: daily files dated before 2026-03-27 carry the bar time in a
timestamp TIMESTAMP[us] column instead of date.
When reading a range that spans the boundary, use
read_parquet(..., union_by_name=true) and
COALESCE(date, CAST(timestamp AS DATE)) (DuckDB), or
normalize per file in pandas/polars.
Stock and ETF prices are split-adjusted (dividends are not embedded in the price series -- see methodology). Futures use continuous ratio adjustment. Compression: Snappy.
Python
pandas
import pandas as pd
# Load one day of stock 1-min bars
df = pd.read_parquet("stock_1min_2024-01-15.parquet")
# Filter to a single ticker
spy = df[df.symbol == "SPY"]
# Compute VWAP
spy["vwap"] = (spy.close * spy.volume).cumsum() / spy.volume.cumsum()
polars (faster, recommended)
import polars as pl
df = pl.read_parquet("stock_daily_2024-01-15.parquet")
# Top 10 by volume
top = df.sort("volume", descending=True).head(10)
# Lazy load multiple files
df = pl.scan_parquet("stock_daily_2024-*.parquet")
monthly_avg = df.group_by("symbol").agg(pl.col("close").mean()).collect()
DuckDB (SQL on Parquet)
import duckdb
con = duckdb.connect()
# Query parquet files directly with SQL
result = con.execute("""
SELECT symbol, AVG(close) AS avg_close, SUM(volume) AS total_vol
FROM 'stock_daily_*.parquet'
WHERE symbol IN ('AAPL', 'MSFT', 'GOOGL')
GROUP BY symbol
""").fetchdf()
REST API
The API is available on the free sample -- recent daily data (the anonymous
intraday sample pack is a plain zip, no key needed). A one-time
Complete Archive purchase unlocks full history, including intraday.
Generate an API key from your account page, then
authenticate with the Authorization header.
Rate limits: 600 requests/min per IP at the edge and per
API key on paid plans; 60 requests/min per key on the free sample. Over the
limit you get 429 with a Retry-After header
(seconds). A manifest call counts as one request however many files it
returns, so bulk downloads should use the manifest, not one
/download call per file.
Machine-readable: GET /api/v1/ (no key needed) returns a
JSON map of every endpoint, the auth header and the asset-type names, and
/api/v1/openapi.json is the OpenAPI 3
document -- point your coding agent at either one instead of letting it guess URLs.
The header is exactly Authorization: Bearer, one space, then the key;
a 401 means it was pasted wrong.
List available assets
GET https://marketparquet.com/api/v1/assets
curl -H "Authorization: Bearer bt_YOUR_KEY" \
https://marketparquet.com/api/v1/assets
# Returns:
{
"plan": "full",
"assets": [
{
"asset_type": "stock_1min",
"asset": "stock",
"timeframe": "1min",
"file_count": 6690,
"earliest_date": "2000-01-03",
"latest_date": "2026-04-06",
"access": "full"
},
...
]
}
List dates for an asset
GET /api/v1/dates/{asset_type}?page=1
curl -H "Authorization: Bearer bt_YOUR_KEY" \
https://marketparquet.com/api/v1/dates/stock_daily
# Returns up to 100 dates per page (newest first)
{
"asset_type": "stock_daily",
"plan": "full",
"page": 1,
"count": 100,
"dates": [
{
"date": "2026-04-06",
"file_size_bytes": 232488,
"download_url": "/api/v1/download/stock_daily/2026-04-06"
},
...
]
}
Download a file
GET /api/v1/download/{asset_type}/{date}
curl -H "Authorization: Bearer bt_YOUR_KEY" \
https://marketparquet.com/api/v1/download/stock_daily/2026-04-06
# Returns a presigned R2 URL (valid 60 seconds)
{
"asset_type": "stock_daily",
"date": "2026-04-06",
"file_size_bytes": 232488,
"download_url": "https://...r2.cloudflarestorage.com/...?X-Amz-Signature=...",
"expires_in": 60
}
Bulk: manifest for a date range
GET /api/v1/manifest/{asset_type}?start=YYYY-MM-DD&end=YYYY-MM-DD
curl -H "Authorization: Bearer bt_YOUR_KEY" \
"https://marketparquet.com/api/v1/manifest/stock_daily?start=2024-01-01&end=2024-12-31"
# One call per date range instead of one per file. URLs valid 60 minutes.
# Up to 400 files per call (a calendar year of any timeframe fits) -- walk
# longer ranges year by year. Each file is logged as a download.
{
"asset_type": "stock_daily",
"start": "2024-01-01",
"end": "2024-12-31",
"file_count": 252,
"total_bytes": 58712345,
"date_range": ["2024-01-02", "2024-12-31"],
"expires_in": 3600,
"files": [
{
"date": "2024-01-02",
"filename": "stock_daily_2024-01-02.parquet",
"file_size_bytes": 232488,
"download_url": "https://...r2.cloudflarestorage.com/...?X-Amz-Signature=..."
},
...
]
}
Asset types
stock_1min stock_5min stock_30min stock_1hour stock_daily etf_1min etf_5min etf_30min etf_1hour etf_daily futures_1min futures_5min futures_30min futures_1hour futures_daily
Examples
Python: download a file via the API
import requests
import pandas as pd
API_KEY = "bt_YOUR_KEY"
BASE = "https://marketparquet.com/api/v1"
headers = {"Authorization": f"Bearer {API_KEY}"}
# 1. Get a presigned URL
resp = requests.get(
f"{BASE}/download/stock_daily/2026-04-06",
headers=headers,
).json()
# 2. Download the parquet file
parquet_bytes = requests.get(resp["download_url"]).content
# 3. Load with pandas
import io
df = pd.read_parquet(io.BytesIO(parquet_bytes))
print(df.head())
Python: download a whole year via the manifest
import pathlib
import requests
H = {"Authorization": "Bearer bt_YOUR_KEY"}
m = requests.get("https://marketparquet.com/api/v1/manifest/stock_daily",
params={"start": "2024-01-01", "end": "2024-12-31"}, headers=H).json()
for f in m["files"]:
pathlib.Path(f["filename"]).write_bytes(requests.get(f["download_url"]).content)
# Full history: repeat per year (2000..today). Wrap the loop in a
# ThreadPoolExecutor to fetch several files at once.
Bash: download every daily stock file (needs curl + jq)
for y in $(seq 2000 2026); do
curl -s -H "Authorization: Bearer bt_YOUR_KEY" "https://marketparquet.com/api/v1/manifest/stock_daily?start=$y-01-01&end=$y-12-31" \
| jq -r '(.files // [])[] | "\(.download_url) -o \(.filename)"' | xargs -P8 -n3 curl -s --retry 3
done
# Swap stock_daily for any asset type; 8 parallel downloads is a good default.
Python: backtest a simple strategy
import pandas as pd
import glob
# Load all daily files for 2024
files = sorted(glob.glob("stock_daily_2024-*.parquet"))
df = pd.concat([pd.read_parquet(f) for f in files])
# SPY-only momentum strategy
spy = df[df.symbol == "SPY"].sort_values("date").reset_index(drop=True)
spy["return"] = spy.close.pct_change()
spy["sma20"] = spy.close.rolling(20).mean()
spy["signal"] = (spy.close > spy.sma20).astype(int)
spy["strategy_return"] = spy.signal.shift(1) * spy["return"]
cumret = (1 + spy.strategy_return).cumprod()
print(f"Final value: {cumret.iloc[-1]:.2f}")
Need more help?
Check the FAQ for data quality details, or browse the interactive API docs at /docs.