gazetteddatesv1

Australian public holidays, show days and school terms. One API, every state, every source cited.

Ask “is this date a holiday for this district?” and get the gazetted answer, including the regional show days and local replacements that state-level lists get wrong.

Get a free API key, or sign in

The link opens your account page. A new account gets its key there, shown once. An existing account sees its plan, Upgrade, Manage billing and Reset key. Signing in never changes your key; only Reset key does.

Try it

Examples that trip up other sources (annual plan, since they need a district): Karratha on King’s Birthday · Geelong on Melbourne Cup Day · Hobart on Recreation Day · Tasmania, Saturday 26 Dec

Endpoints

Base URL https://dates.gazetted.dev. Send the key as Authorization: Bearer gz_… or ?api_key=gz_…. All responses are JSON.

EndpointAuthWhat it returns
GET /v1/is-holiday?state=&date=&q=keyis_public_holiday, statewide records, regional matches for q, and any statewide holiday that is replaced in that district. q takes a postcode or a place name.
GET /v1/holidays?state=&from=&to=&q=&type=keyAll holidays in the range. q takes a postcode, district, council or town. type is public_holiday, bank_holiday or government_holiday.
GET /v1/business-days?state=&from=&to=&q=keyHow many working days fall between two dates, both included. Weekends and public holidays are taken out, for the district q names.
GET /v1/business-days/add?state=&date=&days=&q=keyThe date days working days from date. Negative counts backwards.
GET /v1/school-terms?state=&year=keyTerms, holidays between terms, pupil-free days. Government schools. Most states run to 2030, NT and WA to 2032, Tasmania only to 2027. provisional: true marks a year the government has published but not settled.
GET /v1/changes?since=annualData changes since a date, by jurisdiction. Poll it, refetch what changed.
GET /v1/mekeyYour plan and limits.
POST /v1/checkoutkeyReturns a Stripe Checkout URL for the annual plan. See Billing from code.
POST /v1/billing/portalkeyReturns a Stripe billing page URL: receipts, card, cancel. See Billing from code.
POST /v1/keys/rotatekeyNew key, old one revoked.
GET /v1/coveragenoneWhich states and years exist.
POST /v1/signupnone{"email"} → emails a one-time link to your account page. A new account gets its key there. An existing key is left alone. Three per minute per address.

Working days

Most calls to a holiday API are really one of two questions: how many working days are there between now and then, and what date is five working days out. Both need the holidays and the local replacement rules, and the replacements are the part that goes wrong. A Geelong employee never gets Melbourne Cup Day, so counting it as a day off makes every date after it wrong by one.

curl -H "Authorization: Bearer gz_…" \
  "https://dates.gazetted.dev/v1/business-days?state=VIC&from=2026-11-01&to=2026-11-30"
{ "business_days": 20, "calendar_days": 30, "counted": "both endpoints included",
  "holidays_excluded": [ { "date": "2026-11-03", "name": "Melbourne Cup Day", "scope": "state" } ] }

Add q=3220 and the same month comes back as 21: Greater Geelong swapped the Cup for a local day on 21 October, so November has one more working day there than it does in Melbourne.

curl -H "Authorization: Bearer gz_…" \
  "https://dates.gazetted.dev/v1/business-days/add?state=QLD&date=2026-12-24&days=5"
{ "result": "2027-01-05",
  "holidays_skipped": [ { "date": "2026-12-25", "name": "Christmas Day" },
                        { "date": "2026-12-28", "name": "Boxing Day (additional public holiday)" },
                        { "date": "2027-01-01", "name": "New Year's Day" } ],
  "part_day_holidays": [ { "date": "2026-12-24", "name": "Christmas Eve", "part_day": "18:00-24:00" } ] }

The rules, so you can check them against your own:

Free plan: statewide, current year, ranges up to 31 days. Regional working days and other years are on the annual plan.

Billing from code

Two ways to pay, same Stripe account behind both.

In a browser, sign in with your email and use the buttons on your account page. That is the normal way, and nothing below is needed.

From a script or a terminal, POST /v1/checkout and POST /v1/billing/portal do the same two things with your API key. Each returns a short-lived Stripe URL to open. Useful if you provision keys from your own tooling and never want to leave the shell.

curl -X POST -H "Authorization: Bearer gz_…" \
  https://dates.gazetted.dev/v1/checkout
{ "url": "https://checkout.stripe.com/c/pay/cs_…" }

Open that URL to pay. Your plan flips to annual as soon as Stripe confirms the payment, usually a few seconds. /v1/checkout answers 409 if the account is already on the annual plan, and 503 while card payments are still switched off, which is the case today. /v1/billing/portal answers 404 until you have paid at least once, because there is no billing record to show yet.

Example

curl -H "Authorization: Bearer gz_…" \
  "https://dates.gazetted.dev/v1/is-holiday?state=WA&date=2026-09-28&q=karratha"
{
  "state": "WA",
  "date": "2026-09-28",
  "is_public_holiday": false,
  "statewide": [ { "name": "King's Birthday", "type": "public_holiday", … } ],
  "regional_matches": [],
  "replaced_in_district": [ "King's Birthday" ]
}

