Contents Lesson 12 of 16

4 min read · practitioner

Is this quote stale, or is the market simply closed?

A price has not moved in six hours. Two very different explanations: your data pipeline is broken, or the exchange is shut and the price is exactly as current as it can be. The response object alone cannot tell you which. One extra endpoint can.

The endpoint that knows the calendar

/exchange-details/{EXCHANGE_CODE} returns the operating facts of a market. A live call for US gives:

  • Timezone — America/New_York
  • TradingHours — Open: 09:30:00, Close: 16:00:00, OpenUTC: 13:30:00, CloseUTC: 20:00:00, WorkingDays: Mon,Tue,Wed,Thu,Fri
  • isOpen — true
  • ExchangeHolidays — the named closures, with dates
  • ExchangeEarlyCloseDays — sessions that end early
  • ActiveTickers — about 52,000 on 2026-07-28 — plus PreviousDayUpdatedTickers and UpdatedTickers. These three are live counters: quote them with the date you read them, because they move every day
Live API response: us exchange details

Run the same call for XETRA and everything shifts: Europe/Berlin, open 09:00 local and 07:00 UTC, close 17:30 local and 15:30 UTC, and 4,221 active tickers on 2026-07-28 against 4,189 on 2026-09-15. Same schema, different market, and the difference is why hardcoding "the market opens at 13:30 UTC" is wrong twice over — once for the exchange and once for the season.

Three ways a day is not a normal day

The calendar fields exist because sessions are irregular in three distinct ways, and each breaks a different assumption.

Holidays. The live US calendar for 2026 lists closures including 3 April (Good Friday), 25 May (Memorial Day), 19 June (Juneteenth), and — the one that catches people — both 3 and 4 July, because Independence Day fell on a Saturday and the exchange observed a substitute day on the Friday. A rule like "weekdays are trading days" gets that wrong every year in a different place.

Early closes. ExchangeEarlyCloseDays for 2026 lists 27 November and 24 December, both with EarlyClose: 13:00. On those days a normal-looking daily bar covers three and a half hours instead of six and a half. Volume is a fraction of usual and any "average daily volume" that includes them is dragged down by a calendar quirk rather than by anything about the instrument.

Extended sessions. /v2/exchange-details/{code} adds PreMarketOpen, PreMarketClose, AfterHoursOpen, AfterHoursClose and, where relevant, LunchBreakStart and LunchBreakEnd. Its documented US schedule runs pre-market from 04:00 and after-hours to 20:00 local — which matches the observed fact that the first 1-minute intraday bar of a US day arrives at 08:00 UTC, four and a half hours before the opening auction.

The calendar and the counters, as the endpoint actually returns them:

Live API response: us exchange calendar

Reading a timestamp properly

With those facts in hand, the check on any quote becomes mechanical:

  1. Convert the quote's timestamp to the exchange's local time using Timezone.
  2. Compare it against TradingHours for that day, after checking the day against the holiday and early-close lists.
  3. If the market is open and the quote is more than the expected delay behind, something is wrong. If the market is shut, the quote is as fresh as it can be and should be labelled "as of", not "live".

The freshness counters give you a second, cruder signal. PreviousDayUpdatedTickers against ActiveTickers says how much of the universe actually reported yesterday — roughly 44,000 of 52,000 for the US on the day this was checked, which is not a fault but a reminder that a large share of listed instruments do not trade on a given day. UpdatedTickers counts today's progress, and reading zero mid-session on an exchange that says isOpen: true is worth a look.

One schema warning

The v1 response returns ExchangeHolidays and ExchangeEarlyCloseDays as objects keyed "0", "1", "2" rather than as plain lists, while /v2/exchange-details/{code} keys its holiday collection by date. Parse defensively and do not assume you can iterate either one the way the schema diagram suggests. This is trust-but-verify in miniature: check the shape you actually received.

Try it now

  1. The /exchange-details/US block is the first table in this lesson; here is /exchange-details/XETRA. Put the two TradingHours blocks side by side and compute the UTC offset each implies today. Note that Open is fixed and OpenUTC is not, and say what each OpenUTC will read after the clocks change in late October and early November.
Live API response: mda1 xetra trading hours
  1. Take the 2026 US holiday list from the lesson and generate the trading days for July. Then compare with /eod/AAPL.US?from=2026-07-01&to=2026-07-31, which returned 22 rows on 28 September 2026; its first four dates and its last are below. Find the gap in the first four and name the holiday behind it.
Live API response: mda1 apple first days july 2026
  1. Write the staleness check as a function: given a quote timestamp and an exchange code, return "live", "delayed as expected", "market closed" or "suspicious". You will reuse it in everything you build.