# Companies House Search: UK Company Lookup & Monitor (`fernandosord/uk-company-lookup-companies-house`) Actor

Look up UK companies on the Companies House register by number, name or LEI, without an API key: status, address, SIC codes, filing due dates, overdue flags, previous names, LEI and parent companies. Monitor mode reports changes. Add your free API key for officers, PSC, filings and charges.

- **URL**: https://apify.com/fernandosord/uk-company-lookup-companies-house.md
- **Developed by:** [Plainsource](https://apify.com/fernandosord) (community)
- **Categories:** Lead generation, Business
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $3.00 / 1,000 company looked ups

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

### What this Actor does

UK Company Lookup checks companies on the UK Companies House register and returns one clean JSON row per company. It needs no API key.

Give it any mix of:

- **company numbers** such as `00445790` or `SC045551`,
- **company names** such as `Marks and Spencer Group` (fuzzy match, with a scored candidate list),
- **LEIs** (20-character Legal Entity Identifiers).

For each company you get the registered name, status, company type, incorporation and dissolution dates, registered office address, SIC codes with descriptions, accounts and confirmation statement dates with overdue flags, previous names, charge counts, the LEI if one exists, and the direct and ultimate parent companies from the GLEIF database.

Turn on **Monitor mode** and schedule the Actor, and each run reports what changed since the last one: name, status, address, SIC codes, filing due dates, overdue flags, LEI or ultimate parent. A Slack, Discord or JSON webhook can alert you.

If you have your own free Companies House API key, add it and each company also gets its officers, persons with significant control (PSC), recent filing history and charges.

Typical uses: supplier and customer checks (KYB), enriching a CRM with company numbers and SIC codes, watching clients or debtors for strike-off, liquidation or overdue accounts, and giving an AI agent a reliable UK company lookup tool.

### How it works

| Data | Source | Key needed |
|---|---|---|
| Company profile by number or LEI | Companies House [URI service](https://www.gov.uk/guidance/companies-house-data-products#uri-info) (live register data, including dissolved companies) | No |
| Name search | An index of the Companies House [free company data product](https://download.companieshouse.gov.uk/en_output.html), a monthly snapshot of every live company, rebuilt each month. LEI holders are also searched in GLEIF. | No |
| LEI, direct and ultimate parent | [GLEIF API](https://www.gleif.org/en/lei-data/gleif-api) (CC0 data) | No |
| Officers, PSC, filing history, charges; name search that includes dissolved companies | [Companies House REST API](https://developer.company-information.service.gov.uk/) | Your own key |

Name search runs against the monthly snapshot, so it finds live companies by their current or previous names. The profile is always fetched live from the register, so a company that changed since the snapshot is still shown as it is today.

### Input

The only required field is **Companies**. All settings:

| Field | Default | What it does |
|---|---|---|
| **Companies** (`companies`) | none | One company number, name or LEI per line. Type is detected automatically; start a line with `number:`, `name:` or `lei:` to force it. |
| **Candidates listed per name search** (`maxCandidates`) | `5` | 0 to 20 closest matches listed in `candidates` for each name. |
| **Minimum name match score** (`minNameScore`) | `60` | 30 to 100. Below it, a name search returns `not_found` with the candidates. |
| **Return every matching candidate in full** (`lookupAllCandidates`) | `false` | On: each candidate above the minimum gets its own full row (each charged as a lookup). |
| **Add LEI and parent companies (GLEIF)** (`includeLei`) | `true` | LEI record plus direct and ultimate parents. |
| **Companies House API key** (`companiesHouseApiKey`) | empty | Your own REST API key. Stored encrypted, sent only to Companies House. |
| **Officers** (`includeOfficers`) | `true` | With a key: current officers. |
| **Persons with significant control** (`includePsc`) | `true` | With a key: current PSCs. |
| **Filing history** (`includeFilingHistory`) | `true` | With a key: most recent filings. |
| **Filings per company** (`filingHistoryLimit`) | `10` | 1 to 100. |
| **Charges** (`includeCharges`) | `true` | With a key: registered charges and lenders. |
| **Include resigned officers and ceased PSCs** (`includeResigned`) | `false` | With a key: also past officers and PSCs. |
| **Monitor mode** (`monitor`) | `false` | Compare with the previous run and report changes. |
| **Watch list name** (`stateKey`) | `default` | Saved versions are kept per watch list. Use one per task. |
| **Only output changed companies** (`onlyChanges`) | `false` | In monitor mode, push only changed companies. |
| **Webhook URL** (`webhookUrl`) | empty | https URL that gets a POST when at least one company changed. |
| **Webhook format** (`webhookFormat`) | `json` | `json`, `slack` or `discord`. |

Example input that watches four companies and posts changes to Slack:

```json
{
    "companies": ["00445790", "SC045551", "Marks and Spencer Group", "2138002P5RNKC5W2JZ46"],
    "monitor": true,
    "stateKey": "key-suppliers",
    "webhookUrl": "https://hooks.slack.com/services/XXX/YYY/ZZZ",
    "webhookFormat": "slack"
}
```

### Output

One row per company. This is a real row from a test run, looked up by LEI:

```json
{
    "query": "lei:213800ET4MA6J4AHS397",
    "queryType": "lei",
    "status": "found",
    "companyNumber": "00519500",
    "companyName": "TESCO STORES LIMITED",
    "companyStatus": "active",
    "companyStatusDetail": null,
    "companyStatusText": "Active",
    "companyType": "Private Limited Company",
    "countryOfOrigin": "United Kingdom",
    "incorporationDate": "1953-05-09",
    "dissolutionDate": null,
    "registeredAddress": {
        "careOf": null,
        "poBox": null,
        "premises": null,
        "addressLine1": "TESCO HOUSE, SHIRE PARK",
        "addressLine2": "KESTREL WAY",
        "locality": "WELWYN GARDEN CITY",
        "region": null,
        "postalCode": "AL7 1GA",
        "country": "UNITED KINGDOM"
    },
    "registeredAddressText": "TESCO HOUSE, SHIRE PARK, KESTREL WAY, WELWYN GARDEN CITY, AL7 1GA, UNITED KINGDOM",
    "sicCodes": [
        { "code": "47110", "description": "Retail sale in non-specialised stores with food, beverages or tobacco predominating" },
        { "code": "47290", "description": "Other retail sale of food in specialised stores" },
        { "code": "47710", "description": "Retail sale of clothing in specialised stores" },
        { "code": "47750", "description": "Retail sale of cosmetic and toilet articles in specialised stores" }
    ],
    "accounts": {
        "accountingReferenceDate": "28-02",
        "lastMadeUpTo": "2025-02-22",
        "nextDue": "2026-11-30",
        "category": "FULL",
        "overdue": false
    },
    "confirmationStatement": { "lastMadeUpTo": "2026-06-18", "nextDue": "2027-07-02", "overdue": false },
    "overdue": false,
    "previousNames": [{ "name": "J. COLYER (FURNISHERS) LIMITED", "changedOn": "1987-03-02" }],
    "charges": { "total": 136, "outstanding": 91, "partSatisfied": 0, "satisfied": 45 },
    "hasInsolvencyHistory": null,
    "lei": "213800ET4MA6J4AHS397",
    "leiRecord": {
        "lei": "213800ET4MA6J4AHS397",
        "legalName": "TESCO STORES LIMITED",
        "entityStatus": "ACTIVE",
        "registrationStatus": "ISSUED",
        "initialRegistrationDate": "2013-12-19",
        "lastUpdateDate": "2025-11-21",
        "nextRenewalDate": "2027-01-12",
        "legalFormId": "H0PO",
        "headquartersCountry": "GB",
        "registeredAs": "00519500",
        "registrationAuthority": "RA000585",
        "recordUrl": "https://search.gleif.org/#/record/213800ET4MA6J4AHS397"
    },
    "directParent": {
        "lei": "2138002P5RNKC5W2JZ46",
        "name": "TESCO PLC",
        "country": "GB",
        "registeredAs": "00445790",
        "registrationAuthority": "RA000585",
        "entityStatus": "ACTIVE"
    },
    "directParentException": null,
    "ultimateParent": {
        "lei": "2138002P5RNKC5W2JZ46",
        "name": "TESCO PLC",
        "country": "GB",
        "registeredAs": "00445790",
        "registrationAuthority": "RA000585",
        "entityStatus": "ACTIVE"
    },
    "ultimateParentException": null,
    "nameMatch": null,
    "candidates": [],
    "officers": null,
    "officerCounts": null,
    "psc": null,
    "pscCounts": null,
    "filingHistory": null,
    "chargeDetails": null,
    "changeStatus": null,
    "changes": [],
    "changeSummary": null,
    "previousCheckAt": null,
    "companiesHouseUrl": "https://find-and-update.company-information.service.gov.uk/company/00519500",
    "profileSource": "companies-house-uri",
    "checkedAt": "2026-10-26T20:21:53.470Z",
    "notes": [],
    "error": null
}
```

Field notes:

- `status` is `found`, `not_found`, `invalid`, `failed`, `skipped` (cost limit reached) or `duplicate` (the same company was already returned in this run). The reason is in `error`.
- `companyStatus` uses the Companies House API vocabulary (`active`, `dissolved`, `liquidation`, `administration`, ...). `companyStatusDetail` adds `active-proposal-to-strike-off` and similar. `companyStatusText` is the register's own wording.
- `accounts.overdue` and `confirmationStatement.overdue` are `true` when the due date has passed, and `null` for companies no longer on the live register. `overdue` is `true` when either is.
- `directParentException` and `ultimateParentException` give GLEIF's reason when no parent is reported, for example `NATURAL_PERSONS`, `NON_CONSOLIDATING` or `NO_KNOWN_PERSON`.
- For name searches, `nameMatch` shows the score (0 to 1) and the matched name, which may be a previous name (`isPreviousName`). `candidates` lists the closest matches with their company numbers.
- In monitor mode, `changeStatus` is `baseline`, `changed` or `unchanged`. Each item in `changes` has `field`, `label`, `before` and `after`, plus `added` and `removed` for officers and PSCs.
- With an API key: `officers` items have `name`, `role`, `appointedOn`, `resignedOn`, `nationality`, `occupation`, `countryOfResidence`, `isCorporate`, `corporateRegistrationNumber` and `appointmentId`. `psc` items have `name`, `kind`, `naturesOfControl`, `notifiedOn`, `ceasedOn`, `nationality`, `countryOfResidence` and, for companies, `registrationNumber`, `legalAuthority`, `legalForm` and `placeRegistered`. `filingHistory` items have `date`, `category`, `type`, `description`, `descriptionText`, `transactionId`, `pages` and `documentUrl`. `chargeDetails` items have `chargeCode`, `chargeNumber`, `status`, `classification`, `createdOn`, `deliveredOn`, `satisfiedOn`, `personsEntitled` and `particulars`.

The run also writes a summary to the `OUTPUT` record of its key-value store: counts per status, whether an API key was used, and the changed companies.

### Monitor mode

1. Create a task with your companies and turn on **Monitor mode**. Give it a **Watch list name**.
2. The first run saves a baseline (`changeStatus: "baseline"`).
3. Schedule the task (daily or weekly is usually enough; the register changes when companies file). Each later run reports `changed` or `unchanged`.

Watched fields: company name, status, registered office address, SIC codes, accounts due date, latest accounts date, confirmation statement due date, overdue flag, LEI and ultimate parent, and with an API key the current officers and PSCs. Saved versions live in the key-value store `plainsource-uk-company-lookup-state` in your own Apify account. A field that could not be checked in a run (GLEIF unavailable, or no API key) is not reported as changed.

### Using your own Companies House API key

The key is free. To get one:

1. Go to the [Companies House Developer Hub](https://developer.company-information.service.gov.uk/) and choose **Sign in / Register** to create a Companies House account.
2. Open **Your applications** and choose **Create an application**. Give it a name and description and choose the **Live** environment (not the test sandbox).
3. In the application, choose **Create new key**, pick **REST** as the key type and create it.
4. Paste the key into **Companies House API key**.

Each key allows 600 requests per 5 minutes. With all four extras on, a company takes about 5 requests, so roughly 120 companies per 5 minutes; the Actor waits when the limit is reached. If the key is rejected, the run continues without it and says so in `notes`.

### Pricing

You pay per event, not for compute time:

| Event | Price |
|---|---|
| Run start | $0.002 per run |
| Company looked up | $0.003 per company returned |
| Officers, PSC, filings and charges added (with your API key) | $0.002 per company |
| Change detected (monitor mode) | $0.005 per changed company |

Not charged: companies not found, invalid entries, failed lookups, duplicates, and name searches with no match above the minimum score. With **Only output changed companies** on, unchanged companies are still looked up and charged; only their rows are left out of the dataset. Example: 50 companies checked weekly in monitor mode with about 3 changes a month costs about 4.3 × (0.002 + 50 × 0.003) + 3 × 0.005 ≈ $0.67 a month.

If a run reaches the maximum cost you set, the remaining companies are marked `skipped`. In monitor mode their saved versions are kept, and changes that could not be charged are reported on the next run.

### Limits

- Up to 1,000 entries per run.
- Name search without a key covers companies that were live at the last monthly snapshot (about 5.7 million, current and previous names). Dissolved companies are found by number, by LEI, or by name with an API key.
- Name searches are slower than lookups by number. At the default 1024 MB, a name made of common words (Construction, Property, Trading) can take several seconds, and results are written only after every name is matched. For more than a few hundred names, give the run 2048 MB and a longer timeout, or split the list.
- Name matching is fuzzy, but at least one distinctive word of the name must start with the same three letters as on the register. A typo in the first letters of every word is not found.
- For industrial and provident societies, ICVCs and Royal Charter bodies, Companies House holds only the name and number; the row says so in `notes`.
- GLEIF allows 60 requests a minute. A company with an LEI needs up to 3 GLEIF requests and each name search 1 more, so large lists of LEI holders or names take longer. Turn off **Add LEI and parent companies** if you do not need them.
- The Companies House URI service and REST API are run by Companies House; if they are unavailable, the affected rows are `failed` and not charged.

### Data sources and terms

- Companies House says on its [data products page](https://www.gov.uk/guidance/companies-house-data-products): "We impose no rules or requirements on how the information on the public register is used". You remain responsible for data protection law when you store or use the data.
- Officers and PSCs come only from the official API with your key, and only as published on the public register. Addresses and dates of birth of people are never included.
- LEI data is from GLEIF under CC0. This Actor is not provided, endorsed or supported by GLEIF or Companies House.
- EU VAT (VIES) data is not used.

### FAQ

**Why did my name search pick the wrong company?** Several companies often share similar names. Check `candidates` and look the right one up by its company number, or raise **Minimum name match score**.

**Why is a dissolved company not found by name?** The free name index contains live companies only. Use its company number or LEI, or add an API key, which searches dissolved companies too.

**Do I need an API key?** No. Profiles, filing dates, overdue flags, LEIs, parents and monitoring all work without one. The key adds officers, PSCs, filing history and charges.

**How fresh is the data?** Profiles come from the live register at run time. Only the name index is monthly; `nameMatch.source` gives its snapshot date, and if the index is more than 50 days old, name-search rows say so in `notes`.

**Can I call it from code or an AI agent?** Yes. Use the Apify API or the Apify MCP server. One field takes numbers, names and LEIs, and every row has the same shape, with `status` and `error` explaining any miss. Example with the API client: `client.actor("fernandosord/uk-company-lookup-companies-house").call({ companies: ["00445790"] })`.

Found a bug or need a field? Open an issue on this Actor's Issues tab.

# Actor input Schema

## `companies` (type: `array`):

One company per line: a Companies House number (<code>00445790</code>, <code>SC045551</code>), a company name (<code>Marks and Spencer Group</code>) or a 20-character LEI. The type is detected automatically; start a line with <code>number:</code>, <code>name:</code> or <code>lei:</code> to force it.

## `maxCandidates` (type: `integer`):

For each company name, the closest matches are listed in <code>candidates</code> with their company number and match score. Set to 0 to leave the list out.

## `minNameScore` (type: `integer`):

A name search returns a company only when its name scores at least this much (0 to 100) against the name you entered. Below it, the row is <code>not_found</code> and lists the candidates, so you can pick one by number.

## `lookupAllCandidates` (type: `boolean`):

Off: a name search returns the best match only. On: every candidate above the minimum score (up to the number listed) gets its own full row, and each is charged as a lookup.

## `includeLei` (type: `boolean`):

Look up the company's Legal Entity Identifier and its direct and ultimate parents in the GLEIF database. Free and keyless; turn off for slightly faster runs.

## `companiesHouseApiKey` (type: `string`):

Optional. Your own free REST API key from the Companies House Developer Hub. With it, each company also gets officers, persons with significant control, recent filings and charges, and name search covers dissolved companies. The key is stored encrypted and only sent to Companies House.

## `includeOfficers` (type: `boolean`):

Directors, secretaries and LLP members: name, role, appointment date, nationality, occupation and country of residence. Addresses and dates of birth are never included.

## `includePsc` (type: `boolean`):

People and companies that control the company, with the nature of their control. Addresses and dates of birth are never included.

## `includeFilingHistory` (type: `boolean`):

The most recent filings, with date, type and a readable description.

## `filingHistoryLimit` (type: `integer`):

How many of the most recent filings to include.

## `includeCharges` (type: `boolean`):

Registered charges (mortgages and other security): status, dates and lenders.

## `includeResigned` (type: `boolean`):

Off: only current officers and persons with significant control.

## `monitor` (type: `boolean`):

Compare each company with the version saved by the previous run under the same watch list name, and report changes to name, status, registered address, SIC codes, filing due dates, overdue flags, LEI and ultimate parent, plus officers and PSCs when you use an API key. The first run saves a baseline.

## `stateKey` (type: `string`):

Saved versions are kept per watch list in the key-value store <code>plainsource-uk-company-lookup-state</code> of your account. Use a different name for each task. Renaming starts a fresh baseline.

## `onlyChanges` (type: `boolean`):

In monitor mode, push rows only for companies that changed. Useful when an integration acts on every new dataset row. Unchanged companies are still looked up and charged.

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

Optional. When at least one company changed, the run sends a POST to this https URL: a Slack or Discord incoming-webhook URL, or any endpoint that accepts JSON (Zapier, Make, n8n, your own server).

## `webhookFormat` (type: `string`):

<b>JSON</b> sends the changes as data. <b>Slack</b> and <b>Discord</b> send a readable message.

## Actor input object example

```json
{
  "companies": [
    "00445790",
    "SC045551",
    "07524813"
  ],
  "maxCandidates": 5,
  "minNameScore": 60,
  "lookupAllCandidates": false,
  "includeLei": true,
  "includeOfficers": true,
  "includePsc": true,
  "includeFilingHistory": true,
  "filingHistoryLimit": 10,
  "includeCharges": true,
  "includeResigned": false,
  "monitor": false,
  "stateKey": "default",
  "onlyChanges": false,
  "webhookFormat": "json"
}
```

# Actor output Schema

## `results` (type: `string`):

One row per company: profile, filing deadlines, LEI and parents, candidates for name searches, and changes in monitor mode.

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

Counts per status, whether an API key was used, and the list of changed companies.

# 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 = {
    "companies": [
        "00445790",
        "SC045551",
        "07524813"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("fernandosord/uk-company-lookup-companies-house").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 = { "companies": [
        "00445790",
        "SC045551",
        "07524813",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("fernandosord/uk-company-lookup-companies-house").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 '{
  "companies": [
    "00445790",
    "SC045551",
    "07524813"
  ]
}' |
apify call fernandosord/uk-company-lookup-companies-house --silent --output-dataset

```

## MCP server setup

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

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/CEpGEJDlbQHFZNjCn/builds/VbfsScggJdG0OctDS/openapi.json
