// Get all public holidays for Germany in 2026
const response = await fetch(
"https://worlddataapi.com/v1/holidays/DE?year=2026&type=public",
{ headers: { "X-API-Key": "YOUR_API_KEY" } },
);
const data = await response.json();
console.log(data.holidays);
// [
// { "date": "2026-01-01", "name": "New Year's Day", "types": ["public"] },
// { "date": "2026-04-03", "name": "Good Friday", "types": ["public"] },
// ...
// ]
If you're building an application that handles dates across countries — payroll, scheduling, e-commerce, travel — you need holiday data. This guide covers why hardcoding fails and how to implement holiday support correctly.
The Challenge#
Building international holiday support requires solving multiple interconnected problems: holidays move year to year, observed dates follow country-specific rules, governments add and remove holidays, regional variations exist within countries, and religious calendars like the Islamic calendar shift significantly each year. A hardcoded approach cannot handle this complexity without constant maintenance.
Prerequisites#
Before implementing holiday support, you need:
A World Data API key (get one free at worlddataapi.com)
Basic familiarity with REST APIs and JSON
For JavaScript examples: Node.js 18+ or a modern browser with fetch support
For Python examples: Python 3.7+ with the
requestslibrary installed
Why Hardcoding Holidays Fails#
Problem 1: Holidays Move#
Easter falls on a different date every year (March 22 to April 25). Thanksgiving is the 4th Thursday of November. Chinese New Year falls between January 21 and February 20 depending on the lunar calendar.
A hardcoded list for 2026 is wrong for 2027.
Problem 2: Observed Dates Change#
When July 4th falls on Saturday, it's observed on Friday. When it falls on Sunday, it's observed on Monday. This observation logic varies by country:
US: Saturday holidays observed Friday, Sunday holidays observed Monday
UK: Different rules for different holidays
Some countries: No observation shifting at all
Problem 3: Governments Add and Remove Holidays#
Juneteenth became a US federal holiday in 2021. Japan added Mountain Day in 2016. Countries regularly modify their holiday calendars with little advance notice.
Problem 4: Regional Variations#
Germany has 9 federal holidays. But Bavaria has 13 (adding Epiphany, Corpus Christi, Assumption, All Saints' Day). Berlin has 10. Your "German holidays" list is wrong for most German states.
California observes César Chávez Day. Texas doesn't. The UK has different holidays for Scotland versus England and Wales.
Problem 5: Islamic Holidays Shift#
Islamic holidays follow a lunar calendar and move approximately 11 days earlier each year. Ramadan in 2026 falls in late February/early March. By 2030, it'll be in January.
Worse: the exact start date depends on moon sighting, which varies by location and religious authority. Two countries may celebrate Eid on different days.
The Data Structure#
Holiday data from an API typically includes:
{
"location": "DE-BY",
"period": { "year": 2026 },
"holidays": [
{
"date": "2026-01-01",
"name": "New Year's Day",
"types": ["public"]
},
{
"date": "2026-01-06",
"name": "Epiphany",
"types": ["public"]
},
{
"date": "2026-04-03",
"name": "Good Friday",
"types": ["public"]
}
]
}
Key fields:
date— ISO 8601 date stringname— Holiday name (typically in English or local language)types— Categories:public,bank,school,observance, etc.
Fetching Holiday Data#
curl#
# Get all public holidays for Germany in 2026
curl -H "X-API-Key: YOUR_API_KEY" \
"https://worlddataapi.com/v1/holidays/DE?year=2026&type=public"
# Get holidays for a specific region (Bavaria)
curl -H "X-API-Key: YOUR_API_KEY" \
"https://worlddataapi.com/v1/holidays/DE-BY?year=2026"
# Get holidays within a date range
curl -H "X-API-Key: YOUR_API_KEY" \
"https://worlddataapi.com/v1/holidays/JP?start=2026-04-01&end=2026-04-30"
JavaScript#
async function getHolidays(location, year) {
const response = await fetch(
`https://worlddataapi.com/v1/holidays/${location}?year=${year}`,
{
headers: {
"X-API-Key": process.env.WORLD_DATA_API_KEY,
},
},
);
if (!response.ok) {
throw new Error(`Failed to fetch holidays: ${response.status}`);
}
return response.json();
}
// Get US federal holidays for 2026
const usHolidays = await getHolidays("US", 2026);
// Get California holidays (includes state holidays)
const caHolidays = await getHolidays("US-CA", 2026);
// Get Japanese holidays
const jpHolidays = await getHolidays("JP", 2026);
Python#
import requests
import os
def get_holidays(location: str, year: int) -> dict:
response = requests.get(
f"https://worlddataapi.com/v1/holidays/{location}",
params={"year": year},
headers={"X-API-Key": os.environ['WORLD_DATA_API_KEY']}
)
response.raise_for_status()
return response.json()
# Get German holidays (federal)
de_holidays = get_holidays("DE", 2026)
# Get Bavarian holidays (includes state holidays)
bavaria_holidays = get_holidays("DE-BY", 2026)
Filtering by Holiday Type#
Not all holidays mean "day off." Observances (Mother's Day, Earth Day) aren't public holidays. Bank holidays may differ from public holidays.
// Only public holidays (actual days off)
const response = await fetch(
`https://worlddataapi.com/v1/holidays/US?year=2026&type=public`,
{ headers: { "X-API-Key": API_KEY } },
);
// Only bank holidays (for financial applications)
const bankHolidays = await fetch(
`https://worlddataapi.com/v1/holidays/GB?year=2026&type=bank`,
{ headers: { "X-API-Key": API_KEY } },
);
Holiday types:
public— Official day off for most workersbank— Financial institutions closedschool— School holiday (not necessarily a public holiday)government— Government offices closedobservance— Notable day, not an official holidayoptional— Employer's discretion
Querying Date Ranges#
For trip planning or project scheduling, query a specific date range instead of a full year:
// Get holidays during a trip to Japan
const response = await fetch(
"https://worlddataapi.com/v1/holidays/JP?start=2026-04-01&end=2026-04-30",
{ headers: { "X-API-Key": API_KEY } },
);
const data = await response.json();
// Returns Golden Week holidays in late April
Finding the Next Holiday#
For dashboards and countdowns:
async function getNextHoliday(location) {
const response = await fetch(
`https://worlddataapi.com/v1/holidays/${location}/next`,
{ headers: { "X-API-Key": API_KEY } },
);
return response.json();
}
const next = await getNextHoliday("US");
console.log(`Next holiday: ${next.holiday.name} in ${next.days_until} days`);
// "Next holiday: Memorial Day in 45 days"
Caching Strategy#
Holiday data is stable — it rarely changes mid-year. Cache aggressively:
const holidayCache = new Map();
async function getCachedHolidays(location, year) {
const key = `${location}:${year}`;
if (holidayCache.has(key)) {
return holidayCache.get(key);
}
const data = await getHolidays(location, year);
holidayCache.set(key, data);
return data;
}
For most applications, cache holiday data for 24 hours or longer. Refresh annually or when users report discrepancies.
Creating a Holiday Set for Fast Lookups#
async function getHolidaySet(location, year) {
const data = await getCachedHolidays(location, year);
return new Set(data.holidays.map((h) => h.date));
}
const usHolidays2026 = await getHolidaySet("US", 2026);
function isHoliday(dateString) {
return usHolidays2026.has(dateString);
}
console.log(isHoliday("2026-01-01")); // true (New Year's Day)
console.log(isHoliday("2026-01-02")); // false
Handling Multiple Countries#
For applications supporting users in multiple countries:
class HolidayService {
constructor(apiKey) {
this.apiKey = apiKey;
this.cache = new Map();
}
async getHolidays(location, year) {
const key = `${location}:${year}`;
if (!this.cache.has(key)) {
const response = await fetch(
`https://worlddataapi.com/v1/holidays/${location}?year=${year}`,
{ headers: { "X-API-Key": this.apiKey } },
);
const data = await response.json();
this.cache.set(key, new Set(data.holidays.map((h) => h.date)));
}
return this.cache.get(key);
}
async isHoliday(location, date) {
const year = new Date(date).getFullYear();
const holidays = await this.getHolidays(location, year);
return holidays.has(date);
}
async isBusinessDay(location, date) {
const d = new Date(date);
const dayOfWeek = d.getDay();
// Basic weekday check (doesn't handle non-Western workweeks)
if (dayOfWeek === 0 || dayOfWeek === 6) {
return false;
}
return !(await this.isHoliday(location, date));
}
}
For accurate business day calculations including non-Western workweeks (Israel, UAE), use the business days endpoints instead.
Displaying Holidays in UI#
Simple List#
async function renderHolidayList(location, year) {
const data = await getHolidays(location, year);
return data.holidays
.map(
(h) => `
<li>
<time datetime="${h.date}">${formatDate(h.date)}</time>
<span>${h.name}</span>
</li>
`,
)
.join("");
}
function formatDate(isoDate) {
return new Date(isoDate).toLocaleDateString("en-US", {
month: "long",
day: "numeric",
});
}
Grouped by Month#
function groupByMonth(holidays) {
const grouped = {};
for (const holiday of holidays) {
const month = holiday.date.substring(0, 7); // "2026-01"
if (!grouped[month]) {
grouped[month] = [];
}
grouped[month].push(holiday);
}
return grouped;
}
const data = await getHolidays("US", 2026);
const byMonth = groupByMonth(data.holidays);
// { "2026-01": [...], "2026-02": [...], ... }
Regional Holidays: Country vs Subdivision#
Country-level queries return only national holidays. Regional queries include both national and regional holidays:
// National German holidays only
const national = await getHolidays("DE", 2026);
console.log(national.holidays.length); // 9
// Bavaria includes state holidays
const bavaria = await getHolidays("DE-BY", 2026);
console.log(bavaria.holidays.length); // 13
Use ISO 3166-2 codes for regions: US-CA (California), DE-BY (Bavaria), GB-SCT (Scotland).
For applications where users select their location, let them choose the region:
// User in Munich should use DE-BY, not DE
const userHolidays = await getHolidays(user.region || user.country, 2026);
Error Handling#
async function safeGetHolidays(location, year) {
try {
const response = await fetch(
`https://worlddataapi.com/v1/holidays/${location}?year=${year}`,
{
headers: { "X-API-Key": API_KEY },
signal: AbortSignal.timeout(5000),
},
);
if (response.status === 400) {
throw new Error(`Invalid location code: ${location}`);
}
if (response.status === 401) {
throw new Error("Invalid API key");
}
if (!response.ok) {
throw new Error(`API error: ${response.status}`);
}
return await response.json();
} catch (error) {
if (error.name === "AbortError") {
throw new Error("Request timed out");
}
throw error;
}
}
Common Pitfalls#
Using Country Codes Instead of Region Codes#
A common mistake is fetching holidays for DE (Germany) when your users are in Bavaria, which has additional state holidays. Always use the most specific location code available:
// Wrong: misses 4 Bavarian state holidays
const holidays = await getHolidays("DE", 2026);
// Correct: includes all holidays for Bavaria
const holidays = await getHolidays("DE-BY", 2026);
Not Filtering by Holiday Type#
Fetching all holidays without filtering returns observances (Mother's Day, Earth Day) that are not days off. This leads to incorrect business day calculations:
// Wrong: includes observances that are not days off
const holidays = await getHolidays("US", 2026);
// Correct: only public holidays that affect work schedules
const holidays = await fetch(
`https://worlddataapi.com/v1/holidays/US?year=2026&type=public`,
{ headers: { "X-API-Key": API_KEY } },
);
Ignoring Year Boundaries#
When calculating dates that span years (e.g., December 28 + 10 business days), you need holiday data for both years:
// Wrong: misses New Year's Day if calculation crosses into 2027
const holidays2026 = await getHolidays("US", 2026);
// Correct: fetch both years when calculations may cross year boundaries
const [holidays2026, holidays2027] = await Promise.all([
getHolidays("US", 2026),
getHolidays("US", 2027),
]);
Caching Without Invalidation Strategy#
Holiday data is stable but not immutable. Governments occasionally add holidays mid-year. Cache for 24 hours minimum, but implement a way to force refresh when users report discrepancies.
Assuming Monday-Friday Workweek#
Some countries (Israel, UAE, parts of the Middle East) have different workweeks. The isBusinessDay example in this guide assumes Monday-Friday. For accurate global business day calculations, use the dedicated business days endpoint.
Limitations#
Islamic Holidays#
World Data API calculates Islamic holidays astronomically. In countries that determine holidays by physical moon sighting, actual dates may differ by 1-2 days. For applications where exact Islamic holiday dates are critical, verify with local religious authorities.
Half-Day Holidays#
Some countries observe half-day holidays (Christmas Eve in parts of Europe). The API treats these as full holidays. If you need half-day precision, check local employment law.
Newly Announced Holidays#
Governments occasionally declare holidays with short notice (national mourning, celebrations). The API updates reflect official announcements but may lag breaking news.
Use Cases#
Payroll Systems#
async function getPayrollHolidays(country, region, year) {
// Use regional data for accurate holiday count
const location = region || country;
const data = await getHolidays(location, year);
// Filter to public holidays that affect payroll
return data.holidays.filter(
(h) => h.types.includes("public") || h.types.includes("bank"),
);
}
E-commerce Delivery Estimates#
async function getDeliveryDate(origin, destination, shippingDays) {
// Account for holidays in both origin and destination
const [originData, destData] = await Promise.all([
getHolidays(origin, 2026),
getHolidays(destination, 2026),
]);
// Use business days endpoint for accurate calculation
// See: /guides/delivery-date-calculation
}
Calendar Applications#
async function getCalendarEvents(userLocation, year) {
const data = await getHolidays(userLocation, year);
return data.holidays.map((h) => ({
title: h.name,
start: h.date,
allDay: true,
type: h.types[0],
className: `holiday-${h.types[0]}`,
}));
}
Summary#
Implementing international holiday support requires handling moving dates, regional variations, and calendar system differences that make hardcoding impractical. Key takeaways:
Use an API instead of hardcoding — Holiday rules are complex and change over time
Specify the region, not the country — Regional holidays vary significantly (Bavaria has 4 more holidays than federal Germany)
Filter by holiday type — Distinguish between public holidays (days off) and observances
Cache aggressively — Holiday data changes infrequently; cache for 24+ hours
Handle year boundaries — Calculations spanning years need data from both years
For business day calculations that account for holidays and non-Western workweeks, see the business days guide.
Ready to add international holiday support to your application? Get your free API key and start building. The free tier includes 60 requests per day — enough to prototype and test your integration.
Related guides:
Building a Holiday Calendar Component in React — UI implementation
Handling Islamic Holidays in Software — Lunar calendar challenges
Calculating Delivery Dates with Holiday Awareness — E-commerce applications