How to get pre-market, after-hours and overnight bars as data
Daily bars leave extended hours out of the price, minute bars may need a flag, and overnight bars are one venue's. Which APIs return which session, and how.
Ask for minute bars, not daily ones, and check the session rule. Alpaca, Massive and EODHD put pre-market and after-hours trades in intraday bars by default; Twelve Data needs prepost=true and Tiingo afterHours=true. Overnight bars, 8 p.m. to 4 a.m., come from a single venue, Blue Ocean ATS, through Alpaca, Tiingo or Databento. Daily bars keep extended trades out of the price, and sometimes put them in the volume.
The short way
Ask for minute bars, not daily ones, and label the session yourself.
Alpaca documents its rules more completely than most, and its
historical consolidated bars are on the free plan except for the latest fifteen minutes. Its bars
endpoint has no extended-hours switch, because extended-hours trades are already in its minute
bars. The aggregation table in its market data FAQ, read on 9 October 2026, shows a trade with the
T (extended hours) condition updating a minute bar's open, high, low, close and volume. The same
trade updates only the volume of a daily bar.
from datetime import datetime, time, timedelta
from zoneinfo import ZoneInfo
import requests
NY = ZoneInfo("America/New_York")
URL = "https://data.alpaca.markets/v2/stocks/bars"
HEADERS = {"APCA-API-KEY-ID": "YOUR_API_KEY", "APCA-API-SECRET-KEY": "YOUR_API_SECRET"}
def minute_bars(symbol, start, end, feed="sip"):
params = {"symbols": symbol, "timeframe": "1Min", "start": start, "end": end,
"feed": feed, "adjustment": "raw", "limit": 10000}
while True:
r = requests.get(URL, headers=HEADERS, params=params, timeout=30)
r.raise_for_status()
body = r.json()
yield from body["bars"].get(symbol, [])
if not body.get("next_page_token"):
return
params["page_token"] = body["next_page_token"]
def session(ts):
t = ts.astimezone(NY).time()
if time(4) <= t < time(9, 30):
return "pre"
if time(9, 30) <= t < time(16):
return "regular"
if time(16) <= t < time(20):
return "post"
return "overnight"
def trade_date(ts):
# From 6 December 2026 the tape's trading day starts at 8 p.m. the evening before.
# Holidays and weekends still need an exchange calendar on top of this.
local = ts.astimezone(NY)
return (local + timedelta(days=1)).date() if local.time() >= time(20) else local.date()
for bar in minute_bars("AAPL", "2026-10-07T08:00:00Z", "2026-10-08T00:00:00Z"):
ts = datetime.fromisoformat(bar["t"].replace("Z", "+00:00"))
print(ts.astimezone(NY).strftime("%H:%M"), session(ts), trade_date(ts), bar["c"], bar["v"])
feed=sip is the consolidated tape: history since 2016 on the free plan, with the latest fifteen
minutes reserved for the $99 one. feed=iex is a single exchange, and the only live feed on the
free plan. For the night, Alpaca's documentation lists two more values. boats is Blue Ocean ATS,
the alternative trading system that runs the US evening session. overnight is what Alpaca calls
its derived feed from the same source, "cheaper, but slightly less accurate", whose trades are 15
minutes delayed and adjusted to fit the bid-ask spread. Run the loop once per feed, keep the feed
name beside every row, and never append Blue Ocean rows to the SIP series as if they were the
same market.
What the options are
Consolidated minute bars with extended hours on by default. Alpaca
as above. Massive's custom bars endpoint,
/v2/aggs/ticker/{ticker}/range/{multiplier}/{timespan}/{from}/{to}, is documented as covering
"pre-market, regular market, and after-hours sessions", in Eastern time, from "qualifying trades"
only. Its separate daily ticker summary, /v1/open-close/{ticker}/{date}, returns the regular
open and close together with preMarket, the open in pre-market trading, and afterHours, the
close in after-hours trading. That is the shortest way to get the 4 a.m. print and the 8 p.m.
print for one day. Both endpoints are on every Stocks plan, but the $29 and $79 tiers are 15
minutes delayed and Basic is end of day.
A flag you have to set. Twelve Data's /time_series takes
prepost=true, which the documentation limits to US equities at the 1min, 5min, 15min and
30min intervals, on the Pro plan ($99) and above. It also says the extended-hours values come
"without volume":
import requests
r = requests.get(
"https://api.twelvedata.com/time_series",
params={"symbol": "AAPL", "interval": "5min", "prepost": "true",
"timezone": "America/New_York", "outputsize": 500},
headers={"Authorization": "apikey YOUR_API_KEY"},
timeout=30,
)
bars = r.json()["values"]
Tiingo's intraday endpoint, /iex/{ticker}/prices, takes afterHours=true, which
"includes pre and post market data if available". The bars are IEX's, not the tape's. Its
forceFill=true copies the previous bar's prices into intervals with no trades, which is a
choice you want to make knowingly in the thin hours.
Extended hours you cannot switch off. EODHD's intraday API gives US 1-minute bars from 2004 that include pre-market and after-hours trading, with no parameter to exclude them. Timestamps come back as a UTC Unix time and a UTC datetime, so filtering is yours to do. The 1-minute window is 120 days per request, every request costs 5 API calls, and US 1-minute data is updated two to three hours after after-hours trading ends. Intraday starts on the $29.99 plan.
The overnight session, from one venue. Tiingo added a BOATS endpoint in beta on 21 July 2026,
with overnight bars at /boats/{ticker}/prices for the 8:00 p.m. to 3:59 a.m. Eastern session.
It costs $9 a month on top of a paid plan and is not on the free tier.
Databento sells Blue Ocean's own feed as the OCEA.MEMOIR dataset, with
history from 24 August 2025, trades and OHLCV among its schemas, and a session its announcement
gives as 8:00 p.m. to 4:00 a.m. Eastern, Sunday to Thursday. Databento has no session flag at all:
you pick the venue datasets and build the session from timestamps.
Where this breaks
The daily bar is not the sum of the minute bars. In Alpaca's table, an extended-hours trade moves a minute bar's price and a daily bar's volume. Add up a day of minute bars and you get the daily volume, but not its high or low. A pre-market high is in no daily bar at all. Other vendors document their own "qualifying" rules, or document none. Two charts of the same day can then show the same volume and a different range, or the same range and a different volume. Why two charts show different candles explains the general case.
VWAP over extended hours is a different number, or no number. Twelve Data's extended-hours bars have no volume, so nothing volume-weighted can be computed across them. Where volume exists, a VWAP anchored at 4 a.m. is not the 9:30 a.m. VWAP a chart prints for the regular session. Label the anchor in the column name. VWAP has the definitions.
The flag does not tell you the session. The tape marks a trade outside 9:30 a.m. to 4 p.m.
with the T sale condition. The CTA's extended-hours FAQ, version 1.2 of 29 September 2026, keeps
that one condition for everything from 9 p.m. to 9:30 a.m. and after the 4 p.m. close. A trade at
11 p.m. and one at 7 a.m. look the same. Alpaca's table has a second extended-hours condition, U,
which updates volume and no prices in either bar size. Split sessions by timestamp, as the code
above does.
Thin hours turn venue gaps into price gaps. Massive produces no bar for an interval without
an eligible trade. At 5 a.m. on a quiet name that is most intervals, so a minute series is full of
holes, and filling them with the last price (Tiingo's forceFill) shows trading that never
happened. A single-exchange feed makes it worse. Alpaca's free IEX feed, Tiingo's IEX intraday
bars and every overnight source on this page are one venue's prints. Alpaca's overnight feed is
not prints at all: it is delayed and adjusted to the bid-ask spread by Alpaca.
Four time conventions, and the clocks move. Massive's custom bars are in Eastern time, with
each bar's start as a Unix millisecond timestamp. The daily bar in its own sample response starts
at 05:00 UTC, which is midnight in New York in January. EODHD is UTC. Twelve Data returns exchange
time unless you pass timezone, which it ignores for daily intervals. The pre-market's 4 a.m.
Eastern is 08:00 UTC in summer and 09:00 UTC after clocks change on 1 November 2026. A filter
written in UTC moves the session by an hour twice a year.
The overnight boundary is about to move. Blue Ocean's session starts at 8 p.m. The
consolidated tapes are scheduled to carry overnight trading from 9 p.m., starting 6 December
2026, with the trade date rolling to the next day at 8 p.m. From then an overnight bar can come
from the SIP or from one venue, and the two will not agree. No vendor on this page had documented,
by 9 October 2026, how its minute or daily bars will treat SIP overnight trades.
What 23-hour trading changes in your market data has the plan
documents. The trade_date function above encodes the 8 p.m. boundary, and holidays still need
an exchange calendar.
"Close" after 4 p.m. is ambiguous. Massive's daily summary gives the regular close and a
separate afterHours close. A "last price" read at 6 p.m. is neither the official close nor the
end of the session. Store the official close and the last extended-hours print in two columns, and
never let a refresh after 4 p.m. overwrite the first with the second.
If you outgrow this
When you need years of it, not a window, pagination and per-request limits become the job. How to backfill minute bars compares the archives, including which ones keep extended hours, and how to store minute bars locally covers keeping them.
When you need it live, the same session question applies to a stream. How to stream real-time quotes covers the WebSocket side, and why real-time stock data is so expensive covers the exchange fees behind the consolidated feed.
When you want to look rather than store, a charting platform's extended-hours setting does the labelling for you. The trade-off is that you cannot see which trades it counted. Charting platforms and screeners lists them, and the consolidated tape entry explains what "all exchanges" covers.
The tools that do this
In the order this page recommends trying them. Paid placement does not affect this order.
Alpaca Market Data
Minute bars carry extended-hours trades by default; feed=boats or overnight adds the night. Daily bars count those trades in volume, never in price.
Free IEX data forever, full SIP and OPRA for a flat $99 a month.
$99/moFree tier
Massive
Custom bars in Eastern time cover pre-market, regular and after-hours; a daily summary adds a preMarket open and afterHours close. Delayed below $199.
Full-tick US equities, options and futures from a direct exchange feed.
$29/moFree tier
Twelve Data
prepost=true adds pre- and post-market to 1- to 30-minute US bars, from the $99 Pro plan, and the extended bars arrive with no volume.
Global stocks, forex and crypto over REST and WebSocket, billed in credits per minute.
$29/moFree tier
EODHD
US 1-minute bars from 2004 always include 4 a.m. to 8 p.m. Eastern, timestamped in UTC, with no switch to leave them out. From $29.99 a month.
End-of-day and fundamentals for 60+ exchanges worldwide, at a hobbyist price.
$19.99/moFree tier
Tiingo
afterHours=true adds pre- and post-market to IEX-only intraday bars; Blue Ocean overnight bars are a $9 add-on, in beta since July 2026.
End-of-day equity history back to 1962, plus crypto, forex and news, from $30 a month.
$30/moFree tier
Databento
No session flag, only venues. Blue Ocean's own feed, OCEA.MEMOIR, from August 2025, beside the exchange feeds; billed per gigabyte.
Full order book and tick history from exchange feeds, billed by the gigabyte.
$199/mo
FAQ
Why does my daily bar not show the pre-market high?
Because the daily bar is built from regular-session prices. Alpaca's aggregation table, read on 9 October 2026, lets an extended-hours trade update a minute bar's open, high, low and close, and a daily bar's volume only. A pre-market spike appears in the minute bars and nowhere in the daily high. Build the extended-hours high yourself from minute bars.
Is there a free source of pre-market prices as data?
For history, yes. Alpaca's free plan serves consolidated bars back to 2016 and withholds only the latest fifteen minutes, so yesterday's pre-market is free; live, the free plan is the IEX feed, one exchange. Massive's free tier is end of day only. Tiingo's intraday bars are IEX-only, and its overnight add-on is not offered on the free tier. Nothing free covers the overnight session.
Do overnight trades show up in the same bars as pre-market?
Today, only if you ask for the overnight venue explicitly. Overnight bars come from Blue Ocean ATS through Alpaca's boats feed, Tiingo's BOATS endpoint or Databento's OCEA.MEMOIR dataset, separately from the 4 a.m. to 8 p.m. series. The consolidated tapes are scheduled to carry overnight trades from 6 December 2026, and no vendor here has yet documented how its bars will change.
What time zone are extended-hours bars in?
It depends on the vendor. Massive builds custom bars in Eastern time and returns the start of each bar as a Unix millisecond timestamp. EODHD returns UTC. Twelve Data returns exchange time by default and takes a timezone parameter, which it ignores for daily bars. Convert everything to UTC for storage and to New York time for session labels.
Sources
- Historical Stock Data (feed parameter) — Alpaca, read
- Market Data FAQ (how bars are aggregated from trades) — Alpaca, read
- About Market Data API (subscription plans) — Alpaca, read
- Historical bars — API reference — Alpaca, read
- Custom Bars (OHLC) — Stocks REST — Massive, read
- Daily Ticker Summary (OHLC) — Stocks REST — Massive, read
- Time series — API documentation — Twelve Data, read
- Intraday Historical Stock Data API — EODHD, read
- Real-time and Historical IEX API Documentation — Tiingo, read
- BOATS Overnight Real-time API Documentation — Tiingo, read
- Blue Ocean ATS now available — Databento,
- CTA Extended Trading Hours — Frequently Asked Questions, version 1.2 — Consolidated Tape Association / Securities Industry Automation Corporation,
The catalogue next door
This page names a handful of cards. The rest of them are in Stock Charting Platforms & Screeners, each filled in against the same schema, with the fields to narrow it yourself.
Last updated . Corrected in place: this is a reference page, not a dated post.