API reference

Base URL https://dierum.io. Every endpoint returns JSON over HTTPS. Examples in curl, Python, JavaScript, and C#. The language tabs stay in sync as you read.

Authentication

Pass your key in the X-Api-Key header. A key takes one signup with an email address (free tier included) and is shown once, at creation. Lost keys are replaced, not recovered: POST /v1/rotate with your email revokes the old key and mails a new one.

curl "https://dierum.io/v1/check?region=US&date=2027-11-25" -H "X-Api-Key: $KEY"
import requests

r = requests.get("https://dierum.io/v1/check",
    headers={"X-Api-Key": KEY},
    params={'region': 'US', 'date': '2027-11-25'})
r.raise_for_status()
print(r.json())
const r = await fetch("https://dierum.io/v1/check?region=US&date=2027-11-25", {
  headers: { "X-Api-Key": KEY },
});
if (!r.ok) throw new Error(`dierum: ${r.status}`);
console.log(await r.json());
using var http = new HttpClient();
http.DefaultRequestHeaders.Add("X-Api-Key", key);

var json = await http.GetStringAsync(
    "https://dierum.io/v1/check?region=US&date=2027-11-25");
Console.WriteLine(json);

Where headers are awkward, as with calendar feeds and spreadsheet imports, ?api_key= works as a query parameter. Treat URLs containing it as secrets: they end up in logs.

Agents: a machine-readable spec lives at /openapi.json and a compact plain-text surface at /llms.txt. Signup, keys, and every endpoint are fully self-serve; no human steps anywhere in the flow.

Keys come in two kinds. Your secret key (ck_...) belongs on servers and never in a browser. For client-side code and widgets, mint a publishable key: origin-locked, read-only, and safe to ship. CORS is open on the whole API; the key is the gate.

Limits

PlanRequests / dayPrice
Free100$0
Developer1,000$7 / month
Starter10,000$15 / month
Pro100,000$49 / month
Business500,000$149 / month
Enterprisenegotiatedfrom $500 / month

Every response carries X-RateLimit-Limit and X-RateLimit-Used. Limits reset at midnight UTC. Requests over the limit return 429 and are not counted against you. Batch operations meter per inner operation.

Errors

Errors are { "error": { "code", "message" } } with a matching HTTP status. The codes worth handling:

StatusCodeMeaning
400bad_date · bad_terms · bad_ruleMalformed input. Dates are yyyy-MM-dd.
401missing_key · invalid_keyNo key, or a revoked one.
404unknown_regionNot in /v1/regions.
422out_of_rangeThe result would leave 2020–2035. We refuse rather than guess.
429quota_exceededDaily limit reached.

GET /v1/check

Classify one date: business day, weekend, or holiday, with the next business day included.

ParameterNotes
regionISO 3166 code, subdivision, or market · see /v1/regions
dateyyyy-MM-dd
curl "https://dierum.io/v1/check?region=US&date=2027-11-25" -H "X-Api-Key: $KEY"
import requests

r = requests.get("https://dierum.io/v1/check",
    headers={"X-Api-Key": KEY},
    params={'region': 'US', 'date': '2027-11-25'})
r.raise_for_status()
print(r.json())
const r = await fetch("https://dierum.io/v1/check?region=US&date=2027-11-25", {
  headers: { "X-Api-Key": KEY },
});
if (!r.ok) throw new Error(`dierum: ${r.status}`);
console.log(await r.json());
using var http = new HttpClient();
http.DefaultRequestHeaders.Add("X-Api-Key", key);

var json = await http.GetStringAsync(
    "https://dierum.io/v1/check?region=US&date=2027-11-25");
Console.WriteLine(json);

Response

{ "region": "US", "date": "2027-11-25", "business_day": false,
  "weekend": false, "holiday": true, "holiday_name": "Thanksgiving",
  "next_business_day": "2027-11-26" }

GET /v1/add

Add or subtract business days. days may be negative; zero returns the start date unchanged. The primitive under settlement dates and simple deadlines.

ParameterNotes
regioncalendar to use · XNYS here, so Good Friday counts as closed
startyyyy-MM-dd
days-5000 to 5000
curl "https://dierum.io/v1/add?region=XNYS&start=2027-03-25&days=2" -H "X-Api-Key: $KEY"
import requests

r = requests.get("https://dierum.io/v1/add",
    headers={"X-Api-Key": KEY},
    params={'region': 'XNYS', 'start': '2027-03-25', 'days': '2'})
r.raise_for_status()
print(r.json())
const r = await fetch("https://dierum.io/v1/add?region=XNYS&start=2027-03-25&days=2", {
  headers: { "X-Api-Key": KEY },
});
if (!r.ok) throw new Error(`dierum: ${r.status}`);
console.log(await r.json());
using var http = new HttpClient();
http.DefaultRequestHeaders.Add("X-Api-Key", key);

var json = await http.GetStringAsync(
    "https://dierum.io/v1/add?region=XNYS&start=2027-03-25&days=2");
Console.WriteLine(json);

Response

