One resolution, stored once
Only 1-minute bars are persisted. 5m, 35m, 4h and 1d are computed at read time, so there is never a second copy of the same truth to drift.
Any timeframe
Standard intervals plus any whole number of minutes. Buckets are anchored to the Unix epoch, so the same window always returns the same bars.
Reference, not payload
Large backtests receive presigned Parquet object references and read straight from R2. Gigabytes never stream through an API process.
Markets kept separate
Binance BTCUSDT and Bybit BTCUSDT are different markets and are never merged. An ETF is never returned as the index it tracks.
Authentication
Every data endpoint requires a credential. The dataset is not anonymously downloadable.
curl https://histprice.com/v1/coverage/BTCUSDT \
-H "Authorization: Bearer $HISTPRICE_API_KEY"
Cloudflare Access is also accepted, and is preferred for interactive users.
Endpoints
GET/v1/coverage/{symbol}
What history exists, per provider and venue.
{
"symbol": "BTCUSDT",
"providers": {
"binance:spot": { "from": "2017-08-17T04:00:00Z", "to": "...", "resolution": "1m" },
"bybit:linear": { "from": "2020-03-15T00:00:00Z", "to": "...", "resolution": "1m" }
}
}
GET/v1/history/{symbol}
| parameter | meaning |
|---|---|
provider | binance, bybit, ... |
venue | spot, linear, um, ... |
from, to | ISO 8601, a date, or now-30d |
timeframe | 1m, 35m, 4h, 1d, or any integer minutes |
format | parquet (default), arrow, csv, json |
curl "https://histprice.com/v1/history/BTCUSDT\
?provider=binance&from=2024-01-01&to=2025-01-01&timeframe=35m&format=parquet" \
-H "Authorization: Bearer $HISTPRICE_API_KEY" -o btc-35m.parquet
POST/v1/history/query
Several instruments in one request, for cross-asset and portfolio work.
{
"symbols": ["BTCUSDT", "ETHUSDT", "SOLUSDT"],
"provider": "binance",
"from": "2022-01-01",
"to": "2026-01-01",
"timeframe": "35m",
"format": "arrow"
}
POST/v1/dataset
The right call for a large backtest. Returns presigned R2 object references so the client reads Parquet directly from Cloudflare.
{
"dataset_id": "ds_a1b2c3...",
"total_objects": 3,
"total_rows": 15778800,
"objects": [
{ "key": "histprice/candles/crypto/binance/spot/BTCUSDT/years=2017-2026/part-000.parquet",
"bytes": 231000000, "rows": 4400000, "sha256": "...", "url": "https://...", }
],
"timestamp_semantics": { "meaning": "candle OPEN time", "unit": "epoch ms, UTC" }
}
timestamp is the candle's
open time in UTC epoch milliseconds. A bar stamped 10:35 covers
[10:35, 10:35 + interval) and is only knowable at its close. Acting on its closing
price at 10:35 is look-ahead bias.
Timeframe aggregation
Applied identically by the API, the SDK and any client following the published rule:
open = first(open) by timestamp
high = max(high)
low = min(low)
close = last(close) by timestamp
volume = sum(volume)
bucket = floor(timestamp / interval_ms) * interval_ms # anchored to the Unix epoch
Epoch anchoring is what makes results reproducible: a 35-minute request starting in March and one starting in June produce identical bars for the days they share.
Python SDK
from histprice import HistPrice
hp = HistPrice()
df = hp.history("BTCUSDT", provider="binance",
start="2022-01-01", end="2026-01-01", timeframe="35m")
df.to_pandas() # or .to_arrow()
Returns a Polars DataFrame. The SDK reads Parquet from a local cache and never issues an HTTP request for data it already holds.
Coverage
| asset class | source | status |
|---|---|---|
| Crypto | Binance, Bybit | 1-minute spot and perpetual history |
| Equities | licensed vendor | requires a configured provider |
| Indices | licensed vendor | requires a configured provider |
| Options | Binance | sparse; availability varies by contract |
PROVIDER_NOT_CONFIGURED and name the
exact credential needed. No data is ever fabricated, and an ETF is never substituted
for the index it tracks.
Response formats
| format | use |
|---|---|
parquet | default; smallest and fastest to re-read |
arrow | fastest machine-to-machine; zero-copy into Polars or pandas |
csv | compatibility only; generated on demand, never stored |
json | small results only; refused above one million rows |