# Companies House UK Scraper — Companies, Directors & PSCs (`khadinakbar/companies-house-uk-scraper`) Actor

UK Companies House lookup: company profiles, directors & PSCs. Zero-config public HTML register; optional free API key preferred when set. HTTP-only, MCP-ready.

- **URL**: https://apify.com/khadinakbar/companies-house-uk-scraper.md
- **Developed by:** [Khadin Akbar](https://apify.com/khadinakbar) (community)
- **Categories:** Lead generation, MCP servers
- **Stats:** 1 total users, 0 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $4.00 / 1,000 uk company profile scrapeds

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

## Companies House UK Scraper — Companies, Directors & PSCs

Turn a UK **company number**, **company name**, **director name**, or Companies House URL into **one structured company row** with nested **directors (officers)** and **persons with significant control (PSCs)**.

Works **zero-config** against the public Companies House HTML register. When you supply an optional free Public Data API key, the Actor prefers that JSON API path instead. HTTP-only — no browser login and no residential proxy required for the default path.

Designed for KYB, vendor onboarding, UK lead enrichment, and MCP agents that need a predictable company profile.

### Best fit

- You already have company numbers such as `00000006` or `SC123456` and want officers + PSCs in one call.
- You only know a trading name and need Companies House search → enrich.
- An agent needs public UK register data without managing browser sessions or residential proxies.

For the next workflow step after UK register enrichment, continue with [SEC EDGAR All-in-One Scraper](https://apify.com/khadinakbar/sec-edgar-all-in-one-scraper) when you need US filings, or [USPTO Trademark Batch Search](https://apify.com/khadinakbar/uspto-trademark-batch-search) for US trademarks. This Actor stays a focused live lookup / search enricher for Companies House.

### Practical scenario

A compliance analyst pastes `companyNumbers: ["00000006"]`, leaves `includeOfficers` and `includePscs` on, and caps `maxResults` at `1`. The Actor starts from the public Companies House register (no API key required), then loads the company profile, lists active officers and PSCs, and exports one `company-scraped` row with nested arrays plus a public `sourceUrl`. Optional free Public Data API key is preferred when set. An unknown number finishes `SUCCEEDED` with `VALID_EMPTY` and no company charge.

### Quick start input

```json
{
  "companyNumbers": ["00000006"],
  "includeOfficers": true,
  "includePscs": true,
  "maxResults": 1
}
```

### Input reference

| Field | Type | What it controls |
|---|---|---|
| `companyNumbers` | array | Exact 8-character company numbers (English padding, SC/NI/OC prefixes supported) |
| `companyNames` / `searchQueries` | array | Name search → enrich top hits |
| `officerNames` | array | Director search → company appointments → enrich |
| `companyUrls` | array | Public Companies House `/company/{number}` URLs |
| `includeOfficers` / `includePscs` | boolean | Nested directors and PSCs (default true) |
| `includeResignedOfficers` / `includeCeasedPscs` | boolean | Historical officers/PSCs (default false) |
| `emitOfficerLeadRows` | boolean | Extra flat `officer-lead` dataset rows (default false) |
| `maxResults` | integer | Billed company row cap (default 10, max 100) |
| `companiesHouseApiKey` | secret | Optional BYOK for official API; omit for zero-config public HTML |

### What data you receive

One primary dataset item is one company profile.

```json
{
  "recordType": "company",
  "companyNumber": "00000006",
  "companyName": "EXAMPLE COMPANY LIMITED",
  "companyStatus": "active",
  "companyType": "ltd",
  "dateOfCreation": "1909-04-01",
  "jurisdiction": "england-wales",
  "registeredOfficeAddressFormatted": "1 Example Street, London, E1 1AA",
  "sicCodes": ["62012"],
  "officerCount": 2,
  "pscCount": 1,
  "officers": [
    {
      "recordType": "officer",
      "name": "DOE, Jane",
      "officerRole": "director",
      "appointedOn": "2020-01-01",
      "isActive": true
    }
  ],
  "pscs": [
    {
      "recordType": "psc",
      "name": "DOE, Jane",
      "kind": "individual-person-with-significant-control",
      "naturesOfControl": ["ownership-of-shares-75-to-100-percent"],
      "isActive": true
    }
  ],
  "source": "companies-house-public-html",
  "sourceUrl": "https://find-and-update.company-information.service.gov.uk/company/00000006",
  "fetchedAt": "2026-09-22T12:00:00.000Z"
}
```

| Field | Meaning |
|---|---|
| `companyNumber` + `companyName` | Canonical register identity |
| `officers[]` | Directors and other officers (public register view) |
| `pscs[]` | Persons with significant control notifications |
| `sourceUrl` | Official public company page |
| `fetchedAt` | ISO-8601 fetch time |

`OUTPUT` and `RUN_SUMMARY` hold `outcome`, `itemsPushed`, and `chargedEventCounts`. Outcomes include `COMPLETE`, `PARTIAL`, `VALID_EMPTY`, `INVALID_INPUT`, `UPSTREAM_FAILED`, and `CONFIG_ERROR`.

### Use through the API

```bash
curl -X POST "https://api.apify.com/v2/acts/khadinakbar~companies-house-uk-scraper/runs" \
  -H "Authorization: Bearer $APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"companyNumbers":["00000006"],"includeOfficers":true,"includePscs":true,"maxResults":1}'
```

Then read the dataset and the `OUTPUT` / `RUN_SUMMARY` key-value records for outcome and charge reconciliation. Download rows as JSON, CSV, Excel, or HTML from the Dataset tab.

### Use with AI agents through Apify MCP

> Look up UK company 00000006 on Companies House and return company status, registered office, directors, and PSCs as structured JSON. Cap at 1 company, then report OUTPUT.outcome and chargedEventCounts.

Claude and other MCP clients should prefer company numbers when known; otherwise search by company or officer name. After the run, read dataset rows plus `OUTPUT.outcome` for provenance and cost. Scope is bounded by `maxResults` and `maxSearchResultsPerQuery` (no infinite scroll cursor in v1). Pair filings or trademark work with the sibling Actors linked above.

### Pricing

Pay per event + platform usage pass-through (caller pays Apify platform usage). Open the live Pricing tab for the current schedule.

| Event | Price | When |
|---|---|---|
| `apify-actor-start` | $0.00005 | Every run |
| `company-scraped` | $0.004 | Each saved company row |
| `officer-lead` | $0.002 | Only if `emitOfficerLeadRows` is true |

Example: one company row → start event + one `company-scraped` ≈ $0.00405 plus platform usage. Invalid input and `VALID_EMPTY` runs are not charged the company event.

### Best results

- Provide company numbers when you have them; name and officer search are for discovery.
- Start with `maxResults` at 1–3 while validating a workflow, then schedule larger batches.
- Confirm the public `sourceUrl` on each row before downstream KYB decisions.
- Keep resigned officers and ceased PSCs off unless history is required.

### Builder's note

I built this after finding that requiring a Companies House API key stalled every first-run canary even though the public HTML register already exposes overview, officers, and PSC pages for the same numbers. My goal was a zero-config path agents can call immediately, with the official JSON API preferred only when a free key is present, and with nested public-register fields kept honest (no filings dump).

### Legal / responsible use

Public Companies House data only. Companies House is a UK government service. This independent Actor is not affiliated with, associated with, or endorsed by Companies House or the UK government. Follow applicable laws and UK data-protection rules for any downstream use of director or PSC personal data. This is not legal advice and not a substitute for official filings or identity verification products.

### FAQ

**Do I need my own API key?**\
No. The Actor works zero-config against the public HTML register. A free Companies House Public Data API key is optional and preferred when provided (owner env or BYOK input).

**Does this scrape every UK company?**\
No. It is a live lookup / search enricher, not a bulk snapshot of the full register.

**Are private contact fields included?**\
No. Only public register company, officer, and PSC fields.

# Actor input Schema

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

Exact UK company numbers to enrich, for example 00000006 or SC123456. Accepts 8-character Companies House numbers with optional SC/NI/OC prefixes. Use this when you already know the register number; do not put free-text company names here.

## `companyNames` (type: `array`):

Company name phrases to search on Companies House, for example TESCO PLC. Each query returns up to maxSearchResultsPerQuery matches that are then enriched. Use this for discovery when you do not have the company number yet.

## `searchQueries` (type: `array`):

Alias of companyNames for agents that prefer a generic searchQueries field. Same company-name search behavior.

## `officerNames` (type: `array`):

Director or officer names to search, for example Jane Smith. Returns companies linked through officer appointments, then enriches those company profiles. This is not a personal-data enrichment tool beyond the public register.

## `companyUrls` (type: `array`):

Public Companies House company page URLs. The Actor extracts the company number from paths like /company/00000006. Invalid non-company URLs become INVALID\_INPUT warnings.

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

When true (default), nest active officers on each company row. Turn off to save API quota when you only need the company profile and PSCs.

## `includePscs` (type: `boolean`):

When true (default), nest persons with significant control on each company row. Some companies have no published PSC list and return an empty array.

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

When true, keep resigned directors in the nested officers list. Default false returns currently appointed officers only.

## `includeCeasedPscs` (type: `boolean`):

When true, keep ceased persons with significant control. Default false returns active PSC notifications only.

## `emitOfficerLeadRows` (type: `boolean`):

When true, also write one billed officer-lead dataset row per nested officer in addition to the company row. Default false keeps one company-shaped row.

## `maxResults` (type: `integer`):

Hard cap on billed company-scraped rows for the run. Default 10 keeps KYB canaries cheap; raise toward 100 for batch enrichment.

## `maxSearchResultsPerQuery` (type: `integer`):

How many company or officer search hits to enrich for each name query before applying maxResults. Default 5.

## `maxConcurrency` (type: `integer`):

Reserved for future parallel enrichment. Current build processes jobs sequentially to respect the Companies House 600 requests / 5 minutes free quota.

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

Optional. The caller/user must supply their own Companies House Public Data API key for this run when they want the official API path. When omitted, the Actor uses the free public HTML register (zero-config). The value is never written to the dataset, output, or logs.

## Actor input object example

```json
{
  "companyNumbers": [
    "00000006"
  ],
  "includeOfficers": true,
  "includePscs": true,
  "includeResignedOfficers": false,
  "includeCeasedPscs": false,
  "emitOfficerLeadRows": false,
  "maxResults": 1,
  "maxSearchResultsPerQuery": 3,
  "maxConcurrency": 2
}
```

# Actor output Schema

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

Dataset items containing company profiles, nested directors and PSCs, or optional flat officer lead rows.

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

Run summary including outcome, itemsPushed, billing counters, and Companies House diagnostics.

## `runSummary` (type: `string`):

Machine-readable RUN\_SUMMARY record mirroring OUTPUT.

# 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": [
        "00000006"
    ],
    "includeOfficers": true,
    "includePscs": true,
    "includeResignedOfficers": false,
    "includeCeasedPscs": false,
    "emitOfficerLeadRows": false,
    "maxResults": 1,
    "maxSearchResultsPerQuery": 3,
    "maxConcurrency": 2
};

// Run the Actor and wait for it to finish
const run = await client.actor("khadinakbar/companies-house-uk-scraper").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": ["00000006"],
    "includeOfficers": True,
    "includePscs": True,
    "includeResignedOfficers": False,
    "includeCeasedPscs": False,
    "emitOfficerLeadRows": False,
    "maxResults": 1,
    "maxSearchResultsPerQuery": 3,
    "maxConcurrency": 2,
}

# Run the Actor and wait for it to finish
run = client.actor("khadinakbar/companies-house-uk-scraper").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": [
    "00000006"
  ],
  "includeOfficers": true,
  "includePscs": true,
  "includeResignedOfficers": false,
  "includeCeasedPscs": false,
  "emitOfficerLeadRows": false,
  "maxResults": 1,
  "maxSearchResultsPerQuery": 3,
  "maxConcurrency": 2
}' |
apify call khadinakbar/companies-house-uk-scraper --silent --output-dataset

```

## MCP server setup

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

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/IRRvAn8X2ll9zTIgh/builds/rUpK0yXiHjAZASD1N/openapi.json