{ "region": "XNYS", "start": "2027-03-25", "days": 2, "result": "2027-03-30" }

GET /v1/count

Business days between two dates, inclusive of both ends. Negative when to precedes from, so count(a,b) = -count(b,a) always holds.

ParameterNotes
regionsubdivisions carry their own holidays · Bavaria observes Epiphany; Berlin does not
from, toyyyy-MM-dd
curl "https://dierum.io/v1/count?region=DE-BY&from=2027-01-01&to=2027-01-31" -H "X-Api-Key: $KEY"
import requests

r = requests.get("https://dierum.io/v1/count",
    headers={"X-Api-Key": KEY},
    params={'region': 'DE-BY', 'from': '2027-01-01', 'to': '2027-01-31'})
r.raise_for_status()
print(r.json())
const r = await fetch("https://dierum.io/v1/count?region=DE-BY&from=2027-01-01&to=2027-01-31", {
  headers: { "X-Api-Key": KEY },
});
if (!r.ok) throw new Error(`dierum: ${r.status}`);
console.log(await r.json());
using var http = new HttpClient();
http.DefaultRequestHeaders.Add("X-Api-Key", key);

var json = await http.GetStringAsync(
    "https://dierum.io/v1/count?region=DE-BY&from=2027-01-01&to=2027-01-31");
Console.WriteLine(json);

Response

{ "region": "DE-BY", "from": "2027-01-01", "to": "2027-01-31", "business_days": 19 }

GET /v1/due-date

Invoice date plus payment terms, rolled forward to a real working day when the nominal date lands on a weekend or holiday. Terms grammar: net30 (calendar days), net10b (business days), eom, eom+15.

ParameterNotes
invoice_dateyyyy-MM-dd
termsnet<n>, net<n>b, eom, eom+<n>
curl "https://dierum.io/v1/due-date?region=US&invoice_date=2027-01-15&terms=net30" -H "X-Api-Key: $KEY"
import requests

r = requests.get("https://dierum.io/v1/due-date",
    headers={"X-Api-Key": KEY},
    params={'region': 'US', 'invoice_date': '2027-01-15', 'terms': 'net30'})
r.raise_for_status()
print(r.json())
const r = await fetch("https://dierum.io/v1/due-date?region=US&invoice_date=2027-01-15&terms=net30", {
  headers: { "X-Api-Key": KEY },
});
if (!r.ok) throw new Error(`dierum: ${r.status}`);
console.log(await r.json());
using var http = new HttpClient();
http.DefaultRequestHeaders.Add("X-Api-Key", key);

var json = await http.GetStringAsync(
    "https://dierum.io/v1/due-date?region=US&invoice_date=2027-01-15&terms=net30");
Console.WriteLine(json);

Response

{ "invoice_date": "2027-01-15", "terms": "net30",
  "nominal_due": "2027-02-14", "due_date": "2027-02-16", "rolled": true }

GET /v1/paydays

A year of pay dates from a rule. Dates landing on weekends or holidays roll backward: payroll pays early, never late. December 2027 in the US is the canonical trap: the 31st is New Year's Day observed.

ParameterNotes
year2020–2035
rulelast-business-day · nth:<1-28> · semi:<day>,<day|last>
curl "https://dierum.io/v1/paydays?region=US&year=2027&rule=semi:15,last" -H "X-Api-Key: $KEY"
import requests

r = requests.get("https://dierum.io/v1/paydays",
    headers={"X-Api-Key": KEY},
    params={'region': 'US', 'year': '2027', 'rule': 'semi:15,last'})
r.raise_for_status()
print(r.json())
const r = await fetch("https://dierum.io/v1/paydays?region=US&year=2027&rule=semi:15,last", {
  headers: { "X-Api-Key": KEY },
});
if (!r.ok) throw new Error(`dierum: ${r.status}`);
console.log(await r.json());
using var http = new HttpClient();
http.DefaultRequestHeaders.Add("X-Api-Key", key);

var json = await http.GetStringAsync(
    "https://dierum.io/v1/paydays?region=US&year=2027&rule=semi:15,last");
Console.WriteLine(json);

Response

{ "region": "US", "year": 2027, "rule": "semi:15,last",
  "paydays": ["2027-01-15", "2027-01-29", "...", "2027-12-15", "2027-12-30"] }

GET /v1/common-days

Days that are business days in every region listed. The question behind cross-border settlement, international support coverage, and shipping cutoffs. A memorable case: a US–Saudi pair shares only Monday to Thursday even in a quiet week.

ParameterNotes
regions2–5 comma-separated codes
from, toat most 92 days apart
curl "https://dierum.io/v1/common-days?regions=US,DE&from=2027-05-28&to=2027-06-01" -H "X-Api-Key: $KEY"
import requests

r = requests.get("https://dierum.io/v1/common-days",
    headers={"X-Api-Key": KEY},
    params={'regions': 'US,DE', 'from': '2027-05-28', 'to': '2027-06-01'})
