Bars
A bar is one OHLCV record: open, high, low, close, volume, VWAP and trade count over one
interval. Each source stores its finest timeframe (/api/v1/timeframes?source=stocks lists it);
any coarser timeframe is built from it when you ask, in UTC-aligned buckets.
curl -sD - "https://getmarkethistory.com/query/v1/bars/stocks/SPY/1d?start=2024-01-02&end=2024-01-31&adjust=all" \ -H "Authorization: Bearer $MARKET_HISTORY_KEY"from markethistory import Client
mh = Client() # reads MARKET_HISTORY_KEY
df = mh.frame("stocks", "SPY", "1d", start="2024-01-02", end="2024-01-31", adjust="all")df[["open", "high", "low", "close", "volume"]].head()import jsonimport os
import requests
url = "https://getmarkethistory.com/query/v1/bars/stocks/SPY/1d"params = {"start": "2024-01-02", "end": "2024-01-31", "adjust": "all"}headers = {"Authorization": f"Bearer {os.environ['MARKET_HISTORY_KEY']}"}
rows = []while True: r = requests.get(url, params=params, headers=headers, timeout=60) r.raise_for_status() # see /docs/errors for what each status means rows += [json.loads(line) for line in r.text.splitlines() if line] print(r.headers["X-Row-Count"], "rows,", r.headers["X-Bars-Charged"], "bars") if "X-Next-Cursor" not in r.headers: break params["cursor"] = r.headers["X-Next-Cursor"]HTTP/2 200content-type: application/x-ndjsonx-row-count: 25x-bars-charged: 100x-query-ms: 38
{"close":458.21,"gap_fill_method":0,"high":462.37,"is_gap":false,"low":455.87,"open":461.53,"symbol":"SPY","symbol_id":1529,"timestamp":"2024-01-02T00:00:00Z","trade_count":701844,"volume":123623727.6,"vwap":458.996}{"close":454.24,"gap_fill_method":0,"high":457.31,"is_gap":false,"low":453.78,"open":457.92,"symbol":"SPY","symbol_id":1529,"timestamp":"2024-01-03T00:00:00Z","trade_count":690121,"volume":103585820.9,"vwap":455.402}Shape and headers as served; the values are illustrative, not copied from a response.
GET /query/v1/bars/{source}/{ticker}/{timeframe}, with a key.
| Parameter | Meaning |
|---|---|
source |
The dataset (stocks, fx, crypto, …). /api/v1/sources lists them. |
ticker |
The symbol, as /api/v1/symbols lists it. |
timeframe |
1s, 1m, 5m, 1h, 1d, … The stored one or any coarser one. |
start, end |
Inclusive range (RFC 3339 or a bare date; a bare end is that whole UTC day). |
adjust |
raw (default), splits or all (splits and cash dividends). stocks only. |
columns |
Only these columns, comma-separated. |
limit |
Rows per page: default 1,000, maximum 100,000. |
cursor |
X-Next-Cursor from the previous page. |
format |
jsonl (default), json, csv, parquet, arrow or mbt1. Every one costs the same. |
Pages run oldest first. Follow X-Next-Cursor (or the Link header) until it is absent. The
client’s frame() and table() do it for you and fetch Parquet, so a long range arrives as
typed columns (install it with the pandas and arrow
extras).
adjust= rewrites open, high, low, close, vwap and volume from the published
corporate actions; the stored bars stay raw. Keep the
raw bars when you need to reproduce a backtest exactly: an adjusted history changes each time a
new split or dividend lands.
Free sample
Section titled “Free sample”Hourly bars of any ticker over the last five years, with no key. With no dates you get the newest
100; with start, end or cursor it pages like the billed path, at most 5,000 bars a page.
Other timeframes and adjust= are a 400 there.
curl -sD - "https://getmarkethistory.com/api/v1/bars/stocks/AAPL/1h"from markethistory import Client
page = Client().preview("stocks", "AAPL") # no keypage.to_pandas().tail()import json
import requests
url = "https://getmarkethistory.com/api/v1/bars/stocks/AAPL/1h"params = {}headers = {} # the free sample needs no key
rows = []while True: r = requests.get(url, params=params, headers=headers, timeout=60) r.raise_for_status() # see /docs/errors for what each status means rows += [json.loads(line) for line in r.text.splitlines() if line] if "X-Next-Cursor" not in r.headers: break params["cursor"] = r.headers["X-Next-Cursor"]HTTP/2 200content-type: application/x-ndjsonx-row-count: 100x-preview-limit: 100x-preview-from: 2021-11-01T07:20:48Zx-data-through: 2026-10-31T04:58:00Z
{"close":348.4375,"gap_fill_method":0,"high":348.47,"is_gap":false,"low":347.94,"open":347.98,"symbol":"AAPL","symbol_id":41,"timestamp":"2026-10-22T22:00:00Z","trade_count":1656,"volume":151877.0,"vwap":348.2574253059383}{"close":348.6,"gap_fill_method":0,"high":348.75,"is_gap":false,"low":348.1428,"open":348.4,"symbol":"AAPL","symbol_id":41,"timestamp":"2026-10-22T23:00:00Z","trade_count":1679,"volume":40918.0,"vwap":348.491937076299}Columns
Section titled “Columns”timestamp (the bar’s open, UTC), open, high, low, close, volume, vwap,
trade_count, symbol and symbol_id. is_gap and gap_fill_method are always false and
0: a missing interval is absent, never filled. Types and encodings are in
Data & formats.
One bar is one bar. A page rounds up to a block of 100, and an empty page is free. A free
account’s monthly bars pay for 1h only; every other timeframe needs
bought bars.