# UK Company Check: Companies House Due Diligence & Risk API (`reynolds/uk-company-due-diligence`) Actor

UK company due diligence from the official Companies House API: profile, status, SIC, accounts and confirmation statement due or overdue, recent filings, officers with appointment counts, PSCs, charges, insolvency and a 0-100 risk score with plain-English flags. MCP server for AI agents.

- **URL**: https://apify.com/reynolds/uk-company-due-diligence.md
- **Developed by:** [Reece Reynolds](https://apify.com/reynolds) (community)
- **Stats:** 1 total users, 0 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $10.00 / 1,000 company due-diligence packs

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.

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 Company Check: Companies House Due Diligence & Risk API

Check any UK company in one call. Give a company number or a name and get back a single due-diligence pack built from the official Companies House API: status, age, SIC codes, registered office, accounts and confirmation statement (due or overdue), recent filings, current and resigned officers with how many other companies they run, persons with significant control (PSCs), charges, insolvency, and a 0-100 risk score with plain-English flags.

Built for KYB and supplier checks, sales and credit screening, and AI agents. It runs as a normal Actor, as an MCP server (one tool, `get_uk_company_pack`) and as an HTTP API.

Not affiliated with or endorsed by Companies House. Contains public sector information licensed under the Open Government Licence v3.0.

### Why this Actor

- **One pack, not ten scrapes.** Profile, filing compliance, filings, officers, PSCs, charges and insolvency in one item, instead of separate Actors and joins.
- **A risk verdict you can read.** A 0-100 score with flags like "Annual accounts are overdue (were due 2026-03-31, 209 days ago)" or "First Gazette notice for compulsory strike-off filed on 2026-09-01".
- **Names work.** "Tesco" resolves to TESCO PLC (00445790) and the closest alternatives come back with it, so an agent can confirm the right entity.
- **Official API only.** No scraping of the register website, no proxies.
- **Privacy built in.** No home-address detail for individuals and month and year of birth only (see "Data, privacy and lawful basis").
- **Fair pricing.** No start fee. Not-found and failed lookups are free.

### Input

| Field | Type | Default | Notes |
|---|---|---|---|
| `companies` | list of strings | required | Company numbers (`00445790`, `SC123456`, `OC300001`), register URLs, or names (`Tesco PLC`). Up to 500 per run. |
| `maxFilings` | integer | 25 | Recent filings per company (0-100). |
| `includeResignedOfficers` | boolean | true | Also list former officers. Counts are always included. |
| `includeAppointmentCounts` | boolean | true | Count each officer's other appointments and how many of those companies are dissolved or insolvent. |
| `maxAppointmentLookups` | integer | 15 | Cap on officer lookups per company (current officers first, then those who left in the last 12 months). |
| `apiKey` | secret string | none | Optional: your own free Companies House key. Leave empty to use the Actor's key. |

```json
{ "companies": ["00445790", "Tesco PLC", "SC002934"], "maxFilings": 10 }
```

### Output

One dataset item per company. Views in the Output tab: **Overview**, **Profile**, **Risk flags**. Abridged example (synthetic data):

```json
{
  "input": "Tesco",
  "status": "ok",
  "companyNumber": "00445790",
  "companyName": "TESCO PLC",
  "registerUrl": "https://find-and-update.company-information.service.gov.uk/company/00445790",
  "resolution": { "method": "name-search", "confidence": "exact", "alternatives": [{ "companyNumber": "00519500", "name": "TESCO STORES LIMITED", "score": 52 }] },
  "profile": { "status": "active", "typeLabel": "Public limited company", "incorporatedOn": "1947-11-27", "ageYears": 78.9,
    "sicCodes": [{ "code": "47110", "description": "Retail sale in non-specialised stores with food, beverages or tobacco predominating" }],
    "registeredAddressText": "Tesco House, Shire Park, Kestrel Way, Welwyn Garden City, AL7 1GA, United Kingdom" },
  "accounts": { "lastMadeUpTo": "2026-02-28", "nextDue": "2027-08-28", "overdue": false, "daysUntilDue": 306 },
  "confirmationStatement": { "nextDue": "2027-06-29", "overdue": false },
  "officers": { "activeCount": 4, "resignedCount": 2, "items": [
    { "name": "EXAMPLE, Alice Jane", "roleLabel": "Director", "appointedOn": "2020-10-01", "dateOfBirth": { "month": 3, "year": 1967 },
      "addressArea": "Welwyn Garden City, United Kingdom", "appointments": { "total": 3, "active": 3, "dissolvedOrClosed": 0, "insolvent": 0 } } ] },
  "pscs": { "activeCount": 0, "statements": ["The company knows or has reasonable cause to believe that there is no registrable person ..."] },
  "charges": { "total": 3, "outstanding": 1, "satisfied": 2 },
  "insolvency": { "hasCases": false, "cases": [] },
  "filings": { "totalCount": 3000, "items": [{ "date": "2026-07-01", "type": "AP01", "description": "Appointment of Ms Alice Jane Example as a director on 2026-07-01", "documentUrl": "https://find-and-update.company-information.service.gov.uk/..." }] },
  "risk": { "score": 3, "band": "low", "summary": "Low risk (3/100): no adverse register signals found (accounts filed and up to date, confirmation statement up to date).", "flags": [{ "code": "OUTSTANDING_CHARGES", "severity": "info", "points": 3, "message": "1 outstanding charge(s) (secured lending). Normal for many trading companies." }],
    "positives": ["Accounts filed and up to date", "Confirmation statement up to date", "Trading history of 78 years"] }
}
```

If a lookup fails the item has `status: "error"`, an `errorCode` (`BAD_INPUT`, `NOT_FOUND`, `NO_API_KEY`, `RATE_LIMITED`, `UPSTREAM`) and an `error` message. Name searches with no good match return `NOT_FOUND` with `alternatives` to try. Error items are never charged. The run summary is saved as `OUTPUT_SUMMARY` in the key-value store.

### Risk score

Points are added for each adverse register fact and capped at 100. Higher means riskier. Bands: low 0-19, moderate 20-39, high 40-69, severe 70-100.

| Signal | Points |
|---|---|
| Dissolved or closed | 60, score at least 90 |
| In liquidation, administration or receivership | 60, score at least 85 |
| Active proposal to strike off | 45, score at least 70 |
| Sanctioned PSC | 50, score at least 80 |
| Gazette notice for compulsory / voluntary strike-off (last 12 months) | 35 / 30 |
| Voluntary arrangement (CVA) | 35 |
| Insolvency history on file / previously liquidated | 30 / 15 |
| Accounts overdue (more than 90 days) | 20 (30) |
| Confirmation statement overdue | 15 |
| Incorporated under 1 year / under 2 years | 20 / 8 |
| No current officers | 20 |
| Registered office undeliverable / in dispute | 15 / 12 |
| Director churn in the last 12 months | 6-12 |
| Dormant accounts | 12 |
| Every officer appointed in the last 12 months | 10 |
| Officer linked to 5+ dissolved or insolvent companies | 8 |
| No PSC and no PSC statement | 6 |
| Officer with 20+ appointments (nominee pattern) | 5 |
| Name change in the last 12 months | 5 |
| New charges in the last 12 months / outstanding charges | 4 / 3 |

Filing-compliance and age flags are skipped once a company is dissolved or insolvent. Officer flags ignore corporate officers such as company secretarial firms. This is a transparent screening aid built only from register facts. It is not a credit score and says nothing about financial performance.

### Pricing

Pay per event, no start fee:

| Event | Price |
|---|---|
| Company pack (one company found and returned) | $0.01 |

Not-found, invalid and failed lookups are free. 100 companies cost $1.00. A monthly re-check of 50 suppliers costs $0.50 a month. If you set a maximum cost per run, the Actor stops cleanly when it is reached.

### Use with AI agents (MCP)

The Actor is an MCP server with one read-only tool, `get_uk_company_pack` (inputs: `company`, `maxFilings` (default 10), `includeAppointmentCounts`, `includeResignedOfficers`). An agent can ask "Is ACME LTD up to date with its filings?" or "Who controls 00445790 and have its directors run failed companies?" and get structured JSON back.

**Option 1: through the Apify MCP server (OAuth sign-in, easiest).** Add this to Claude Desktop (`claude_desktop_config.json`), Cursor (`.cursor/mcp.json`), VS Code or any client that supports remote MCP servers:

```json
{
  "mcpServers": {
    "uk-company-check": {
      "url": "https://mcp.apify.com/?tools=reynolds/uk-company-due-diligence"
    }
  }
}
```

Your client opens an Apify sign-in the first time. To use a token instead, add `"headers": { "Authorization": "Bearer <APIFY_TOKEN>" }`. Combine it with other Actors, for example `?tools=reynolds/uk-company-due-diligence,reynolds/domain-intel`. Claude Code: `claude mcp add --transport http uk-company-check "https://mcp.apify.com/?tools=reynolds/uk-company-due-diligence"`.

**Option 2: connect straight to the Actor's Standby MCP endpoint** (lowest latency, needs an Apify API token):

```json
{
  "mcpServers": {
    "uk-company-check": {
      "url": "https://reynolds--uk-company-due-diligence.apify.actor/mcp",
      "headers": { "Authorization": "Bearer <APIFY_TOKEN>" }
    }
  }
}
```

A plain HTTP endpoint is also available with the same header: `GET https://reynolds--uk-company-due-diligence.apify.actor/v1/company?q=00445790&maxFilings=10` (or `POST /v1/company` with `{"company": "Tesco PLC"}`). It returns 404 with `alternatives` when nothing matches.

Clients that only support local (stdio) servers can use `npx -y @apify/actors-mcp-server --tools reynolds/uk-company-due-diligence` with `APIFY_TOKEN` set in the environment.

### Integrations: API, n8n, Make, Zapier, LangChain

- **REST API** (run and get results in one call):

  ```bash
  curl -X POST "https://api.apify.com/v2/acts/reynolds~uk-company-due-diligence/run-sync-get-dataset-items" \
    -H "Authorization: Bearer $APIFY_TOKEN" -H "Content-Type: application/json" \
    -d '{"companies": ["00445790", "Tesco PLC"], "maxFilings": 5}'
  ```

- **n8n**: add the Apify node, choose "Run an Actor and get dataset", Actor `reynolds/uk-company-due-diligence`, and pass the input JSON. Put a Schedule trigger in front and an IF node on `risk.band` after it to alert when a supplier moves to high or severe.

- **Make** and **Zapier**: use the Apify app's "Run an Actor" action with the same Actor and input, then read the dataset items in the next step.

- **LangChain** (Python):

  ```python
  from langchain_apify import ApifyActorsTool
  uk_company_tool = ApifyActorsTool("reynolds/uk-company-due-diligence")
  # give uk_company_tool to your agent; it takes the Actor input as JSON
  ```

### Companies House API key

The Actor reads the free official Companies House API. The hosted Actor has a key configured, so most users need nothing. To use your own key (higher throughput for big batches):

1. Go to developer.company-information.service.gov.uk and register or sign in.
2. Create an application and choose the **Live** environment.
3. Add a **REST** API key to the application.
4. Paste it into the `apiKey` input (stored as a secret), or set the environment variable `COMPANIES_HOUSE_API_KEY` when running the code yourself.

Companies House allows 600 requests per 5 minutes per key. A full pack uses about 6 requests plus one per officer looked up, so a run handles roughly 25-100 companies a minute depending on settings. The Actor throttles itself and backs off if limited.

### Data, privacy and lawful basis

- **Source.** Only the official Companies House Public Data API. The register is public by law under the Companies Act 2006, and the data is published under the Open Government Licence v3.0.
- **Minimisation.** For individual officers and PSCs the Actor drops premises, street and postcode and keeps only the town and country of the published correspondence address. Date of birth is month and year only. Insolvency practitioners are returned by name and role only. Corporate officers and PSCs keep their full registered address.
- **Your responsibility.** Officer and PSC names are still personal data under UK GDPR. You are the controller of what you collect and you need a lawful basis. For due diligence, KYB, anti-money-laundering checks, credit and supplier vetting, that is usually legitimate interests (Article 6(1)(f)) or a legal obligation (Article 6(1)(c)). Use the data only for the purpose the register exists for, keep it no longer than needed, and do not use officer details for unsolicited marketing (PECR applies to marketing emails, calls and texts).
- **Accuracy and corrections.** Data is as filed with Companies House. Errors must be corrected at Companies House, and each item links to the register page. Officers can ask Companies House to suppress some details.
- **No storage by the Actor.** Results go only to your own dataset. Nothing is kept between runs.

### FAQ

**Is this the same as the Companies House website?** It uses the same official register, through its API, and joins the separate sections into one pack with a risk verdict.

**What if a name matches several companies?** You get the best match with `resolution.confidence` and up to five `alternatives`. If nothing scores well you get `NOT_FOUND` with suggestions and pay nothing. Use the company number for certainty.

**Does it cover Scotland, Northern Ireland and LLPs?** Yes. Any entity on the UK register, including `SC`, `NI`, `OC`, `SO`, `NC` and other prefixes.

**Does it include financial figures?** No. It reports accounts type and filing dates, and links to each filed document. Turnover and profit from iXBRL accounts are not parsed yet.

**What does a failed lookup cost?** Nothing.

### Limitations

- The risk score uses register facts only. It does not use financial accounts, court judgments (CCJs), sanctions lists or credit data, beyond the register's own PSC sanctions marker.
- Appointment checks look at each officer's 50 most recent appointments. Roles inside the company's own group (same distinctive name word, e.g. "Tesco") are left out, and a company only counts as failed if it became insolvent or was dissolved while the person was still in office. Totals are complete.
- Officer lists are capped at 300 per company and charges at 50 (counts stay correct).
- Officers are matched by their Companies House officer ID, so the same person filed under different IDs is counted separately.

### Local development

```bash
npm install
npm run build
npm test                                  # unit tests on synthetic fixtures (no key needed)
npm run cli -- "Tesco" --fixtures         # offline demo
COMPANIES_HOUSE_API_KEY=... npm run cli -- 00445790 --filings 5
COMPANIES_HOUSE_API_KEY=... npm run smoke # live smoke test
npm run start:mcp                         # MCP over stdio
PORT=8787 npm run start:http              # HTTP API; UKC_ENABLE_MCP_HTTP=1 also serves /mcp
```

The HTTP server also has an x402 pay-per-call stub for self-hosting (off by default, `X402_ENABLED=1`, $0.01 per successful pack; real payment verification is not implemented yet). Environment: `COMPANIES_HOUSE_API_KEY`, `UKC_CONTACT` (contact added to the User-Agent).

# Actor input Schema

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

One per line: a Companies House number (e.g. 00445790, SC123456, OC300001), a register URL, or a company name (e.g. Tesco PLC). Names resolve to the best match and the closest alternatives are returned. Up to 500 per run.

## `maxFilings` (type: `integer`):

How many recent filings to return per company (0-100).

## `includeResignedOfficers` (type: `boolean`):

Also list former directors and secretaries (counts are always included).

## `includeAppointmentCounts` (type: `boolean`):

Look up how many companies each current (and recently resigned) officer is appointed to, and how many of those are dissolved or insolvent. Powers the prolific-director and failed-companies flags. Uses one extra API call per officer.

## `maxAppointmentLookups` (type: `integer`):

Cap on officer appointment lookups per company (current officers first).

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

Your own free Companies House REST API key. Leave empty to use the key configured on the Actor. Get one at developer.company-information.service.gov.uk.

## Actor input object example

```json
{
  "companies": [
    "00445790",
    "Tesco PLC"
  ],
  "maxFilings": 25,
  "includeResignedOfficers": true,
  "includeAppointmentCounts": true,
  "maxAppointmentLookups": 15
}
```

# Actor output Schema

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

All dataset items, one per company (status ok or error). Opens the Overview view.

## `profile` (type: `string`):

Register facts per company: type, SIC codes, registered office, charges, insolvency.

## `risk` (type: `string`):

Risk score, band, flags and positives per company.

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

OUTPUT_SUMMARY record: {companies, ok, failed} 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 = {
    "companies": [
        "00445790",
        "Tesco PLC"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("reynolds/uk-company-due-diligence").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",
        "Tesco PLC",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("reynolds/uk-company-due-diligence").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",
    "Tesco PLC"
  ]
}' |
apify call reynolds/uk-company-due-diligence --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,reynolds/uk-company-due-diligence"
        }
    }
}
```

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/8Pn7oF15VbGAQrZtG/builds/J14EpYEfdUFz4f9np/openapi.json