r.raise_for_status()
print(r.json())
const r = await fetch("https://dierum.io/v1/common-days?regions=US,DE&from=2027-05-28&to=2027-06-01", {
  headers: { "X-Api-Key": KEY },
});
if (!r.ok) throw new Error(`dierum: ${r.status}`);
console.log(await r.json());
using var http = new HttpClient();
http.DefaultRequestHeaders.Add("X-Api-Key", key);

var json = await http.GetStringAsync(
    "https://dierum.io/v1/common-days?regions=US,DE&from=2027-05-28&to=2027-06-01");
Console.WriteLine(json);

Response

{ "regions": ["US", "DE"], "from": "2027-05-28", "to": "2027-06-01",
  "common_business_days": ["2027-05-28", "2027-06-01"] }

GET /v1/year

A year at a glance: total business days, per-month counts, and the full holiday list. Capacity planning and forecasting input.

ParameterNotes
year2020–2035
curl "https://dierum.io/v1/year?region=GB&year=2027" -H "X-Api-Key: $KEY"
import requests

r = requests.get("https://dierum.io/v1/year",
    headers={"X-Api-Key": KEY},
    params={'region': 'GB', 'year': '2027'})
r.raise_for_status()
print(r.json())
const r = await fetch("https://dierum.io/v1/year?region=GB&year=2027", {
  headers: { "X-Api-Key": KEY },
});
if (!r.ok) throw new Error(`dierum: ${r.status}`);
console.log(await r.json());
using var http = new HttpClient();
http.DefaultRequestHeaders.Add("X-Api-Key", key);

var json = await http.GetStringAsync(
    "https://dierum.io/v1/year?region=GB&year=2027");
Console.WriteLine(json);

Response

{ "region": "GB", "year": 2027, "business_days_total": 253,
  "business_days_per_month": [20, 20, 23, ...], "holidays_count": 10,
  "holidays": [{ "date": "2027-01-01", "name": "New Year's Day" }, ...] }

GET /v1/bridge-days

Windows where a little leave buys a long break, ranked by days off per day spent. Germany's best 2027 window: one day of leave around Easter yields five consecutive days off.

ParameterNotes
year2020–2035
ptoleave budget, 1–5 days
curl "https://dierum.io/v1/bridge-days?region=DE&year=2027&pto=1" -H "X-Api-Key: $KEY"
import requests

r = requests.get("https://dierum.io/v1/bridge-days",
    headers={"X-Api-Key": KEY},
    params={'region': 'DE', 'year': '2027', 'pto': '1'})
r.raise_for_status()
print(r.json())
const r = await fetch("https://dierum.io/v1/bridge-days?region=DE&year=2027&pto=1", {
  headers: { "X-Api-Key": KEY },
});
if (!r.ok) throw new Error(`dierum: ${r.status}`);
console.log(await r.json());
using var http = new HttpClient();
http.DefaultRequestHeaders.Add("X-Api-Key", key);

var json = await http.GetStringAsync(
    "https://dierum.io/v1/bridge-days?region=DE&year=2027&pto=1");
Console.WriteLine(json);

Response

{ "region": "DE", "year": 2027, "pto_budget": 1, "windows": [
  { "from": "2027-03-25", "to": "2027-03-29", "pto_days": 1,
    "total_days_off": 5, "pto_dates": ["2027-03-25"], "efficiency": 5.0 }, ...] }

GET /v1/next-holiday

Upcoming holidays with a countdown. Ops dashboards, deploy-freeze checks, or a desk widget.

ParameterNotes
count1–10, default 1
curl "https://dierum.io/v1/next-holiday?region=US&count=2" -H "X-Api-Key: $KEY"
import requests

r = requests.get("https://dierum.io/v1/next-holiday",
    headers={"X-Api-Key": KEY},
    params={'region': 'US', 'count': '2'})
r.raise_for_status()
print(r.json())
const r = await fetch("https://dierum.io/v1/next-holiday?region=US&count=2", {
  headers: { "X-Api-Key": KEY },
});
if (!r.ok) throw new Error(`dierum: ${r.status}`);
console.log(await r.json());
using var http = new HttpClient();
http.DefaultRequestHeaders.Add("X-Api-Key", key);

var json = await http.GetStringAsync(
    "https://dierum.io/v1/next-holiday?region=US&count=2");
Console.WriteLine(json);

Response

{ "region": "US", "as_of": "2026-08-31", "holidays": [
  { "date": "2026-09-07", "name": "Labor Day", "days_until": 7 }, ...] }

GET /v1/holidays

The raw holiday calendar for a region and range (up to three years), when you want the list itself.

ParameterNotes
from, torange up to 3 years
curl "https://dierum.io/v1/holidays?region=GB&from=2027-12-01&to=2027-12-31" -H "X-Api-Key: $KEY"
import requests

r = requests.get("https://dierum.io/v1/holidays",
    headers={"X-Api-Key": KEY},
    params={'region': 'GB', 'from': '2027-12-01', 'to': '2027-12-31'})
