# UK Bank Holidays & Working Day Calendar (`grit-77/uk-bank-holidays`) Actor

UK bank-holiday calendar from GOV.UK for England/Wales, Scotland and Northern Ireland. Export holiday rows or derived working-day flags and inclusive counts as JSON with source notes.

- **URL**: https://apify.com/grit-77/uk-bank-holidays.md
- **Developed by:** [Grit](https://apify.com/grit-77) (community)
- **Categories:** Business, Other
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$1.00 / 1,000 holidays

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## UK Bank Holidays & Working Day Calendar

Get official GOV.UK bank holidays for England/Wales, Scotland and Northern Ireland, or derive daily working-day flags and inclusive working-day counts from the same feed.

### What it does

The actor fetches [GOV.UK bank-holidays.json](https://www.gov.uk/bank-holidays.json) once on a successful run. Transient network failures, HTTP 429 and server errors receive sequential retries with 1-second and 2-second backoff, up to three attempts. It preserves substitute-day notes, the official bunting boolean and special holidays rather than recomputing holiday dates.

Choose holidays for official events, calendar for one row per date, or working-days for a count summary per selected division. England and Wales share one official division. Selecting all keeps the three calendars separate.

A requested year must be present in the live feed for the selected division. A range checks every calendar year it touches, including internal gaps. Unavailable years fail with the available years; the actor does not invent future holidays. A valid range without holidays can return an empty holiday dataset.

### Input

The prefilled example is:

```json
{"division":"england-and-wales","year":2026,"maxItems":10}
```

| Input | Meaning | Default / limits |
|---|---|---|
| division | england-and-wales, scotland, northern-ireland or all | england-and-wales |
| year | Optional year filter; must exist in the source | Omitted means all source years; prefill is 2026 |
| mode | holidays, calendar or working-days | holidays |
| startDate, endDate | ISO YYYY-MM-DD bounds, both inclusive; supply together | Required for calendar and working-days; optional holiday filter; at most 366 days |
| weekend | saturday-sunday, friday-saturday, sunday-only or none | saturday-sunday |
| includeWeekdayFlags | Add derived flags and weekend assumptions to holiday rows | false; calendar mode always has flags |
| maxItems | Global dataset cap across divisions, including error rows | 1000; 1–10000; prefill is 10 |

When year and a range are both supplied, both endpoints must lie in that year. Omit year to use a range crossing two supported years. Numeric strings, invalid dates, unsupported options and unknown input keys are rejected clearly.

Working-day count example:

```json
{
  "division": "england-and-wales",
  "mode": "working-days",
  "startDate": "2026-01-01",
  "endDate": "2026-01-07",
  "weekend": "saturday-sunday",
  "maxItems": 1
}
```

In the recorded local run this produced 7 calendar days, 2 weekend days, 1 bank-holiday date and 4 working days. Changing weekend to none produced 6 working days. These observations are saved in evidence/observations.json.

### Output fields

The default dataset contains one of four record types. The actor output links to its dataset items. Missing fields for another record type are omitted, not filled with guessed values.

| Common field | Meaning |
|---|---|
| recordType | holiday, calendar, working-days or error |
| division | Official division identifier |
| errors | Empty list on a successful row; readable messages on an uncharged error row |
| sourceName, sourceUrl | GOV.UK and the official JSON feed URL |
| fetchedAt | UTC retrieval timestamp for the run |
| copyright | © Crown copyright |
| licence, licenceUrl | Open Government Licence v3.0 and its full URL |
| attribution | Public-sector information, OGL and Crown/GOV.UK attribution |

| Holiday field | Meaning |
|---|---|
| title | Official title with Unicode compatibility normalization and collapsed whitespace; punctuation is retained |
| date, year | ISO holiday date and integer year |
| notes | Official notes with normalized whitespace, including Substitute day |
| bunting | Official true/false value; not inferred |

| Derived daily / optional holiday field | Meaning |
|---|---|
| weekday, weekdayNumber | English weekday name and ISO weekday number, Monday=1 through Sunday=7 |
| isWeekday | Monday through Friday, independent of the selected weekend rule |
| isWeekend | Date belongs to the chosen weekend days |
| isBankHoliday | Selected division has a feed holiday on this date |
| isWorkingDay | Date is neither a chosen weekend day nor an official bank holiday |
| holidayTitles | Calendar mode: titles of holidays on that date, otherwise an empty list |
| weekendDays, weekendAssumptions | Named weekend days and the calculation's explicit assumptions |
| calendarComplete | Calendar/count mode: touched years are present and applicable holiday events parsed without errors; source completeness depends on GOV.UK |

| Working-days summary field | Meaning |
|---|---|
| rangeStart, rangeEnd, inclusive | Bounds and true, indicating both endpoints count |
| calendarDaysCount | Number of dates in the whole range |
| weekdayDaysCount | Monday–Friday dates |
| weekendDaysCount | Dates excluded by the selected weekend rule |
| bankHolidayDaysCount | Distinct holiday dates in the range, including holidays falling on a chosen weekend |
| nonWeekendBankHolidayDaysCount | Holiday dates outside the selected weekend; prevents double subtraction |
| workingDaysCount | Remaining working dates |
| weekendDays, weekendAssumptions, calendarComplete | Calculation assumptions and source-parsing coverage |

Error rows may also include eventIndex, inputDate, rangeStart and rangeEnd for diagnosis. An event's malformed title, date, notes or bunting produces an error row while other valid holiday rows survive. Derived results for a division are withheld when an applicable source year contains malformed events; no optimistic working-day count is emitted for that damaged calendar.

The RUN_SUMMARY key-value record includes availableYears, totalRows, outputRows, emittedRows, successfulRows, errorRows, sourceErrorRows, maxItems, truncated, budgetReached, durationSeconds and full-range workingDaySummaries in derived modes. Unsupported-year/source failures have a shorter summary with status and errors. Invalid input fails before fetching and has no result rows.

Counts use the complete source calendar and full requested range even when maxItems truncates daily output. Use RUN_SUMMARY.truncated to detect a capped dataset. Errors count against maxItems but are uncharged. The dataset may be empty for a valid holiday filter; this is distinct from an unsupported year, which fails.

### Actual output example

This row comes from sample_output.json, containing the 8 England/Wales 2026 holidays returned by the recorded local run:

```json
{
  "sourceName": "GOV.UK",
  "sourceUrl": "https://www.gov.uk/bank-holidays.json",
  "fetchedAt": "2026-10-06T08:30:41.896626+00:00",
  "copyright": "© Crown copyright",
  "licence": "Open Government Licence v3.0",
  "licenceUrl": "https://www.nationalarchives.gov.uk/doc/open-government-licence/version/3/",
  "attribution": "Contains public sector information licensed under the Open Government Licence v3.0. © Crown copyright. Source: GOV.UK bank holidays.",
  "recordType": "holiday",
  "division": "england-and-wales",
  "date": "2026-01-01",
  "year": 2026,
  "title": "New Year’s Day",
  "notes": "",
  "bunting": true,
  "errors": []
}
```

### Pricing

Proposed pay-per-event pricing: **holiday = $0.001 per successful dataset row**, or **$1 per 1,000 rows**. Holiday events, calendar dates and working-days summaries each use one event per successful row. Error rows and RUN_SUMMARY are not charged. There is no proposed start fee or minimum event spend. Price activation is the owner's publication step; platform usage depends on Apify settings and the caller's plan.

The Store API comparison on 2026-10-06 found [easyapi/public-holidays-api](https://apify.com/easyapi/public-holidays-api) as the most used comparable returned by the recorded holiday/calendar searches, with 30 lifetime users. Its current result fee was $0.00299 with a $0.09 start event. Our proposed result fee is lower. PRICING.md and evidence/pricing-comparison.json contain the selection rule, full event prices and retrieval timestamp.

### Limits

- Date ranges are inclusive and bounded to 366 days; output is capped globally by maxItems.
- Source access is sequential with one connection and a 20-second request timeout. Responses larger than 2 MiB are rejected.
- Years come from the feed at run time. Stored test fixtures are regression evidence, not a fallback data source.
- Saturday/Sunday is the default weekend, but the chosen rule is stated in derived output. Monday–Friday isWeekday remains a separate flag.
- Working-day calculations exclude official bank holidays and the selected weekend only. Employer-specific closures, local holidays and other calendars are not included.
- Source failures produce an uncharged error row and failed run; malformed individual events preserve valid holiday rows and diagnostics.
- The SDK charging budget can stop output before maxItems; this is reported as truncated with budgetReached. Paid cloud billing and Docker execution have not been exercised in this local build.

### FAQ

**Why are England and Wales combined?** The official feed uses a single england-and-wales division.

**Does this forecast holidays?** No. It uses the events and years present in the fetched official feed, including any special holidays added by GOV.UK.

**Are substitute days preserved?** Yes. The returned date is the official substitute date and notes retains Substitute day when supplied.

**Can I calculate a full range with maxItems=1?** Yes. working-days mode emits one full-range summary per division. In calendar mode the dataset is capped but full-range summaries remain in RUN_SUMMARY. With division=all and maxItems=1, only the first dataset row is emitted; RUN_SUMMARY announces truncation.

**Does bunting determine working days?** No. It is preserved as source data; each listed holiday date is excluded regardless of bunting.

### Use with AI agents / MCP

Once this actor is published, use [Apify's MCP server](https://docs.apify.com/integrations/mcp) to discover/run it and read dataset results, or supply these same JSON inputs through the Apify actor API in your automation. The MCP integration has not been exercised as part of this local build.

Ask an agent to select the correct division, verify requested years, set a bounded range and explicit weekend, then inspect recordType, errors and RUN_SUMMARY.truncated before using a result. For a planning calculation use working-days mode; for per-date decisions use calendar mode. Attribution fields should travel with reused output.

### Source and attribution

**© Crown copyright. Contains public sector information licensed under the [Open Government Licence v3.0](https://www.nationalarchives.gov.uk/doc/open-government-licence/version/3/).** Source: [GOV.UK bank holidays](https://www.gov.uk/bank-holidays) and its [official JSON feed](https://www.gov.uk/bank-holidays.json). [GOV.UK content terms](https://www.gov.uk/help/terms-conditions) describe Crown copyright and OGL reuse.

This actor is an independent tool. GOV.UK supplies the source calendar; the actor supplies normalization, filtering and calculations.

### Verification

Unit tests use a full official response saved with its source, retrieval timestamp and OGL attribution in tests/fixtures. One test is marked live and fetches GOV.UK. Exact command output is in evidence/tests-final.txt; CLI schema output is in evidence/schemaValid.txt; real-process observations are in evidence/BEHAVIOR_PROOF.md and evidence/observations.json. The sample is from an actual local SDK run, not generated documentation data.

# Actor input Schema

## `division` (type: `string`):

Select one official UK division or all three. England and Wales share a calendar.

## `year` (type: `integer`):

Optional holiday year filter. Omit for all years currently in the official feed. Unavailable years fail clearly. With a date range, both endpoints must be in this year; omit year to cross years.

## `mode` (type: `string`):

holidays: official events; calendar: one row per date; working-days: one count summary per division. Calendar and count modes require both dates. Each successful dataset row uses one holiday event.

## `startDate` (type: `string`):

YYYY-MM-DD. Supply together with endDate. Optional holiday date filter; required for calendar and working-days. The inclusive range may contain at most 366 days. All touched years must exist in the feed.

## `endDate` (type: `string`):

YYYY-MM-DD, on or after startDate. Both endpoints are included. No dates or holidays are extrapolated beyond source years.

## `weekend` (type: `string`):

Applies to derived flags and counts. Output states the selected days. Working days exclude these days and official bank holidays; employer and local closures are excluded.

## `includeWeekdayFlags` (type: `boolean`):

Add weekday name/ISO number, weekday, weekend, bank-holiday and working-day flags plus weekend assumptions to holidays mode. Calendar mode always includes these fields.

## `maxItems` (type: `integer`):

Global dataset row cap, including error rows, across all divisions. RUN_SUMMARY announces truncation. Working-day counts use the full uncapped range. Only successful emitted rows are chargeable.

## Actor input object example

```json
{
  "division": "england-and-wales",
  "year": 2026,
  "mode": "holidays",
  "weekend": "saturday-sunday",
  "includeWeekdayFlags": false,
  "maxItems": 10
}
```

# Actor output Schema

## `dataset` (type: `string`):

Dataset of capped holiday, calendar, working-day summary and uncharged error rows.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "division": "england-and-wales",
    "year": 2026,
    "maxItems": 10
};

// Run the Actor and wait for it to finish
const run = await client.actor("grit-77/uk-bank-holidays").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "division": "england-and-wales",
    "year": 2026,
    "maxItems": 10,
}

# Run the Actor and wait for it to finish
run = client.actor("grit-77/uk-bank-holidays").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "division": "england-and-wales",
  "year": 2026,
  "maxItems": 10
}' |
apify call grit-77/uk-bank-holidays --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,grit-77/uk-bank-holidays"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/axTOcpdcOV04FQBfT/builds/AMdMAe3VwttoFUDKt/openapi.json
