How to Get US Stock After-Hours Data? Market Data API Integration and Quality Verification
TickDB Research ·
How to Get US Stock After-Hours Data? Market Data API Integration and Quality Verification
Abstract: After connecting to a US stock market data API, how do you determine whether the returned prices and volume belong to after-hours trading? This article explains after-hours field reading, cross-midnight time attribution, the difference between missing data and zero volume, and provides reproducible request examples and Python validation code using synthetic samples, helping you check the after-hours data quality of your US stock data source.
The API returned price, timestamp, and volume — can you directly use them as after-hours market data?
Not yet. A single response may contain regular session, pre-market, after-hours, and overnight data simultaneously, with different objects having different time windows and statistics. A stock with no new trades all night could also be a normal market condition. The real questions to answer are: which instruments should have data, which night these records belong to, and what zero updates actually mean.
This approach applies to watchlist scanning, overnight dashboards, and research data ingestion. This article does not perform continuous overnight collection, so no market-wide coverage rates, packet loss rates, or vendor quality rankings are provided.
1. First Clarify Which Type of After-Hours Data You Are Checking
After-hours market data requires specifying instrument, time window, granularity, and source. Without these conditions, the same price difference could have completely different explanations.
| Dimension | Questions to Verify | Required Evidence |
|---|---|---|
| Instrument Scope | Are the agreed-upon after-hours instruments all included? | After-hours coverage list with effective dates, instrument mapping, and exclusion reasons |
| Session Scope | Does it cover the entire agreed-upon after-hours period? | Session start/end, calendar exceptions, collection and recovery logs |
| Data Granularity | Is this the latest snapshot or complete trade events? | Channel definition, historical boundaries, pagination or sequence and replay rules |
| Venue Scope | Which trading venue(s) or source(s) are covered? | Explicit venue list and aggregation methodology |
Instrument listing venue and after-hours trading venue should be recorded separately. Covering stocks listed on a certain market does not automatically mean covering trades in all after-hours venues for those stocks. FINRA's extended hours guidance also notes that prices may differ across systems; therefore, cross-source price discrepancies should first be checked against source scope and timing, not immediately labeled as errors. Source: FINRA Extended Hours Guidance

Figure 1: Four check boundaries for after-hours data, schematic diagram.
It is recommended to keep a collection specification for each analysis: target session, timezone, expected instrument list version, source scope, data type, collection start/end, failed requests, recovery status, and final statistics time. Only then can today's results be compared with tomorrow's.
2. The Coverage Rate Denominator Cannot Come From "What Was Returned This Time"
First define the expected set U: instruments that should be included in evaluation during the target session according to the confirmed coverage list. If a stock joins, pauses, or resumes during the session, record the effective interval; do not retroactively apply today's latest list to historical after-hours data.
Then define the set of unique instruments actually returned R, and the set V that passed the required field and session checks this time. Report separately:
- Return coverage rate: count of intersection of U and R ÷ count of U.
- Rule-pass coverage rate this time: count of intersection of U and V ÷ count of U.
- Pending explanation list: not returned, after-hours object missing, not target session, field anomalies — list codes and reasons separately.
When U is empty or there is no reliable list, coverage rate should show "cannot calculate" and not default to 100%. Simply counting "rows with after-hours objects ÷ total returned rows" only yields the field occurrence ratio in the returned sample.
Even more importantly, do not use "proportion of instruments with trades" as data completeness rate. No trades, insufficient permissions, API not covering the instrument, or collection failure can all manifest as no new records, but they require completely different handling.
3. Lock Down the After-Hours Session First, Then Interpret Timestamps
Using Blue Ocean's common after-hours window as an example, the session runs from 20:00 to 04:00 the next day Eastern Time; actual collection should still use the target venue's calendar and temporary notices, not just day-of-week calculations. Source: Blue Ocean Hours
When ingesting data, store at least three types of times separately: original market data time, client receive time, and business session date. Store original time uniformly in UTC; use America/New_York for display and session segmentation, avoiding hardcoding Eastern Time as UTC−5 year-round.
For example, October 8, 2026 20:00 to October 9, 2026 04:00 Eastern Time converts to October 9, 2026 08:00 to 16:00 Beijing Time. Do not split this session at Beijing time midnight, and do not use UTC date directly as the trading day.
"Session date" and "trade occurrence date" must also not be conflated. Blue Ocean's documentation distinguishes between the session date in FIX Tag 75 and the TRF date recorded by actual Eastern time trade occurrence. When adapting, preserve source fields and calculate your own session labels separately; do not overwrite the original date. Source: Blue Ocean Date Convention
Also clarify what the timestamp actually represents: latest trade, quote update, snapshot generation, or session summary cutoff. If a batch of records all stopped at 04:00, first verify whether this is a session end tag; do not determine that all stocks traded at exactly this moment based on identical timestamps alone, and do not immediately flag boundary values as out-of-range.
4. Read After-Hours Fields Correctly — Don't Mistake Regular Session Snapshots for After-Hours
A single stock record may simultaneously contain regular session, pre-market, after-hours, and overnight objects. When performing after-hours analysis, price, volume, high/low, and time must come from the same object and the same statistical window.
The request example below queries three instruments and is suitable for initial response structure verification. Documentation limits single queries to a maximum of 50 instruments; more instruments require batch processing per API limits. This time, market data tools were used to obtain responses for these three instruments; the curl commands below are written according to official API documentation and were not executed separately this time. API Field Reference
# First set TICKDB_API_KEY locally; do not write it into manuscripts or repositories.
: "${TICKDB_API_KEY:?Please set the environment variable first}"
curl --fail-with-body --compressed --get \
--connect-timeout 10 --max-time 60 \
-H "X-API-Key: ${TICKDB_API_KEY}" \
--data-urlencode "symbols=AAPL.US,MSFT.US,NVDA.US" \
--data-urlencode "type=stock" \
"https://api.tickdb.ai/v1/market/ticker" \
-o us_snapshot.json
After saving the response, first check HTTP status, business status codes, and array structure. HTTP success only means the request completed; also verify whether target instruments were returned, whether codes are duplicated, and whether each record's time and field conditions are met. A single snapshot cannot restore all trades that occurred between two samples.
In this single query, all three instruments carried after-hours objects with identical timestamps within those objects. This observation supports "reading from after-hours objects and checking time semantics," but cannot be used to infer that all three stocks traded simultaneously, nor to extrapolate availability for other instruments. The difference between top-level time and after-hours object time in the query records indicates that corresponding object-level time must also be fetched when validating prices.