r.raise_for_status()
print(r.json())
const r = await fetch("https://dierum.io/v1/holidays?region=GB&from=2027-12-01&to=2027-12-31", {
  headers: { "X-Api-Key": KEY },
});
if (!r.ok) throw new Error(`dierum: ${r.status}`);
console.log(await r.json());
using var http = new HttpClient();
http.DefaultRequestHeaders.Add("X-Api-Key", key);

var json = await http.GetStringAsync(
    "https://dierum.io/v1/holidays?region=GB&from=2027-12-01&to=2027-12-31");
Console.WriteLine(json);

Response

{ "region": "GB", "holidays": [
  { "date": "2027-12-25", "name": "Christmas Day" },
  { "date": "2027-12-27", "name": "Christmas Day (observed)" }, ...] }

GET /v1/holidays.ics

The same calendar as an iCal feed. Subscribe from Google Calendar, Apple Calendar, or Outlook and holidays appear alongside your events. No key required: calendar apps can't send headers, and the data is open-licensed. Rate-limited per IP.

ParameterNotes
yeardefaults to the current year
curl "https://dierum.io/v1/holidays.ics?region=US&year=2027" -H "X-Api-Key: $KEY"
import requests

r = requests.get("https://dierum.io/v1/holidays.ics",
    headers={"X-Api-Key": KEY},
    params={'region': 'US', 'year': '2027'})
r.raise_for_status()
print(r.json())
const r = await fetch("https://dierum.io/v1/holidays.ics?region=US&year=2027", {
  headers: { "X-Api-Key": KEY },
});
if (!r.ok) throw new Error(`dierum: ${r.status}`);
console.log(await r.json());
using var http = new HttpClient();
http.DefaultRequestHeaders.Add("X-Api-Key", key);

var json = await http.GetStringAsync(
    "https://dierum.io/v1/holidays.ics?region=US&year=2027");
Console.WriteLine(json);

Response

BEGIN:VCALENDAR
VERSION:2.0
X-WR-CALNAME:Holidays US 2027
BEGIN:VEVENT
DTSTART;VALUE=DATE:20270101
SUMMARY:New Year's Day
...

POST /v1/batch

Up to 100 operations in one request (1,000 on Pro and above): add, count, check, or due-date. Each operation is metered as one request; results come back in order, with per-item errors instead of all-or-nothing failure.

ParameterNotes
operations[].opadd · count · check · due-date
operations[].regionany region code
operations[]. …parameters matching the single-call endpoint
curl -X POST "https://dierum.io/v1/batch" \
  -H "X-Api-Key: $KEY" -H "Content-Type: application/json" \
  -d '{"operations": [{"op": "add", "region": "XNYS", "start": "2027-03-25", "days": 2}, {"op": "due-date", "region": "US", "start": "2027-01-15", "terms": "net30"}]}'
import requests

r = requests.post("https://dierum.io/v1/batch",
    headers={"X-Api-Key": KEY},
    json={"operations": [{"op": "add", "region": "XNYS", "start": "2027-03-25", "days": 2}, {"op": "due-date", "region": "US", "start": "2027-01-15", "terms": "net30"}]})
r.raise_for_status()
print(r.json())
const r = await fetch("https://dierum.io/v1/batch", {
  method: "POST",
  headers: { "X-Api-Key": KEY, "Content-Type": "application/json" },
  body: JSON.stringify({"operations": [{"op": "add", "region": "XNYS", "start": "2027-03-25", "days": 2}, {"op": "due-date", "region": "US", "start": "2027-01-15", "terms": "net30"}]}),
});
if (!r.ok) throw new Error(`dierum: ${r.status}`);
console.log(await r.json());
using var http = new HttpClient();
http.DefaultRequestHeaders.Add("X-Api-Key", key);

var body = JsonContent.Create({"operations": [{"op": "add", "region": "XNYS", "start": "2027-03-25", "days": 2}, {"op": "due-date", "region": "US", "start": "2027-01-15", "terms": "net30"}]});
var res = await http.PostAsync("https://dierum.io/v1/batch", body);
res.EnsureSuccessStatusCode();
Console.WriteLine(await res.Content.ReadAsStringAsync());

Response

{ "count": 2, "results": [
  { "result": "2027-03-30" },
  { "due_date": "2027-02-16", "rolled": true } ] }

GET /v1/regions

Every supported region: countries, subdivisions, and financial markets. Each carries its own weekend definition, applied to calculations automatically. Mind the UK: bare GB holds only holidays common to the whole UK, so it has no Easter Monday and no summer bank holiday. For business-day math that matches England, Wales, Scotland, or Northern Ireland, use GB-ENG, GB-WLS, GB-SCT, or GB-NIR.

curl "https://dierum.io/v1/regions" -H "X-Api-Key: $KEY"
import requests

r = requests.get("https://dierum.io/v1/regions",
    headers={"X-Api-Key": KEY})
r.raise_for_status()
print(r.json())
const r = await fetch("https://dierum.io/v1/regions", {
  headers: { "X-Api-Key": KEY },
});
if (!r.ok) throw new Error(`dierum: ${r.status}`);
console.log(await r.json());
using var http = new HttpClient();
http.DefaultRequestHeaders.Add("X-Api-Key", key);