Karratha holds its King’s Birthday on 3 August, so 28 September is a normal working day there. The response says so instead of guessing.

Record shape

FieldMeaning
date, name, jurisdictionISO date, name as published, state code.
typepublic_holiday, bank_holiday (the NSW and ACT August day, and Queensland's ~90 special holidays a year in the Torres Strait and Cape York: real, gazetted, but most people work them), government_holiday (Tasmania Easter Tuesday, Launceston Cup: public service only). Only public_holiday counts towards is_public_holiday.
scopestate or regional.
district_textThe government’s own wording for the area, verbatim. Some are prose (“south of and including Oatlands and Swansea”).
precisionstate, lga, postcodes, or sub_lga_prose when the district is a fragment of a council area described in words.
replacesName of the statewide holiday this local one stands in for. On that statewide date, the district is not on holiday.
part_daye.g. 19:00-24:00 for SA and NT Christmas Eve.
source_url, source_checkedWhere it came from and when it was last checked.
matched_by, match_basis, certainOnly when q was a postcode. See Postcode lookup.
replaced_in_districtOn a statewide row: this day is not observed here, a regional day stands in for it.

Postcode lookup

Pass a four-digit postcode as q and regional days resolve without you knowing the district's name. Your payroll system has a postcode. It does not have "the area of the former Stanthorpe Shire".

curl -H "Authorization: Bearer gz_…" \
  "https://dates.gazetted.dev/v1/is-holiday?state=VIC&date=2026-11-03&q=3220"

Melbourne Cup Day, postcode 3220. The answer is false: Geelong holds its own cup day instead, and replaced_in_district names the day that was given up.

Districts are written the way each government wrote them, so how far a postcode can be trusted varies. Every regional row matched by postcode says which case it is in match_basis, and certain is false for the last one.

match_basisWhat it means
gazettedThe government named the postcodes itself. Nothing was inferred.
lgaThe district is a whole council area. Exact, give or take where a postcode straddles a boundary.
localityThe district is a list of named towns, and these are their postcodes. May reach slightly past the edge.
parentThe district is a slice of something larger, described in words: a parish list, a former shire division, "all areas west of the Campaspe River". The postcode only says you are inside that larger area. certain is false, the day is left out of is_public_holiday, and no replacement is applied. Read district_text and decide.

Postcodes come from the Australian Bureau of Statistics correspondence files, ASGS Edition 3, used under CC BY 4.0. 227 districts are covered. A place name still works if you prefer: q=karratha.

Caching

Holidays change a few times a year, when a state gazettes something. Don’t poll the data on a timer. Do this instead:

  1. Fetch what you need once and store it.
  2. Every data response carries ETag and X-Data-Version. Re-request with If-None-Match and you get a 304 with no body when nothing has changed.
  3. On the annual plan, poll GET /v1/changes?since=<last date you saw> once a day. When count is above zero, refetch the jurisdictions it lists. That’s the whole refresh loop.

On the free plan, fetch the current year in 31-day windows and revalidate with If-None-Match. The version header tells you whether anything moved without downloading it again.

curl -i -H "Authorization: Bearer gz_…" -H 'If-None-Match: "<etag from last time>"' \
  "https://dates.gazetted.dev/v1/holidays?state=QLD&from=2026-01-01&to=2026-12-31"
# HTTP/2 304 when unchanged

Plan and API changes

Any change to what the free plan includes, or to the API shape, is listed here dated, before it ships. Data changes go to the change feed.

DateChange

Coverage

StateHolidaysSchool terms

Regional holidays: Queensland show days (98 districts), Tasmania (11 regional days with areas from the Statutory Holidays Act), Northern Territory show days, Victoria non-metropolitan replacements, Western Australia regional King’s Birthday. These fill in as states gazette them, usually September to November for the following year.

2026 and 2027 are checked by hand against each government page. 2020 to 2025 come from the federal dataset described below, which carries statewide holidays for all eight jurisdictions but almost no regional days. Every record says which source it came from. Ask for the current gap list at hello@gazetted.dev.

Why this exists

The federal machine-readable holidays dataset was retired in January 2026. State pages publish their own lists in eight different shapes, and none of them map regional days to a district you can query. Payroll, rostering and delivery apps end up hand-coding “second Monday in March” rules that drift the year a date is proclaimed differently.

Gazetted stores dates, not rules. Every record is a published date with the URL it came from. When a state changes something, the record changes and the source line tells you why.

Plans

Holidays don’t change often, so we don’t charge per request. The free plan is what the free lists already give you. The annual plan is everything they don’t.

FreeAnnual
Statewide public holidayscurrent year2020 to 2027
Regional and show days, local replacements—2021 to 2027
Date ranges31 daysunlimited
School termscurrent termall terms, all years
Change feed—/v1/changes
Licencenon-commercialcommercial
Price$0A$99 a year, renews yearly, cancel any time

Both plans have a per-minute cap for abuse control. Free-plan responses to regional or multi-year queries return 402 with a note, so you can see exactly where the line is.

Upgrade, manage billing, or reset your key

Sign in with your email at the top of this page for your account page: plan, billing and Reset key.

Card payments open shortly. Until then the annual plan is invoiced. Email hello@gazetted.dev with the address on your account and we'll send one.