Guide
Exchange calendar API: check if a market is open, in code
Market hours look like a constant until the first half day, lunch break or clock change. Put the calendar behind one request and let your code ask.
On this page
- Why a trading calendar belongs behind an API
- The exchange calendar API in four endpoints
- Is the stock market open today? One request
- Global market hours for a watchlist with /markets/status/bulk
- Asian stock market hours, lunch breaks and DST with /markets/sessions
- Holidays and half days with /markets/holidays
- Patterns that keep it cheap and correct
- Questions
Key takeaways
- GET /markets/status returns whether a market is open, its phase and the next regular open and close, with holidays, early closes and daylight saving applied.
- is_open is true in pre-market and after-hours too; test status or phase when you mean the regular session.
- GET /markets/status/bulk checks up to 25 instruments in one request, and an unknown item fails alone while the response stays 200.
- GET /markets/sessions returns a local day's trading windows in UTC, and GET /markets/holidays lists every closure and early close for a year.
- The at parameter evaluates any instant, so holiday and half-day logic can be tested in CI instead of waiting for Thanksgiving.
An exchange calendar API tells your code whether a market is open right now, which session it is in, and when it next opens or closes, with holidays, half days and daylight saving time already applied. TickerLayer exposes one as four REST endpoints: GET /markets/status, /markets/status/bulk, /markets/sessions and /markets/holidays. They cover 20 regional stock calendars plus crypto, forex and commodities, and they answer from a curated calendar rather than from live prices.
If you want the hours themselves rather than code, the guide to stock market hours covers the US schedule in every time zone. This page is the developer layer: real requests, real responses from September 28, 2026, and the edge cases that make hard-coded schedules fail.
Why a trading calendar belongs behind an API
- Clocks moveThe US changes clocks on March 8 and November 1, 2026, Europe on March 29 and October 25, and most of Asia never. For weeks each year the gaps between markets change.
- Half daysUS exchanges close at 1:00 p.m. ET on November 27 and December 24, 2026. A job that waits for 4:00 p.m. runs three hours late.
- Lunch breaksTokyo, Hong Kong and Shanghai stop for lunch, and Thailand pauses from 12:30 to 14:30. "Open 9 to 5" is wrong in both halves.
- Different weekendsThe Saudi market trades Sunday to Thursday. A Monday-to-Friday assumption misses one trading day and invents another.
- Clustered holidaysJapan closed Monday to Wednesday, September 21 to 23, 2026: two public holidays and the bridge day between them.
- Moving feastsLunar New Year, Eid and Easter move every year, so last year's holiday list is wrong this year.
The exchange calendar API in four endpoints
| Endpoint | Answers | Key parameters |
|---|---|---|
GET /markets/status | Is it open now, which phase, next open and close. With no parameters: one overview of every region | asset, symbol, market, at, includeSessions |
GET /markets/status/bulk | The same, for up to 25 instruments in one call | items as asset:symbol, at |
GET /markets/sessions | One local day's trading windows, in UTC | market (or asset and symbol), date |
GET /markets/holidays | Closures and early closes for a year | market, year, start, end |
Is the stock market open today? One request
Pass a market, or an asset and symbol, and you get one status object. The at parameter evaluates any instant instead of now, which is how this response describes Thanksgiving morning although it was requested in September:
curl -sS "https://api.tickerlayer.com/markets/status?asset=stocks&market=US&at=2026-11-26T15:00:00Z" \
-H "x-api-key: $TICKERLAYER_API_KEY"US status at 10:00 a.m. ET on Thanksgiving 2026
{
"asset": "stocks",
"market": "US",
"status": "closed",
"is_open": false,
"phase": "holiday",1
"reason": "Regional calendar closure",
"timestamp": "2026-11-26T15:00:00.000Z",2
"timezone": "America/New_York",
"local_time": "2026-11-26T10:00:00.000-05:00",
"next_open": "2026-11-27T14:30:00.000Z",3
"next_close": "2026-11-27T18:00:00.000Z",4
"holiday": {5
"date": "2026-11-26",
"name": "Thanksgiving Day",
"schedule": "closed",
"is_open": false,
"observed": true
},
"meta": { "calendar_version": "v1-static-2026-2027" }
}
phaseWhy it is closed: holiday here. Other values include primary, pre_market, post_market, closed, weekend and early_close.timestampThe instant evaluated: theatyou passed, or the current time.next_openFriday's 9:30 a.m. ET open, in UTC. 14:30Z, because New York is back on standard time.next_close18:00Z is 1:00 p.m. ET. The day after Thanksgiving is a half day, and the calendar already knows.holidayPresent when a calendar row applies to the local date. The nested object has no memo field; the holidays route always sends one.
Three fields answer different questions, and mixing them up is the classic bug:
| Question | Read | Watch out for |
|---|---|---|
| Is the regular session on? | status == "open" | status is pre_market or post_market in extended hours |
| Is any session active? | is_open | True in pre-market and after-hours as well |
| Why is it closed? | phase and reason | closed covers both night and a midday break; reason tells them apart |
| When does it open next? | next_open | Omitted while the regular session runs, and for 24/7 markets |
The overview has a trap of its own. Called with no parameters, /markets/status returns a regions map with one word per market. At 10:50 UTC on September 28 it read "us": "open" while the detail call said pre_market: the map flattens is_open. Use it to color a world map, not to drive trading logic.
Global market hours for a watchlist with /markets/status/bulk
Dashboards rarely care about one market. The bulk route takes up to 25 asset:symbol items, resolves each to its calendar and returns one row per item. An unknown item comes back as a row with an error while the others succeed, so one typo does not blank the board:
import os
from datetime import datetime, timezone
import requests
BASE_URL = "https://api.tickerlayer.com"
session = requests.Session()
session.headers["x-api-key"] = os.environ["TICKERLAYER_API_KEY"]
WATCHLIST = [
"stocks:US:KO",
"stocks:DE:SAP",
"stocks:JP:7203",
"stocks:HK:0700",
"forex:EURUSD",
"crypto:BTCUSD",
"stocks:XX:NOPE",
]
def get(path, **params):
resp = session.get(BASE_URL + path, params=params, timeout=10)
resp.raise_for_status()
return resp.json()
def countdown(iso):
"""'2026-09-28T13:30:00.000Z' -> 'in 2h 32m' (or '' when absent)."""
if not iso:
return ""
when = datetime.fromisoformat(iso.replace("Z", "+00:00"))
minutes = int((when - datetime.now(timezone.utc)).total_seconds() // 60)
days, rest = divmod(minutes, 1440)
hours, mins = divmod(rest, 60)
return f"in {days}d {hours}h" if days else f"in {hours}h {mins:02d}m"
board = get("/markets/status/bulk", items=",".join(WATCHLIST))
print(f"{'SYMBOL':<9} {'MARKET':<7} {'PHASE':<11} {'OPENS':<11} CLOSES")
for row in board["data"]:
if "error" in row: # unresolved items fail alone; the response is still 200
print(f"{row['item']:<17} error {row['error']['code']}")
continue
print(
f"{row['symbol']:<9} {row['market']:<7} {row['phase']:<11} "
f"{countdown(row.get('next_open')):<11} {countdown(row.get('next_close'))}"
)SYMBOL MARKET PHASE OPENS CLOSES
US:KO US pre_market in 2h 32m in 9h 02m
DE:SAP DE primary in 4h 32m
JP:7203 JP closed in 13h 02m in 19h 32m
HK:0700 HK closed in 14h 32m in 21h 02m
EURUSD FOREX primary in 4d 11h
BTCUSD CRYPTO primary
stocks:XX:NOPE error UNRESOLVEDPrice the call before you ship it: one bulk request per board refresh, never one status call per row, and never one per tick. The calendar only changes at session boundaries, so refreshing when the nearest next_open or next_close passes is enough. Even a 25-market board then needs well under a hundred bulk requests a day, against 1,440 at one a minute.
Asian stock market hours, lunch breaks and DST with /markets/sessions
The sessions route answers "what does this market's day look like" in UTC, with that local date's time zone rules applied. The US calendar returns a 13:30 UTC open for September 28 and a 14:30 UTC open for November 2, the first Monday after New York leaves daylight time, with no code on your side. Thailand, which pauses at midday, comes back as two primary windows:
{
"market": "TH",
"date": "2026-09-29",
"timezone": "Asia/Bangkok",
"is_open": true,
"holiday": null,
"sessions": [
{ "phase": "primary", "status": "open", "start": "2026-09-29T03:00:00.000Z", "end": "2026-09-29T05:30:00.000Z" },
{ "phase": "primary", "status": "open", "start": "2026-09-29T07:30:00.000Z", "end": "2026-09-29T09:30:00.000Z" }
],
"meta": { "calendar_version": "v1-static-2026-2027" }
}Windows carry "status": "open" for the regular session and "available" for extended hours such as US pre-market. On a weekend or holiday the windows still come back with their scheduled times, each marked "closed", so you can always draw the shape of a normal day. One limit to know: the API models the Thailand break today, while Tokyo, Hong Kong and Shanghai come back as one continuous window. Their breaks are on the country pages, for example Japan market hours.
Global market hours on a UTC clock
Hours in UTC
Holidays and half days with /markets/holidays
The holidays route returns a year of closures and early closes for one calendar. Combine it with the sessions route and you can describe any day in full. This script lists the US half days of 2026, then prints the day after Thanksgiving in UTC:
import os
import requests
BASE_URL = "https://api.tickerlayer.com"
HEADERS = {"x-api-key": os.environ["TICKERLAYER_API_KEY"]}
def get(path, **params):
resp = requests.get(BASE_URL + path, params=params, headers=HEADERS, timeout=10)
resp.raise_for_status()
return resp.json()
# 1. Every early close in the US calendar for 2026.
cal = get("/markets/holidays", market="US", year=2026)
for day in cal["holidays"]:
if day["schedule"] == "early_close":
print("early close:", day["date"], day["name"])
# 2. What the day after Thanksgiving looks like, in UTC, DST already applied.
day = get("/markets/sessions", market="US", date="2026-11-27")
for window in day["sessions"]:
print(f"{window['phase']:<12} {window['start'][11:16]} to {window['end'][11:16]} UTC")early close: 2026-11-27 Post-holiday early close
early close: 2026-12-24 Christmas Eve early close
pre_market 09:00 to 14:30 UTC
primary 14:30 to 18:00 UTC
post_market 18:00 to 22:00 UTCEarly-close rows carry "is_open": true, because the market does open; full closures carry false. meta.completeness is full when a market's year is fully seeded and partial when it is not, which is your cue to fall back to a conservative default instead of assuming the market is open. On the half day, after-hours ends at 5:00 p.m. ET (22:00 UTC), not 8:00 p.m.
Patterns that keep it cheap and correct
- Ask once, then sleepRead
next_openornext_closeand schedule the next check for that instant, instead of polling every minute all night. - Cache per local daySessions and holidays come from a versioned static calendar (
calendar_version"v1-static-2026-2027"). Fetch them once a day per market. - Test with atPass
at=2026-11-27T19:00:00Zand the API answerspost_marketfor the half day. Put Thanksgiving, a daylight saving Monday and a weekend in your test suite. - Gate on the data as wellThe calendar says when a market should trade, not that a quote arrived. Check quote timestamps, and follow trading halts for single stocks.
- Give agents the same clockThe MCP server exposes
get_market_status,get_market_sessionsandget_market_holidays, so an assistant can check the calendar before quoting a price. See financial MCP server.
For people rather than programs, every calendar also has a page: the world market hours map shows 38 markets, and each country page, such as United States, lists sessions, breaks and the 2026 holidays. Currencies run on their own weekly clock, covered in forex market hours, and the US extended sessions in after-hours trading.
Questions
Is the stock market open today?
US stocks trade Monday to Friday, 9:30 a.m. to 4:00 p.m. ET, except market holidays. In code, call GET /markets/status?asset=stocks&market=US: status reads open during the regular session, and phase explains any closure.
What is an exchange calendar?
A dataset of a market's trading days, session times, holidays and early closes, in the market's own time zone. An exchange calendar API turns it into answers such as "open now" and "next open at 13:30 UTC".
What is the difference between a trading calendar and market hours?
Market hours are the normal daily schedule. A trading calendar adds which specific days in a year are trading days, holidays or shortened sessions.
Does the API handle daylight saving time?
Yes. Sessions are defined in each market's time zone and returned in UTC for the date you ask about, so the US open is 13:30 UTC in summer and 14:30 UTC in winter without any conversion on your side.
What are Asian stock market hours in UTC?
On a normal weekday: Tokyo 00:00 to 06:30 with a lunch break, Hong Kong 01:30 to 08:00 and Shanghai 01:30 to 07:00, both with lunch breaks, Mumbai 03:45 to 10:00, and Bangkok 03:00 to 09:30 with a midday pause. None of these markets change clocks.
How many markets does the calendar cover?
The API covers 20 regional stock calendars plus crypto, forex and commodities. The public market-hours pages cover 38 markets.