var json = await http.GetStringAsync(
    "https://dierum.io/v1/regions");
Console.WriteLine(json);

Response

{ "count": 1801, "regions": [
  { "code": "US", "country": "US", "subdiv": null, "kind": "country" },
  { "code": "US-CA", "country": "US", "subdiv": "CA", "kind": "subdivision" },
  { "code": "XNYS", "country": "XNYS", "subdiv": null, "kind": "financial" }, ...] }

GET /v1/diff

Day-type census for a range: how many business days, weekend days, and holidays between two dates. Capacity planning and prorating.

ParameterNotes
from, toup to 3 years apart
curl "https://dierum.io/v1/diff?region=DE-BY&from=2027-01-01&to=2027-03-31" -H "X-Api-Key: $KEY"
import requests

r = requests.get("https://dierum.io/v1/diff",
    headers={"X-Api-Key": KEY},
    params={'region': 'DE-BY', 'from': '2027-01-01', 'to': '2027-03-31'})
r.raise_for_status()
print(r.json())
const r = await fetch("https://dierum.io/v1/diff?region=DE-BY&from=2027-01-01&to=2027-03-31", {
  headers: { "X-Api-Key": KEY },
});
if (!r.ok) throw new Error(`dierum: ${r.status}`);
console.log(await r.json());
using var http = new HttpClient();
http.DefaultRequestHeaders.Add("X-Api-Key", key);

var json = await http.GetStringAsync(
    "https://dierum.io/v1/diff?region=DE-BY&from=2027-01-01&to=2027-03-31");
Console.WriteLine(json);

Response

{ "region": "DE-BY", "from": "2027-01-01", "to": "2027-03-31",
  "total_days": 90, "business_days": 60, "weekend_days": 26, "holiday_days": 4 }

GET /v1/window

The next N business days from a date, as a list. The feed behind date pickers, slot schedulers, and delivery choosers.

ParameterNotes
count1-100, default 10
startincluded when it is a business day
curl "https://dierum.io/v1/window?region=US&start=2027-11-24&count=5" -H "X-Api-Key: $KEY"
import requests

r = requests.get("https://dierum.io/v1/window",
    headers={"X-Api-Key": KEY},
    params={'region': 'US', 'start': '2027-11-24', 'count': '5'})
r.raise_for_status()
print(r.json())
const r = await fetch("https://dierum.io/v1/window?region=US&start=2027-11-24&count=5", {
  headers: { "X-Api-Key": KEY },
});
if (!r.ok) throw new Error(`dierum: ${r.status}`);
console.log(await r.json());
using var http = new HttpClient();
http.DefaultRequestHeaders.Add("X-Api-Key", key);

var json = await http.GetStringAsync(
    "https://dierum.io/v1/window?region=US&start=2027-11-24&count=5");
Console.WriteLine(json);

Response

{ "region": "US", "start": "2027-11-24", "count": 5,
  "business_days": ["2027-11-24", "2027-11-26", "2027-11-29", "2027-11-30", "2027-12-01"] }

GET /v1/holidays.csv

The holiday calendar as CSV for spreadsheets and data pipelines. Pairs well with the query-parameter key form.

ParameterNotes
yeardefaults to the current year
curl "https://dierum.io/v1/holidays.csv?region=GB&year=2027" -H "X-Api-Key: $KEY"
import requests

r = requests.get("https://dierum.io/v1/holidays.csv",
    headers={"X-Api-Key": KEY},
    params={'region': 'GB', 'year': '2027'})
r.raise_for_status()
print(r.json())
const r = await fetch("https://dierum.io/v1/holidays.csv?region=GB&year=2027", {
  headers: { "X-Api-Key": KEY },
});
if (!r.ok) throw new Error(`dierum: ${r.status}`);
console.log(await r.json());
using var http = new HttpClient();
http.DefaultRequestHeaders.Add("X-Api-Key", key);

var json = await http.GetStringAsync(
    "https://dierum.io/v1/holidays.csv?region=GB&year=2027");
Console.WriteLine(json);

Response

date,name
2027-01-01,"New Year's Day"
2027-03-26,"Good Friday"
...

GET /v1/nth

The nth business day of each month, from either end. The schedule behind "invoice on the 3rd business day" and "settle 3 days before month end" rules.

ParameterNotes
n1-23
from_endtrue counts backward from month end
curl "https://dierum.io/v1/nth?region=US&year=2027&n=3&from_end=false" -H "X-Api-Key: $KEY"
import requests

r = requests.get("https://dierum.io/v1/nth",
    headers={"X-Api-Key": KEY},
    params={'region': 'US', 'year': '2027', 'n': '3', 'from_end': 'false'})
