# Companies House Monitor - UK Filing, Director & PSC Alerts (`skillmerc/companies-house-monitor`) Actor

Watch UK companies and officers on Companies House. Get one row, webhook or Slack message per change: new filings, director moves, PSC, charges, status, address, name. $0.02 per alert.

- **URL**: https://apify.com/skillmerc/companies-house-monitor.md
- **Developed by:** [Kyle Pretorius](https://apify.com/skillmerc) (community)
- **Categories:** Automation, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $14.00 / 1,000 alerts

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

## Companies House Company Monitor

Watch a list of UK companies and get told only when something changes. Each run compares the live Companies House register against the snapshot from the previous run and writes one row per change: a new filing, a director appointed or resigned, a new person with significant control, a charge registered or satisfied, a status change such as liquidation or dissolution, a registered office move, or a name change.

Nothing changed? The run writes nothing and you pay almost nothing. Data comes from the official Companies House REST API, not from scraped HTML. A built-in API key is included, so there is no setup.

### Common uses

- Credit control: know the day a customer or supplier files for liquidation or has a charge registered.
- Portfolio and competitor watching: new filings, director changes and ownership changes across a list.
- Compliance: track changes in persons with significant control for KYC refreshes.

### What it watches

| Watch option | Alert types produced |
|---|---|
| `new_filings` | `new_filing` (any document added to the filing history, with a PDF link) |
| `officers` | `officer_appointed`, `officer_resigned` |
| `psc` | `psc_added`, `psc_ceased`, `psc_changed` (natures of control changed) |
| `status` | `status_change` (active, liquidation, administration, dissolved, receivership and so on) |
| `registered_office` | `registered_office_change` |
| `charges` | `charge_created`, `charge_status_change` (for example outstanding to fully-satisfied) |
| `name_change` | `name_change` |

You can also watch people. Add Companies House officer IDs in `officerIds` and you get `officer_new_appointment` and `officer_appointment_resigned` alerts whenever that person joins or leaves the board of any company.

### How to schedule it

1. Run the actor once with your company numbers and a `watchlistName`. The first run saves a baseline and reports nothing, unless you set `initialLookbackDays` (see below).
2. In Apify Console open the actor, click Schedule, and pick an interval. Daily is enough for most watchlists; Companies House updates the register during UK working hours. Hourly works too.
3. Optional: put a Slack incoming webhook URL or your own HTTPS endpoint in the input to receive each run's alerts as a push message.

Every scheduled run reuses the same `watchlistName`, so it always compares against the previous run. The snapshot lives in a key-value store named `companies-house-monitor-<watchlistName>` in your account. Delete that store to start over.

If you leave `watchlistName` empty, the name is derived from the list of company numbers. That means editing the list starts a fresh baseline, so naming the watchlist is better.

### Initial lookback

Set `initialLookbackDays` to a number such as 30 and the first run also reports everything dated within that window: filings, appointments, resignations, PSC notifications, charges, dissolutions and past name changes. This gives you a useful first result straight away and lets you check the output format before scheduling. Later runs ignore this setting and report only what is new since the previous run.

### Input

| Field | Type | Default | Description |
|---|---|---|---|
| `companyNumbers` | array | | Company numbers to watch, for example `00102498`. |
| `officerIds` | array | `[]` | Optional officer IDs (the part after `/officers/` in a Companies House URL). |
| `watch` | array | all | Which change types raise alerts. |
| `initialLookbackDays` | integer | `0` | On the first run, also report changes dated within this many days. |
| `watchlistName` | string | | Name of the watchlist. Keeps the snapshot between runs. |
| `webhookUrl` | string | | HTTPS endpoint that receives a JSON POST per run with the summary and alerts. |
| `slackWebhookUrl` | string | | Slack incoming webhook. One message per run. |
| `maxAlertsPerRun` | integer | `500` | Upper bound on alerts delivered and charged in a single run. |
| `apiKey` | string | | Optional. Your own Companies House API key if you want your own rate limit. |

### Output

One dataset row per change. Example (a real row from a test run):

```json
{
  "watchlist": "my-portfolio",
  "company_number": "00102498",
  "company_name": "BP P.L.C.",
  "company_url": "https://find-and-update.company-information.service.gov.uk/company/00102498",
  "change_type": "new_filing",
  "summary": "New filing SH04: capital sale or transfer treasury shares with date currency capital figure",
  "before": null,
  "after": {
    "type": "SH04",
    "category": "capital",
    "date": "2026-10-02",
    "description": "capital sale or transfer treasury shares with date currency capital figure"
  },
  "filing_type": "SH04",
  "filing_date": "2026-10-02",
  "filing_url": "https://find-and-update.company-information.service.gov.uk/company/00102498/filing-history/MzU0NjI4NTM1MmFkaXF6a2N4/document?format=pdf&download=0",
  "detected_at": "2026-10-06T07:02:24.569Z",
  "run_id": "abc123"
}
```

An officer change looks like this:

```json
{
  "company_number": "00102498",
  "company_name": "BP P.L.C.",
  "change_type": "officer_resigned",
  "summary": "Officer resigned: ASHDOWN, Hannah (secretary) on 2020-05-31",
  "before": { "name": "ASHDOWN, Hannah", "role": "secretary", "appointed_on": "2018-01-01", "resigned_on": null },
  "after": { "name": "ASHDOWN, Hannah", "role": "secretary", "appointed_on": "2018-01-01", "resigned_on": "2020-05-31" },
  "filing_date": "2020-05-31"
}
```

`before` and `after` hold the old and new values for the thing that changed. `filing_url` is present for filing alerts and opens the document on the Companies House website.

The run also stores a summary in the `OUTPUT` record of the run's key-value store:

```json
{
  "watchlist": "my-portfolio",
  "baseline": false,
  "companies_checked": 4,
  "not_found": [],
  "changes_detected": 3,
  "alerts_delivered": 3,
  "alerts_skipped": 0
}
```

### Webhook payloads

`webhookUrl` receives one POST per run: the summary above plus `run_id` and an `alerts` array of the rows. Nothing is sent when a run has no alerts (except the first baseline run, which sends the summary).

`slackWebhookUrl` receives a short message listing up to 20 changes with document links.

### Pricing

You pay per alert, not per company. Each change row written to the dataset costs USD 0.02. Each run also costs USD 0.001 to start, which covers checking your whole list when nothing changed. Platform usage is included.

Example: a daily schedule watching 50 companies that together produce 10 changes a month costs about USD 0.20 for the alerts plus USD 0.03 for the 30 runs. The first baseline run writes no alert rows and is charged only the start fee, unless you use `initialLookbackDays`.

### Limits and notes

- Filing history is compared on the latest 100 entries per company. If a company files more than 100 documents between two runs, older ones in that batch are not reported.
- Companies House allows 600 API calls per 5 minutes on the built-in key. A company with all watch types enabled uses about 5 calls, so a few hundred companies per run is comfortable. The actor pauses automatically when the limit is near.
- Status, address and name changes are detected by comparing snapshots, so they appear on the run after the register changes. On the first run with a lookback, dissolutions and past name changes are reported from their dates; address changes have no date and are not reported on a baseline run.
- If a company number is not found it is listed in `not_found` in the summary and skipped.
- If the Companies House API is down for a company, its previous snapshot is kept so nothing is missed on the next run.

# Actor input Schema

## `companyNumbers` (type: `array`):

Companies House company numbers to watch, e.g. 00102498 (BP), 00445790 (Tesco). Up to a few hundred per watchlist.

## `officerIds` (type: `array`):

Companies House officer IDs (the part after /officers/ in a Companies House URL). Alerts when that person takes or leaves an appointment at any company.

## `watch` (type: `array`):

Which kinds of change raise an alert. Leave all selected to watch everything.

## `initialLookbackDays` (type: `integer`):

On the very first run for a watchlist, also report changes dated within this many days. 0 means the first run only saves a baseline and reports nothing. Use 30 to see recent activity immediately.

## `watchlistName` (type: `string`):

Name for this watchlist. The actor keeps its last snapshot in a key-value store named companies-house-monitor-<name>. If left empty, the name is derived from the list of company numbers, so changing the list starts a fresh baseline.

## `webhookUrl` (type: `string`):

HTTPS endpoint that receives one POST per run with the run summary and the list of alerts as JSON.

## `slackWebhookUrl` (type: `string`):

Slack incoming webhook. One message per run listing the changes.

## `maxAlertsPerRun` (type: `integer`):

Cap on alert rows delivered (and charged) in one run. Extra changes are counted in the run summary but not delivered.

## `apiKey` (type: `string`):

Leave blank to use the built-in key. Provide your own free key from developer.company-information.service.gov.uk if you want your own rate limit.

## Actor input object example

```json
{
  "companyNumbers": [
    "00102498",
    "SC327000",
    "12345678"
  ],
  "officerIds": [],
  "watch": [
    "new_filings",
    "officers",
    "psc",
    "status",
    "registered_office",
    "charges",
    "name_change"
  ],
  "initialLookbackDays": 30,
  "watchlistName": "my-portfolio",
  "maxAlertsPerRun": 500
}
```

# Actor output Schema

## `alerts` (type: `string`):

Dataset rows, one per change

## `summary` (type: `string`):

Counts for this run

# 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 = {
    "companyNumbers": [
        "00102498",
        "00445790",
        "00048839"
    ],
    "officerIds": [],
    "watch": [
        "new_filings",
        "officers",
        "psc",
        "status",
        "registered_office",
        "charges",
        "name_change"
    ],
    "initialLookbackDays": 30,
    "watchlistName": "my-portfolio"
};

// Run the Actor and wait for it to finish
const run = await client.actor("skillmerc/companies-house-monitor").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 = {
    "companyNumbers": [
        "00102498",
        "00445790",
        "00048839",
    ],
    "officerIds": [],
    "watch": [
        "new_filings",
        "officers",
        "psc",
        "status",
        "registered_office",
        "charges",
        "name_change",
    ],
    "initialLookbackDays": 30,
    "watchlistName": "my-portfolio",
}

# Run the Actor and wait for it to finish
run = client.actor("skillmerc/companies-house-monitor").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 '{
  "companyNumbers": [
    "00102498",
    "00445790",
    "00048839"
  ],
  "officerIds": [],
  "watch": [
    "new_filings",
    "officers",
    "psc",
    "status",
    "registered_office",
    "charges",
    "name_change"
  ],
  "initialLookbackDays": 30,
  "watchlistName": "my-portfolio"
}' |
apify call skillmerc/companies-house-monitor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,skillmerc/companies-house-monitor"
        }
    }
}
```

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/ZfOMOaE1d47VO03WH/builds/7r6Whx8Jvptfd1kxh/openapi.json
