feriados.io ← Inicio
apienglishbusiness-dayslatamcalendars

Custom Business Calendars via API: Company-Specific Non-Working Days

How to manage company-specific non-working days — plant shutdowns, collective vacations, trade fairs — via API and apply them to any business day calculation in LATAM.

4 de abril de 2026

Public holidays in Chile, Colombia, or Mexico are well-documented and updated by someone else. Your company’s own non-working days are not. Annual plant maintenance, industry trade fairs, collective vacation periods, end-of-year shutdowns — these need to live somewhere, and in most companies that somewhere is a hardcoded array, an environment variable, or a spreadsheet that HR updates once a year and nobody else remembers to check.

The result is predictable: a cron job that fires during a plant shutdown, a delivery date that lands on collective vacations, an SLA deadline calculated without accounting for a company-wide closure.

The standard approach and why it breaks

Most teams end up with something like this:

javascript The classic approach
const COMPANY_CLOSURES = [
  "2026-07-15", // Annual maintenance
  "2026-12-26", // Year-end closure
  "2026-12-27",
  "2026-12-28",
];

function isWorkingDay(date, country) { if (COMPANY_CLOSURES.includes(date)) return false; // … official holiday logic }

The problem is not the logic — it’s the coupling. These dates live in the codebase, which means:

Updating next year's closures requires a deploy

Multiple services (backend, workers, payroll scripts) each keep their own copy that diverges over time

There's no clear audit trail for changes

The array needs to be duplicated for each country your operations cover

An environment variable or internal database doesn’t solve the coordination problem — it just moves it.

A cleaner approach: custom calendars via API

The idea: define a calendar with a name and a slug, add your company’s non-working dates to it, then use ?calendar=slug as an overlay parameter on any business day calculation. Official country holidays still apply — your calendar adds on top.

Create the calendar

bash Create a new custom calendar
curl -X POST https://api.feriados.io/v1/calendars \
  -H "Authorization: Bearer frd_your_key" \
  -H "Content-Type: application/json" \
  -d '{"name": "Plant North", "slug": "plant-north"}'
json Respuesta
{
  "success": true,
  "data": {
    "id": "01HZ...",
    "name": "Plant North",
    "slug": "plant-north",
    "created_at": "2026-04-04T12:00:00.000Z"
  }
}

Add non-working dates

bash Add closure dates to the calendar
curl -X POST https://api.feriados.io/v1/calendars/plant-north/dates \
  -H "Authorization: Bearer frd_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "dates": [
      { "date": "2026-07-15", "name": "Annual maintenance" },
      { "date": "2026-12-26", "name": "Year-end closure" },
      { "date": "2026-12-27", "name": "Year-end closure" },
      { "date": "2026-12-28", "name": "Year-end closure" }
    ]
  }'

Up to 100 dates per request. Duplicates are silently ignored, so running the same script twice is safe.

Use the calendar in calculations

Once the calendar is set up, any business day endpoint accepts ?calendar=slug:

bash Is July 15th a working day at Plant North?
curl "https://api.feriados.io/v1/CL/is-business-day?date=2026-07-15&calendar=plant-north" \
  -H "Authorization: Bearer frd_your_key"
json Respuesta
{
  "success": true,
  "data": {
    "date": "2026-07-15",
    "is_business_day": false,
    "day_of_week": "Wednesday",
    "region": null
  },
  "meta": { "country": "CL", "calendar": "plant-north", "total": 1 }
}

Wednesday, July 15 has no official Chilean holiday — but the API returns false because it’s registered as a plant closure. Without ?calendar, it would return true.

Practical examples

Cron jobs that skip company shutdowns

javascript Skip payroll processing during plant shutdowns
const API = "https://api.feriados.io/v1";
const HEADERS = { Authorization: `Bearer ${process.env.FERIADOS_API_KEY}` };

async function isWorkingDayToday(country, calendar) { const today = new Date().toISOString().slice(0, 10); const url = ${API}/${country}/is-business-day?date=${today}&calendar=${calendar}; const { data } = await fetch(url, { headers: HEADERS }).then(r => r.json()); return data.is_business_day; }

if (await isWorkingDayToday(“CL”, “plant-north”)) { await processPayroll(); } else { console.log(“Plant closed — skipping payroll run”); }

Delivery dates that avoid company closures

javascript Order placed July 14 — 3 business days, skipping July 15 maintenance
async function estimatedDelivery(country, calendar, businessDays = 3) {
  const today = new Date().toISOString().slice(0, 10);
  const url = `${API}/${country}/business-days/add?date=${today}&days=${businessDays}&calendar=${calendar}`;
  const { data } = await fetch(url, { headers: HEADERS }).then(r => r.json());
  return data.result_date;
}

const delivery = await estimatedDelivery(“CL”, “plant-north”, 3); // → “2026-07-21” instead of “2026-07-17”

SLA deadlines that account for operational closures

javascript 5-day SLA opened July 13, respecting plant closure on July 15
async function slaDeadline(country, calendar, openedAt, slaDays) {
  const url = `${API}/${country}/business-days/add?date=${openedAt}&days=${slaDays}&calendar=${calendar}`;
  const { data } = await fetch(url, { headers: HEADERS }).then(r => r.json());
  return data.result_date;
}

const deadline = await slaDeadline(“CL”, “plant-north”, “2026-07-13”, 5); // → “2026-07-22” instead of “2026-07-20”

Multiple operations, multiple calendars

If your company operates in multiple countries or has plants with different schedules, use one calendar per context:

javascript One calendar per plant — same calculation logic
const CALENDARS = {
  CL: "plant-santiago",
  CO: "plant-bogota",
  PE: "plant-lima",
};

async function calculateDeadline(country, date, days) { const cal = CALENDARS[country]; const params = cal ? &calendar=${cal} : ""; const url = ${API}/${country}/business-days/add?date=${date}&days=${days}${params}; const { data } = await fetch(url, { headers: HEADERS }).then(r => r.json()); return data.result_date; }

Each plant has its own closure schedule. The calculation logic stays the same — only the calendar slug changes.

Keeping calendars up to date

At the start of each year, or whenever closures are confirmed, an upsert script is enough. Duplicates are ignored, so running it multiple times is safe:

bash Add next year's closures (safe to run multiple times)
curl -X POST https://api.feriados.io/v1/calendars/plant-north/dates \
  -H "Authorization: Bearer frd_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "dates": [
      { "date": "2027-07-14", "name": "Annual maintenance 2027" },
      { "date": "2027-12-26", "name": "Year-end closure 2027" },
      { "date": "2027-12-27", "name": "Year-end closure 2027" }
    ]
  }'
bash Delete a date if it changes, then re-add the corrected one
curl -X DELETE https://api.feriados.io/v1/calendars/plant-north/dates/2027-07-14 \
  -H "Authorization: Bearer frd_your_key"

No deploys, no environment variable updates, no cross-team coordination.

Plan limits

Custom calendars by plan

The ?calendar=slug overlay works on all five business day endpoints

Plan Calendars Future dates per calendar
Free 1 10
Starter 3 Unlimited
Team 10 Unlimited
Business Unlimited Unlimited

Custom calendars are available from the Starter plan

The Free plan includes 1 calendar with up to 10 future dates to test the feature.

Get your free API key →

See also

Integra feriados.io en tu proyecto

API key gratis en 30 segundos. Sin tarjeta. 11 países de Latinoamérica, siempre actualizada con los feriados oficiales.

← Ver todos los artículos