r.raise_for_status()
print(r.json())
const r = await fetch("https://dierum.io/v1/nth?region=US&year=2027&n=3&from_end=false", {
  headers: { "X-Api-Key": KEY },
});
if (!r.ok) throw new Error(`dierum: ${r.status}`);
console.log(await r.json());
using var http = new HttpClient();
http.DefaultRequestHeaders.Add("X-Api-Key", key);

var json = await http.GetStringAsync(
    "https://dierum.io/v1/nth?region=US&year=2027&n=3&from_end=false");
Console.WriteLine(json);

Response

{ "region": "US", "year": 2027, "n": 3, "from_end": false,
  "dates": ["2027-01-06", "2027-02-03", "..."] }

POST /v1/public-keys

Mints a publishable key (pk_...) for browser code and widgets: locked to the origins you list, accepted only on read-only endpoints, metered against your account, and revoked when you rotate your secret key. Requires your secret key; keep that one server-side.

ParameterNotes
origins1-10; https only, except http on localhost
curl -X POST "https://dierum.io/v1/public-keys" \
  -H "X-Api-Key: $KEY" -H "Content-Type: application/json" \
  -d '{"origins": ["https://your-site.com", "http://localhost:4200"]}'
import requests

r = requests.post("https://dierum.io/v1/public-keys",
    headers={"X-Api-Key": KEY},
    json={"origins": ["https://your-site.com", "http://localhost:4200"]})
r.raise_for_status()
print(r.json())
const r = await fetch("https://dierum.io/v1/public-keys", {
  method: "POST",
  headers: { "X-Api-Key": KEY, "Content-Type": "application/json" },
  body: JSON.stringify({"origins": ["https://your-site.com", "http://localhost:4200"]}),
});
if (!r.ok) throw new Error(`dierum: ${r.status}`);
console.log(await r.json());
using var http = new HttpClient();
http.DefaultRequestHeaders.Add("X-Api-Key", key);

var body = JsonContent.Create({"origins": ["https://your-site.com", "http://localhost:4200"]});
var res = await http.PostAsync("https://dierum.io/v1/public-keys", body);
res.EnsureSuccessStatusCode();
Console.WriteLine(await res.Content.ReadAsStringAsync());

Response

{ "public_key": "pk_4c9...",
  "origins": ["https://your-site.com", "http://localhost:4200"] }

GET /v1/pay-periods

Pay periods with paydays rolled back off non-working days. The anchored biweekly rule is the one fixed-date systems cannot express and most US payrolls run on: pass any known payday as the anchor and the phase is preserved forever.

ParameterNotes
ruleweekly | biweekly | semi-monthly | monthly
anchora known payday; required for weekly/biweekly
from, countfirst period on/after from; up to 60
curl "https://dierum.io/v1/pay-periods?region=US&rule=biweekly&anchor=2027-01-08&from=2027-02-01&count=3" -H "X-Api-Key: $KEY"
import requests

r = requests.get("https://dierum.io/v1/pay-periods",
    headers={"X-Api-Key": KEY},
    params={'region': 'US', 'rule': 'biweekly', 'anchor': '2027-01-08', 'from': '2027-02-01', 'count': '3'})
r.raise_for_status()
print(r.json())
const r = await fetch("https://dierum.io/v1/pay-periods?region=US&rule=biweekly&anchor=2027-01-08&from=2027-02-01&count=3", {
  headers: { "X-Api-Key": KEY },
});
if (!r.ok) throw new Error(`dierum: ${r.status}`);
console.log(await r.json());
using var http = new HttpClient();
http.DefaultRequestHeaders.Add("X-Api-Key", key);

var json = await http.GetStringAsync(
    "https://dierum.io/v1/pay-periods?region=US&rule=biweekly&anchor=2027-01-08&from=2027-02-01&count=3");
Console.WriteLine(json);

Response

{ "rule": "biweekly", "periods": [
  { "period_start": "2027-01-23", "period_end": "2027-02-05", "payday": "2027-02-05" },
  { "period_start": "2027-02-06", "period_end": "2027-02-19", "payday": "2027-02-19" }, ...] }

GET /v1/add-hours

Business-hours arithmetic inside a working window: "respond within 8 business hours" resolved to an exact date and time. Hours spill across holidays and weekends; a start outside the window clips to the next opening. Times are local wall-clock; time zones stay the caller's affair.

ParameterNotes
timeHH:mm start
hours0-2000, fractions allowed
windowHH:mm-HH:mm, default 09:00-17:00
curl "https://dierum.io/v1/add-hours?region=DE&start=2027-03-25&time=16:00&hours=2&window=09:00-17:00" -H "X-Api-Key: $KEY"
import requests

r = requests.get("https://dierum.io/v1/add-hours",
    headers={"X-Api-Key": KEY},
    params={'region': 'DE', 'start': '2027-03-25', 'time': '16:00', 'hours': '2', 'window': '09:00-17:00'})
r.raise_for_status()
print(r.json())
const r = await fetch("https://dierum.io/v1/add-hours?region=DE&start=2027-03-25&time=16:00&hours=2&window=09:00-17:00", {
  headers: { "X-Api-Key": KEY },
});
if (!r.ok) throw new Error(`dierum: ${r.status}`);
console.log(await r.json());
using var http = new HttpClient();
http.DefaultRequestHeaders.Add("X-Api-Key", key);

