Handling Public Holidays in API Integrations
Patterns and best practices for building holiday-aware backend systems.
The Problem with Ignoring Holidays
Backend systems that process scheduled tasks, trigger notifications, or calculate deadlines frequently need to know whether a given day is a working day. Ignoring public holidays leads to concrete problems: an automated payment processor tries to settle on Christmas Day; a notification system sends SLA breach alerts for days that should not have counted; a scheduling service books a delivery for a day when warehouses are closed.
These are not edge cases. Any system that deals with business days across countries will encounter them. The question is whether you handle them proactively in your architecture or reactively through bug reports.
Architecture Patterns for Holiday Handling
There are three common patterns for integrating holiday awareness into backend systems. Each has trade-offs around complexity, accuracy, and maintenance burden.
Pattern 1: Static Holiday Lists
The simplest approach is maintaining a database table or configuration file with holiday dates. You populate it manually or from a CSV, and your application queries it when performing date calculations.
CREATE TABLE holidays (
country_code CHAR(2) NOT NULL,
holiday_date DATE NOT NULL,
name VARCHAR(100),
PRIMARY KEY (country_code, holiday_date)
);
-- Your application queries:
SELECT 1 FROM holidays
WHERE country_code = 'US'
AND holiday_date = '2026-12-25';This works initially but has significant maintenance costs. Someone must update the table every year for every supported country. Moving holidays (Easter, Lunar New Year, various national observances) require annual recalculation. If you support 10 countries, you are maintaining 10 separate holiday calendars, each with different rules.
Pattern 2: Holiday Calculation Library
Libraries that calculate holiday dates algorithmically solve the annual update problem for holidays with fixed rules. Python’s holidays library and similar packages in other languages can generate holiday dates for a given country and year.
The limitation is that these libraries are language-specific. If your backend is a mix of Node.js microservices, a Python data pipeline, and a Go scheduler, you need separate library integrations for each. You also depend on the library maintainers to keep up with government changes to holiday schedules.
Pattern 3: External Holiday API
Delegating holiday awareness to a dedicated API service decouples holiday data from your application code. Your backend makes an HTTP call to check dates, and the service handles data accuracy and updates. This pattern works across all languages and services in your architecture.
const API_KEY = process.env.BIZDAY_API_KEY;
async function getNextBusinessDay(date, country) {
const res = await fetch(
`https://api.bizday.dev/v1/next?date=${date}&country=${country}`,
{ headers: { Authorization: `Bearer ${API_KEY}` } }
);
const json = await res.json();
return json.data.workday;
}
// Schedule a task for the next business day
async function scheduleTask(taskId, country) {
const today = new Date().toISOString().slice(0, 10);
const nextDay = await getNextBusinessDay(today, country);
await db.query(
'UPDATE tasks SET scheduled_date = $1 WHERE id = $2',
[nextDay, taskId]
);
}Caching Strategies
When integrating with an external holiday API, caching is essential for both performance and resilience. Holiday data changes infrequently (typically once per year per country), so aggressive caching is appropriate.
Response-Level Caching
Cache individual API responses for business day checks. Since a date is either a business day or it is not, and this fact does not change once the holiday calendar for that year is set, you can cache responses for long periods:
const cache = new Map();
async function isBusinessDay(date, country) {
const key = `${country}:${date}`;
if (cache.has(key)) {
return cache.get(key);
}
const res = await fetch(
`https://api.bizday.dev/v1/check?date=${date}&country=${country}`,
{ headers: { Authorization: `Bearer ${API_KEY}` } }
);
const json = await res.json();
const result = json.data.is_workday;
cache.set(key, result);
return result;
}In production, replace the in-memory Map with Redis or Memcached and set a TTL of 24 hours or longer. For dates in the current year, the result is effectively permanent once the year’s holiday calendar is published.
Batch Prefetching
If your system processes dates in bulk (such as generating a monthly invoice schedule), use the batch endpoint to prefetch all dates at once and store the results:
async function prefetchMonth(year, month, country) {
// Generate all dates in the month
const dates = [];
const daysInMonth = new Date(year, month, 0).getDate();
for (let d = 1; d <= daysInMonth; d++) {
const dateStr = `${year}-${String(month).padStart(2, '0')}-${String(d).padStart(2, '0')}`;
dates.push(dateStr);
}
const res = await fetch('https://api.bizday.dev/v1/batch', {
method: 'POST',
headers: {
Authorization: `Bearer ${API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
operation: 'check',
country,
dates,
}),
});
const json = await res.json();
// Cache all results
for (const result of json.data.results) {
cache.set(`${country}:${result.date}`, result.is_workday);
}
}Multi-Country Support
One of the most challenging aspects of holiday handling is supporting multiple countries simultaneously. Consider an international logistics platform where the origin country, transit country, and destination country may all have different holiday calendars.
async function calculateShippingDeadline(
originCountry,
destCountry,
orderDate,
processingDays,
transitDays
) {
// Processing happens in the origin country
const shipDate = await addBusinessDays(orderDate, processingDays, originCountry);
// Transit uses calendar days (carriers operate on their own schedule)
const transitDate = new Date(shipDate);
transitDate.setDate(transitDate.getDate() + transitDays);
// Delivery only happens on business days in the destination country
const deliveryDateStr = transitDate.toISOString().slice(0, 10);
const isDeliveryBusinessDay = await isBusinessDay(deliveryDateStr, destCountry);
if (!isDeliveryBusinessDay) {
// Push to next business day in destination country
return getNextBusinessDay(deliveryDateStr, destCountry);
}
return deliveryDateStr;
}Without an API that handles country-specific holidays, implementing this logic requires maintaining holiday calendars for every country in your supply chain. With the BizDay API, the country parameter on each call handles this naturally.
Error Handling and Resilience
Any external API dependency requires proper error handling. Here are patterns for building resilient holiday-aware systems:
Fallback to Weekend-Only
If the holiday API is temporarily unavailable, falling back to weekend-only calculations is better than failing entirely:
async function isBusinessDayResilient(date, country) {
try {
const res = await fetch(
`https://api.bizday.dev/v1/check?date=${date}&country=${country}`,
{
headers: { Authorization: `Bearer ${API_KEY}` },
signal: AbortSignal.timeout(3000), // 3s timeout
}
);
if (!res.ok) throw new Error(`API returned ${res.status}`);
const json = await res.json();
return json.data.is_workday;
} catch (error) {
console.warn('Holiday API unavailable, falling back to weekend check');
const dayOfWeek = new Date(date).getDay();
return dayOfWeek !== 0 && dayOfWeek !== 6;
}
}Circuit Breaker Pattern
For high-traffic systems, implement a circuit breaker that stops making API calls after repeated failures, automatically switching to the fallback. Re-test the API periodically and resume normal operation once it recovers. This prevents cascading failures and reduces unnecessary timeout delays.
Testing Holiday Logic
Testing business day calculations requires specific attention because bugs often only surface on actual holiday dates. Some recommendations:
- Test with known holidays: Write test cases using dates you know are holidays (Christmas, New Year) to verify your integration handles them correctly.
- Test year boundaries: December 31 to January 1 transitions across years are a common source of bugs, especially when caching by year.
- Test multiple countries: If your system supports the US and UK, verify that US holidays do not affect UK calculations and vice versa.
- Test the fallback path: Simulate API failures to ensure your fallback logic works correctly.
Summary
Handling public holidays in backend systems is an infrastructure concern, not a one-time feature. The right approach depends on your scale: static lists work for single-country, low-complexity systems; calculation libraries work for single-language stacks; and an external API like BizDay works best for multi-country, multi-service architectures where you want holiday accuracy without the maintenance overhead.
The BizDay API offers a free tier with 10,000 requests per month. Get started by creating your API key or browsing the documentation for integration examples in multiple languages.