How do you compare a full year of price action for a stock, a currency pair, and a token without storing every individual trade that ever printed? You don't store the trades. You store OHLCV bars, and almost every chart, backtest, and price dashboard you've ever seen runs on them.
OHLCV is the default shape of historical market data, and understanding what a bar actually contains (and what it quietly throws away) is the difference between a backtest you can trust and one that silently lies to you.
What an OHLCV bar records#
OHLCV stands for Open, High, Low, Close, Volume. Each bar summarizes price activity over one fixed time window into five numbers: the open is the first trade price in the window, the high is the maximum, the low is the minimum, the close is the last trade price, and the volume is the total quantity that changed hands. A bar can cover one minute, one hour, one day, or one month.
The compression is the point. A day of one-minute bars folds thousands of individual trades into a few hundred rows for a US equity session, or 1,440 rows for a 24-hour crypto market. A candlestick chart is nothing more than OHLCV drawn as rectangles and wicks: the body spans open to close, the wicks reach to high and low.
The format predates electronic markets, and it survives because it answers the four questions analysts ask most. Where did price start, how far did it range, where did it settle, and how much interest was behind the move? Those four answers, plus a timestamp, are enough to build moving averages, volatility estimates, support and resistance levels, and most of the indicators a strategy will reference.
Why APIs serve bars instead of raw ticks#
A tick is a single event: one trade, or one quote update. Tick data is the ground truth, but it is expensive to move and expensive to store. A liquid crypto pair can print tens of thousands of ticks per hour. If all you need is a daily volatility figure or a 50-day moving average, pulling every tick is wasteful, and your code spends most of its time aggregating data you'll immediately discard.
Bars solve that by pre-aggregating on the server. You ask for the interval you need, and the data arrives already bucketed. This is also why a market data API exposes intervals as a parameter rather than forcing one resolution. A research script computing yearly returns wants 1d or 1mo bars; an execution-monitoring dashboard wants 1m. Same endpoint, different interval.
With SiftingIO the historical bar endpoints live under /v1/hist/*, grouped by purpose rather than by asset class, so the request shape stays identical whether you're pulling equities, forex, or crypto:
curl -H "X-API-Key: $SIFTING_KEY" \
-H "Accept-Encoding: gzip" \
"https://api.sifting.io/v1/hist/stocks/{ticker}/bars?interval=1d"
Swap the path segment to cross asset classes under one credential:
# Crypto bars, v is base-asset volume
curl -H "X-API-Key: $SIFTING_KEY" -H "Accept-Encoding: gzip" \
"https://api.sifting.io/v1/hist/crypto/BTCUSD/bars?interval=1h"
# Forex bars, OHLC only
curl -H "X-API-Key: $SIFTING_KEY" -H "Accept-Encoding: gzip" \
"https://api.sifting.io/v1/hist/forex/EURUSD/bars?interval=1h"
Each bar comes back with a timestamp field t, then o, h, l, c, and v. Stock bars support intervals from 1m up to 1mo; forex and crypto bars run 1m to 1h. Large pulls page through a cursor: the response meta carries next_cursor (null on the last page), so you loop until it's null rather than guessing how many rows exist. Default page size is 50, max 200.
What the close price actually is#
Here is the part most explanations skip. The close in a bar is a single number, but for fragmented and on-chain markets there is rarely a single "real" price at any instant. Different venues quote slightly different numbers, thin pools drift, and a stale or manipulated print can poison a naive last-trade close.
SiftingIO builds each bar's price from a cross-venue consensus rather than passing through one venue's tape. The fair price is a volume-and-reputation-weighted median across multiple independent venues, scored for outliers and staleness before it's published. Because a median needs a majority of venues to err in the same direction before the output moves, a single bad feed cannot drag the close on its own. For thin or on-chain markets, that consensus number is frequently closer to reality than any one venue's print. The full method is published at /data-methodology.
Treat that close as a reference and validation layer: a price to research against, model on, and cross-check the number your execution venue is showing. It is a synthetic reference value, not an exchange-of-record print and not an execution feed.
Common pitfalls#
The heavy historical endpoints require gzip. Send Accept-Encoding: gzip on every /v1/hist/* request or the bar endpoints return 406 gzip_required with an empty body. If your HTTP client strips the header or you copied a snippet without it, you'll see a 406 that looks like an auth or path problem but isn't. Most language SDKs and curl handle the decompression for you once the header is set.
Forex volume is always zero. FX bars carry OHLC only, and the v field is hard-coded to 0 because spot FX has no consolidated volume tape. If a generic bar-cleaning function filters out rows where v == 0, it will silently delete every forex bar you fetch. Branch on asset class before applying any volume filter.
Bars are UTC, and timestamps are not interchangeable formats. Date-only fields come back as ISO YYYY-MM-DD, full timestamps as RFC 3339 UTC, and live ticks as int64 epoch milliseconds in t. If you assume venue-local time or mix epoch seconds with milliseconds, your bars will line up off by hours or land in 1970. Normalize everything to UTC at ingest, before you join two series together.
Watch the interval support per asset class too. Asking for a 1d crypto or forex bar where only 1m to 1h exists returns an error rather than silently downsampling, so check the supported set in the docs rather than assuming every interval works everywhere.
Putting it to work#
OHLCV is the lingua franca of market data because it is small enough to move cheaply, detailed enough to chart and backtest, and uniform enough to compare a stock, a currency pair, and a token side by side. Pull the interval you need, respect the gzip and UTC conventions, page through the cursor, and remember that a well-formed close is a consensus reference rather than a single venue's last tick.
The same bar shape covers six asset classes through one credential, with official Go, Python, and JavaScript SDKs that handle gzip and pagination for you. Read the docs to see the full endpoint reference and start pulling bars on the free tier.