var json = await http.GetStringAsync(
    "https://dierum.io/v1/add-hours?region=DE&start=2027-03-25&time=16:00&hours=2&window=09:00-17:00");
Console.WriteLine(json);

Response

{ "start": "2027-03-25", "time": "16:00", "hours": 2,
  "result_date": "2027-03-30", "result_time": "10:00" }

GET /v1/recurrence

Recurring schedules that respect the calendar: every N weeks on a weekday, the nth weekday monthly, or the last weekday monthly, with your choice of roll policy when a date lands closed.

ParameterNotes
ruleweekly:<1-12>:<dow> | nth:<1-5>:<dow> | last:<dow>
rollforward | backward | skip | none
curl "https://dierum.io/v1/recurrence?region=US&rule=nth:3:thu&start=2027-01-01&count=3&roll=forward" -H "X-Api-Key: $KEY"
import requests

r = requests.get("https://dierum.io/v1/recurrence",
    headers={"X-Api-Key": KEY},
    params={'region': 'US', 'rule': 'nth:3:thu', 'start': '2027-01-01', 'count': '3', 'roll': 'forward'})
r.raise_for_status()
print(r.json())
const r = await fetch("https://dierum.io/v1/recurrence?region=US&rule=nth:3:thu&start=2027-01-01&count=3&roll=forward", {
  headers: { "X-Api-Key": KEY },
});
if (!r.ok) throw new Error(`dierum: ${r.status}`);
console.log(await r.json());
using var http = new HttpClient();
http.DefaultRequestHeaders.Add("X-Api-Key", key);

var json = await http.GetStringAsync(
    "https://dierum.io/v1/recurrence?region=US&rule=nth:3:thu&start=2027-01-01&count=3&roll=forward");
Console.WriteLine(json);

Response

{ "rule": "nth:3:thu", "dates": ["2027-01-21", "2027-02-18", "2027-03-18"] }

GET /v1/quarter

The fiscal quarter containing a date, with business-day totals, elapsed, and remaining. Custom fiscal-year starts for the July and February crowd.

ParameterNotes
fiscal_startmonth 1-12, default 1
datedefaults to today
curl "https://dierum.io/v1/quarter?region=US&date=2027-02-15&fiscal_start=7" -H "X-Api-Key: $KEY"
import requests

r = requests.get("https://dierum.io/v1/quarter",
    headers={"X-Api-Key": KEY},
    params={'region': 'US', 'date': '2027-02-15', 'fiscal_start': '7'})
r.raise_for_status()
print(r.json())
const r = await fetch("https://dierum.io/v1/quarter?region=US&date=2027-02-15&fiscal_start=7", {
  headers: { "X-Api-Key": KEY },
});
if (!r.ok) throw new Error(`dierum: ${r.status}`);
console.log(await r.json());
using var http = new HttpClient();
http.DefaultRequestHeaders.Add("X-Api-Key", key);

var json = await http.GetStringAsync(
    "https://dierum.io/v1/quarter?region=US&date=2027-02-15&fiscal_start=7");
Console.WriteLine(json);

Response

{ "quarter": "Q3", "quarter_start": "2027-01-01", "quarter_end": "2027-03-31",
  "business_days_total": 61, "business_days_elapsed": 32, "business_days_remaining": 29 }

Invoice due dates that survive the calendar

Accounts receivable at volume: one batch call turns an invoice run into correct, holiday-aware due dates.

curl -X POST "https://dierum.io/v1/batch" \
  -H "X-Api-Key: $KEY" -H "Content-Type: application/json" \
  -d '{"operations": [{"op": "due-date", "region": "US", "start": "2027-01-15", "terms": "net30"}, {"op": "due-date", "region": "DE", "start": "2027-01-15", "terms": "net45"}, {"op": "due-date", "region": "GB", "start": "2027-01-15", "terms": "eom+15"}]}'
import requests

r = requests.post("https://dierum.io/v1/batch",
    headers={"X-Api-Key": KEY},
    json={"operations": [{"op": "due-date", "region": "US", "start": "2027-01-15", "terms": "net30"}, {"op": "due-date", "region": "DE", "start": "2027-01-15", "terms": "net45"}, {"op": "due-date", "region": "GB", "start": "2027-01-15", "terms": "eom+15"}]})
r.raise_for_status()
print(r.json())
const r = await fetch("https://dierum.io/v1/batch", {
  method: "POST",
  headers: { "X-Api-Key": KEY, "Content-Type": "application/json" },
  body: JSON.stringify({"operations": [{"op": "due-date", "region": "US", "start": "2027-01-15", "terms": "net30"}, {"op": "due-date", "region": "DE", "start": "2027-01-15", "terms": "net45"}, {"op": "due-date", "region": "GB", "start": "2027-01-15", "terms": "eom+15"}]}),
});
if (!r.ok) throw new Error(`dierum: ${r.status}`);
console.log(await r.json());
using var http = new HttpClient();
http.DefaultRequestHeaders.Add("X-Api-Key", key);