Figure 2: After-hours field reading in two integration methods, documentation illustration, not continuous collection results.
The adaptation layer should preserve three distinct states: field does not exist, field value is empty, field explicitly is zero. Only the last case allows inclusion in zero-value calculations, and even then, you need to confirm that zero belongs to the target session. get("volume", 0) disguises missing information as zero volume and is not suitable for quality statistics.
5. Verify Judgment Logic with Executable Code
The code below checks after-hours snapshots converted to a unified format. event_time is an adaptation layer field, not a requirement for vendors to use the original field name; this example explicitly specifies it represents event time, using a left-closed, right-open session interval. If the source provides a session end tag, corresponding rules must be established first; this example cannot be applied directly.
Requires Python 3.9+ with IANA timezone data available in the environment. All five inputs are synthetic data used to verify missing values, zero values, cross-session, and price constraints — not live market data.
from datetime import datetime
from decimal import Decimal, InvalidOperation
from zoneinfo import ZoneInfo
ny = ZoneInfo("America/New_York")
start = datetime(2026, 10, 8, 20, tzinfo=ny)
end = datetime(2026, 10, 9, 4, tzinfo=ny)
def check(q):
required = ("event_time", "last", "low", "high", "volume")
if not isinstance(q, dict) or any(
q.get(k) is None for k in required
):
return "MISSING"
try:
t = datetime.fromisoformat(q["event_time"])
if t.tzinfo is None:
return "INVALID_TIME"
p, lo, hi, vol = [
Decimal(str(q[k]))
for k in ("last", "low", "high", "volume")
]
except (ValueError, TypeError, InvalidOperation):
return "INVALID_VALUE"
if not all(x.is_finite() for x in (p, lo, hi, vol)):
return "INVALID_VALUE"
if not start <= t.astimezone(ny) < end:
return "OUTSIDE_SESSION"
if vol < 0 or not 0 < lo <= p <= hi:
return "INCONSISTENT"
return "ZERO_REVIEW" if vol == 0 else "RULE_PASS"
base = dict(event_time="2026-10-09T02:00:00+00:00",
last="100", low="99", high="101", volume=10)
samples = [base, None, {**base, "volume": 0},
{**base, "event_time": "2026-10-08T02:00:00+00:00"},
{**base, "last": "102"}]
for q in samples:
print(check(q))
Actual output in order: RULE_PASS, MISSING, ZERO_REVIEW, OUTSIDE_SESSION, INCONSISTENT.
RULE_PASS only indicates passing these local rules; ZERO_REVIEW indicates preserving zero values for semantic verification. The code does not verify whether tick-by-tick data is complete, nor does it prove that other markets lack better prices. The high/low price constraints also have prerequisites: last price, high, and low must use consistent trade ranges and revision conventions.
6. What Evidence Is Still Needed for a Full Overnight Quality Report
Distinguish between no updates and data stream interruption. Long-tail stocks may have no trades for extended periods; when trade records are unchanged, judge based on connection status, heartbeats, subscription confirmations, and other active instruments. A healthy link does not prove data completeness for a single stock — it only narrows the investigation scope.
Calculate record age and transmission delay separately. Receive time minus latest trade time may include the market's own no-trade intervals. Only when timestamp semantics, clock synchronization, and collection location are clear is it appropriate to compare latency percentiles; without sync error budgets, negative values should also first be checked against clocks and field meanings.
Process trade events and cumulative values separately. Cumulative volume sampled per second cannot be directly summed. Cross-session resets, late trades, cancellations, and corrections can all change cumulative values; first confirm rules, then perform differencing or tick-by-tick recalculation. When revision messages are missing, mark aggregation results as provisional.
Duplicate and out-of-order determination based on protocol. When stable event IDs exist, deduplicate by their uniqueness scope; when sequence numbers exist, check gaps by channel, partition, and reset rules. Do not delete records directly with "same time + price + quantity"; two real trades may also be identical. When sequence numbers or complete reconciliation sources are absent, do not report unprovable packet loss rates.
Price deviations first enter the review queue. After-hours may feature lower liquidity, wider spreads, and higher volatility. Significant deviations from regular session closing prices can trigger checks but cannot alone serve as error indicators; review requires same venue, same time, same price type, plus checks of corporate actions and adjustment conventions. Source: FINRA Extended Hours Guidance
The final report should simultaneously provide numerator, denominator, exclusion reasons, and unknown items. First group by activity level, stocks vs. ETFs, and source venues, then look at overall metrics to avoid having a few active stocks mask long-tail issues. Thresholds should be determined by use case: freshness requirements for dashboards, revision wait periods for research, and completeness requirements for tick-by-tick replay should be set separately.
A verifiable after-hours data quality analysis is complete only when you can explain which session each record belongs to, why each gap exists, and what evidence each conclusion depends on. Returning many records is just the starting point.