API reference

Base URL https://chronosapi.dev. 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://chronosapi.dev/v1/check?region=US&date=2027-11-25" -H "X-Api-Key: $KEY"
import requests

r = requests.get("https://chronosapi.dev/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://chronosapi.dev/v1/check?region=US&date=2027-11-25", {
  headers: { "X-Api-Key": KEY },
});
if (!r.ok) throw new Error(`chronos: ${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://chronosapi.dev/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.

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
Developer2,500$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://chronosapi.dev/v1/check?region=US&date=2027-11-25" -H "X-Api-Key: $KEY"
import requests

r = requests.get("https://chronosapi.dev/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://chronosapi.dev/v1/check?region=US&date=2027-11-25", {
  headers: { "X-Api-Key": KEY },
});
if (!r.ok) throw new Error(`chronos: ${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://chronosapi.dev/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, "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://chronosapi.dev/v1/add?region=XNYS&start=2027-03-25&days=2" -H "X-Api-Key: $KEY"
import requests

r = requests.get("https://chronosapi.dev/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://chronosapi.dev/v1/add?region=XNYS&start=2027-03-25&days=2", {
  headers: { "X-Api-Key": KEY },
});
if (!r.ok) throw new Error(`chronos: ${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://chronosapi.dev/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://chronosapi.dev/v1/count?region=DE-BY&from=2027-01-01&to=2027-01-31" -H "X-Api-Key: $KEY"
import requests

r = requests.get("https://chronosapi.dev/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://chronosapi.dev/v1/count?region=DE-BY&from=2027-01-01&to=2027-01-31", {
  headers: { "X-Api-Key": KEY },
});
if (!r.ok) throw new Error(`chronos: ${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://chronosapi.dev/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://chronosapi.dev/v1/due-date?region=US&invoice_date=2027-01-15&terms=net30" -H "X-Api-Key: $KEY"
import requests

r = requests.get("https://chronosapi.dev/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://chronosapi.dev/v1/due-date?region=US&invoice_date=2027-01-15&terms=net30", {
  headers: { "X-Api-Key": KEY },
});
if (!r.ok) throw new Error(`chronos: ${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://chronosapi.dev/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://chronosapi.dev/v1/paydays?region=US&year=2027&rule=semi:15,last" -H "X-Api-Key: $KEY"
import requests

r = requests.get("https://chronosapi.dev/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://chronosapi.dev/v1/paydays?region=US&year=2027&rule=semi:15,last", {
  headers: { "X-Api-Key": KEY },
});
if (!r.ok) throw new Error(`chronos: ${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://chronosapi.dev/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://chronosapi.dev/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://chronosapi.dev/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://chronosapi.dev/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(`chronos: ${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://chronosapi.dev/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://chronosapi.dev/v1/year?region=GB&year=2027" -H "X-Api-Key: $KEY"
import requests

r = requests.get("https://chronosapi.dev/v1/year",
    headers={"X-Api-Key": KEY},
    params={'region': 'GB', 'year': '2027'})
r.raise_for_status()
print(r.json())
const r = await fetch("https://chronosapi.dev/v1/year?region=GB&year=2027", {
  headers: { "X-Api-Key": KEY },
});
if (!r.ok) throw new Error(`chronos: ${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://chronosapi.dev/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://chronosapi.dev/v1/bridge-days?region=DE&year=2027&pto=1" -H "X-Api-Key: $KEY"
import requests

r = requests.get("https://chronosapi.dev/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://chronosapi.dev/v1/bridge-days?region=DE&year=2027&pto=1", {
  headers: { "X-Api-Key": KEY },
});
if (!r.ok) throw new Error(`chronos: ${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://chronosapi.dev/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://chronosapi.dev/v1/next-holiday?region=US&count=2" -H "X-Api-Key: $KEY"
import requests

r = requests.get("https://chronosapi.dev/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://chronosapi.dev/v1/next-holiday?region=US&count=2", {
  headers: { "X-Api-Key": KEY },
});
if (!r.ok) throw new Error(`chronos: ${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://chronosapi.dev/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://chronosapi.dev/v1/holidays?region=GB&from=2027-12-01&to=2027-12-31" -H "X-Api-Key: $KEY"
import requests

r = requests.get("https://chronosapi.dev/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://chronosapi.dev/v1/holidays?region=GB&from=2027-12-01&to=2027-12-31", {
  headers: { "X-Api-Key": KEY },
});
if (!r.ok) throw new Error(`chronos: ${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://chronosapi.dev/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://chronosapi.dev/v1/holidays.ics?region=US&year=2027" -H "X-Api-Key: $KEY"
import requests

r = requests.get("https://chronosapi.dev/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://chronosapi.dev/v1/holidays.ics?region=US&year=2027", {
  headers: { "X-Api-Key": KEY },
});
if (!r.ok) throw new Error(`chronos: ${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://chronosapi.dev/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: 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://chronosapi.dev/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://chronosapi.dev/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://chronosapi.dev/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(`chronos: ${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://chronosapi.dev/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.

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

r = requests.get("https://chronosapi.dev/v1/regions",
    headers={"X-Api-Key": KEY})
r.raise_for_status()
print(r.json())
const r = await fetch("https://chronosapi.dev/v1/regions", {
  headers: { "X-Api-Key": KEY },
});
if (!r.ok) throw new Error(`chronos: ${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://chronosapi.dev/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://chronosapi.dev/v1/diff?region=DE-BY&from=2027-01-01&to=2027-03-31" -H "X-Api-Key: $KEY"
import requests

r = requests.get("https://chronosapi.dev/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://chronosapi.dev/v1/diff?region=DE-BY&from=2027-01-01&to=2027-03-31", {
  headers: { "X-Api-Key": KEY },
});
if (!r.ok) throw new Error(`chronos: ${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://chronosapi.dev/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": 62, "weekend_days": 25, "holiday_days": 3 }

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://chronosapi.dev/v1/window?region=US&start=2027-11-24&count=5" -H "X-Api-Key: $KEY"
import requests

r = requests.get("https://chronosapi.dev/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://chronosapi.dev/v1/window?region=US&start=2027-11-24&count=5", {
  headers: { "X-Api-Key": KEY },
});
if (!r.ok) throw new Error(`chronos: ${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://chronosapi.dev/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://chronosapi.dev/v1/holidays.csv?region=GB&year=2027" -H "X-Api-Key: $KEY"
import requests

r = requests.get("https://chronosapi.dev/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://chronosapi.dev/v1/holidays.csv?region=GB&year=2027", {
  headers: { "X-Api-Key": KEY },
});
if (!r.ok) throw new Error(`chronos: ${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://chronosapi.dev/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"
...

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://chronosapi.dev/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://chronosapi.dev/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://chronosapi.dev/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(`chronos: ${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://chronosapi.dev/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"] }

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://chronosapi.dev/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://chronosapi.dev/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://chronosapi.dev/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(`chronos: ${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://chronosapi.dev/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://chronosapi.dev/v1/add?region=DE&start=2027-03-24&days=5" -H "X-Api-Key: $KEY"
import requests

r = requests.get("https://chronosapi.dev/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://chronosapi.dev/v1/add?region=DE&start=2027-03-24&days=5", {
  headers: { "X-Api-Key": KEY },
});
if (!r.ok) throw new Error(`chronos: ${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://chronosapi.dev/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://chronosapi.dev/v1/paydays?region=US&year=2027&rule=semi:15,last" -H "X-Api-Key: $KEY"
import requests

r = requests.get("https://chronosapi.dev/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://chronosapi.dev/v1/paydays?region=US&year=2027&rule=semi:15,last", {
  headers: { "X-Api-Key": KEY },
});
if (!r.ok) throw new Error(`chronos: ${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://chronosapi.dev/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://chronosapi.dev/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://chronosapi.dev/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://chronosapi.dev/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(`chronos: ${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://chronosapi.dev/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.