var body = JsonContent.Create({"operations": [{"op": "due-date", "region": "US", "start": "2027-01-15", "terms": "net30"}, {"op": "due-date", "region": "DE", "start": "2027-01-15", "terms": "net45"}, {"op": "due-date", "region": "GB", "start": "2027-01-15", "terms": "eom+15"}]});
var res = await http.PostAsync("https://dierum.io/v1/batch", body);
res.EnsureSuccessStatusCode();
Console.WriteLine(await res.Content.ReadAsStringAsync());

Response

{ "count": 3, "results": [
  { "due_date": "2027-02-16", "rolled": true },
  { "due_date": "2027-03-01", "rolled": false },
  { "due_date": "2027-02-16", "rolled": true } ] }

An SLA deadline that holds up in a dispute

Ticket opened in Munich on the Wednesday before Easter, five business days to respond. Good Friday and Easter Monday are statutory holidays in Germany; the deadline is a week later than a weekend-only calculation claims.

curl "https://dierum.io/v1/add?region=DE&start=2027-03-24&days=5" -H "X-Api-Key: $KEY"
import requests

r = requests.get("https://dierum.io/v1/add",
    headers={"X-Api-Key": KEY},
    params={'region': 'DE', 'start': '2027-03-24', 'days': '5'})
r.raise_for_status()
print(r.json())
const r = await fetch("https://dierum.io/v1/add?region=DE&start=2027-03-24&days=5", {
  headers: { "X-Api-Key": KEY },
});
if (!r.ok) throw new Error(`dierum: ${r.status}`);
console.log(await r.json());
using var http = new HttpClient();
http.DefaultRequestHeaders.Add("X-Api-Key", key);

var json = await http.GetStringAsync(
    "https://dierum.io/v1/add?region=DE&start=2027-03-24&days=5");
Console.WriteLine(json);

Response

{ "region": "DE", "start": "2027-03-24", "days": 5, "result": "2027-04-01" }

Publishing next year's payroll calendar

One call per pay group. Semi-monthly staff paid on the 15th and month-end, rolled back from weekends and holidays:

curl "https://dierum.io/v1/paydays?region=US&year=2027&rule=semi:15,last" -H "X-Api-Key: $KEY"
import requests

r = requests.get("https://dierum.io/v1/paydays",
    headers={"X-Api-Key": KEY},
    params={'region': 'US', 'year': '2027', 'rule': 'semi:15,last'})
r.raise_for_status()
print(r.json())
const r = await fetch("https://dierum.io/v1/paydays?region=US&year=2027&rule=semi:15,last", {
  headers: { "X-Api-Key": KEY },
});
if (!r.ok) throw new Error(`dierum: ${r.status}`);
console.log(await r.json());
using var http = new HttpClient();
http.DefaultRequestHeaders.Add("X-Api-Key", key);

var json = await http.GetStringAsync(
    "https://dierum.io/v1/paydays?region=US&year=2027&rule=semi:15,last");
Console.WriteLine(json);

Response

{ "paydays": ["2027-01-15", "2027-01-29", "2027-02-12", "2027-02-26",
   "...", "2027-12-15", "2027-12-30"] }

Checking a cross-border settlement window

A EUR/USD trade needs both TARGET2 and US markets open. Ask for the days both calendars agree on:

curl "https://dierum.io/v1/common-days?regions=ECB,XNYS&from=2027-05-01&to=2027-05-31" -H "X-Api-Key: $KEY"
import requests

r = requests.get("https://dierum.io/v1/common-days",
    headers={"X-Api-Key": KEY},
    params={'regions': 'ECB,XNYS', 'from': '2027-05-01', 'to': '2027-05-31'})
r.raise_for_status()
print(r.json())
const r = await fetch("https://dierum.io/v1/common-days?regions=ECB,XNYS&from=2027-05-01&to=2027-05-31", {
  headers: { "X-Api-Key": KEY },
});
if (!r.ok) throw new Error(`dierum: ${r.status}`);
console.log(await r.json());
using var http = new HttpClient();
http.DefaultRequestHeaders.Add("X-Api-Key", key);

var json = await http.GetStringAsync(
    "https://dierum.io/v1/common-days?regions=ECB,XNYS&from=2027-05-01&to=2027-05-31");
Console.WriteLine(json);

Response

{ "regions": ["ECB", "XNYS"],
  "common_business_days": ["2027-05-03", "2027-05-04", "..."] }

Data provenance

Holiday data derives from the MIT-licensed python-holidays project, one of the most actively maintained calendar datasets anywhere. It is regenerated and cross-verified against independent sources on every refresh. Weekend rules are curated separately: Friday–Saturday across much of the Middle East, Saturday alone in Nepal, Friday and Sunday in Brunei. The dataset version and generation timestamp are public at /health.

Found an error in a calendar? Tell us. Corrections ship in the next daily refresh, and we credit reporters who want it.