# Companies House UK Scraper (Officers & PSC) (`scrapyx/companieshouse-uk-scraper`) Actor

The UK companies register: search by name or look up company numbers for status, type, incorporation date, registered office and SIC codes, plus every officer and person with significant control. Restores the leading zeros spreadsheets strip from company numbers.

- **URL**: https://apify.com/scrapyx/companieshouse-uk-scraper.md
- **Developed by:** [Ibnu Adzim](https://apify.com/scrapyx) (community)
- **Categories:** Business, Lead generation, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.40 / 1,000 results

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?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Companies House UK Scraper (Officers & PSC)

The **UK companies register** — search by name or look up company numbers,
and get the full public record: status, type, incorporation date, registered
office, SIC codes and previous names, plus every **officer** (director and
secretary, current and resigned) and every **person with significant
control**.

No login. No API key. HTTP only. This reads the public register website, not
the key-gated REST API.

| Record type | One per | Carries |
| --- | --- | --- |
| `SEARCH_SUMMARY` | search run | term, register's own total, pages walked, whether the "no results" page was reached |
| `COMPANY` | company | number, name, status, type, incorporation date, registered office, SIC codes, previous names |
| `OFFICER` | appointment | name, role, **status (active/resigned)**, appointment and resignation dates, nationality, country of residence, occupation, correspondence address, redacted date of birth |
| `PSC` | beneficial owner | name, kind, nature of control, notified/ceased dates — plus DOB and nationality for individuals, or legal form, governing law and registration number for entities |
| `ERROR` | failed input | `_error` code and an `_errorDetail` saying what to change |

### Things this register will mislead you about

Each is measured, and each has a scenario in
`tests/smoke/companieshouse-uk-scraper_traps.sh` (17/17 passing).

**Your spreadsheet has already broken your company numbers.** A UK company
number is exactly eight characters and its leading zeros are part of it —
`00445790` is Tesco. Excel and Google Sheets strip those zeros the moment a
column is read as a number, so the commonest way this input arrives is
`445790`, which the register answers with a **404 that reads as "no such
company"** rather than "wrong format". This Actor restores the zeros, and
uppercases registrar prefixes for the same reason (`sc090312` is a 404,
`SC090312` is NatWest Markets). A number it genuinely cannot repair is
refused with a message saying so — and a well-formed number that simply is
not on the register gets a *different* error, so you can tell a typo from an
absence.

**Most of the officers are not current.** The register lists resigned
appointments alongside serving ones by default. Tesco shows 74 officers, of
which **11 are active**. Read as "current management" that is wrong about
85% of the rows, so every officer carries its own `appointmentStatus`.

**Officers are paginated 35 to a page.** Reading one page returns less than
half the register's answer, with no error and a perfectly plausible result.
This Actor walks every page.

**A date of birth is month and year only.** Companies House redacts the day
for individuals. This Actor keeps what was published (`dateOfBirthRaw`) and
splits it into `dateOfBirthMonth` / `dateOfBirthYear` — it never assembles a
full date, because that would assert a birthday the register deliberately
withholds.

**A search that matches nothing is an HTTP 200** carrying a "No results"
block. Zero rows alone cannot distinguish that from the markup having moved,
so the summary reports `noResultsBlockSeen` explicitly.

**PSC records come in two disjoint shapes.** An individual carries a date of
birth, nationality and place of residence; a corporate entity carries
governing law, legal form, place registered, registration number and country
of incorporation. They share little beyond the address and nature of control,
so `pscKind` labels which you have and `fields` preserves everything the
register actually published for that record.

**Two markup details that quietly corrupt text**, both handled: the status
badge is nested inside the name and role cells with no separator (a raw read
gives `RoleActive` and names ending in `…Active`), and the
identity-verification cell contains an inline SVG stylesheet whose CSS
otherwise bleeds into the value.

### Notes

The register is a public GOV.UK service. Requests are paced by default
(`minRequestInterval`, 0.5s) and a proxy is **off by default** — no anti-bot
layer was observed on any surface, and `robots.txt` is a genuine 404.

Search results carry only a name and a number; set `fetchDetails` to follow
each hit to its profile, and `includeOfficers` / `includePsc` to pull the
people. Each of those is one extra request per company, so a large search
with everything enabled is a large number of requests — keep `maxResults`
honest.

# Actor input Schema

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

`search` finds companies by name. `companies` looks up company numbers you already have.

## `searchTerm` (type: `string`):

Search mode. Company name or part of one, e.g. `bakery limited`.

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

Companies mode. Eight characters, e.g. `00445790` or `SC090312`. If your spreadsheet stripped the leading zeros, paste them anyway — `445790` is padded back to `00445790` for you.

## `fetchDetails` (type: `boolean`):

Search mode: follow every hit to its profile page for status, type, incorporation date, registered office and SIC codes. Search results alone carry only a name and number.

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

Emit one OFFICER row per appointment, walked across every page. Note the register lists RESIGNED appointments alongside current ones — each row carries its own status.

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

Emit one PSC row per beneficial owner: individuals (with redacted month/year date of birth) and corporate entities (with legal form and registration number).

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

Search mode cap. The register serves 20 results per page.

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

Parallel in-flight requests.

## `minRequestInterval` (type: `number`):

Politeness pacing for a public government register.

## `proxyConfiguration` (type: `object`):

Optional. No anti-bot layer was observed, so a proxy is OFF by default.

## Actor input object example

```json
{
  "mode": "search",
  "searchTerm": "bakery limited",
  "companyNumbers": [
    "00445790"
  ],
  "fetchDetails": false,
  "includeOfficers": false,
  "includePsc": false,
  "maxResults": 100,
  "maxConcurrency": 3,
  "minRequestInterval": 0.5,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

## `items` (type: `string`):

One row per scraped record. See the dataset's default view for field definitions.

# 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 = {
    "searchTerm": "bakery limited",
    "companyNumbers": [
        "00445790"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("scrapyx/companieshouse-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 = {
    "searchTerm": "bakery limited",
    "companyNumbers": ["00445790"],
}

# Run the Actor and wait for it to finish
run = client.actor("scrapyx/companieshouse-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 '{
  "searchTerm": "bakery limited",
  "companyNumbers": [
    "00445790"
  ]
}' |
apify call scrapyx/companieshouse-uk-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,scrapyx/companieshouse-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/ELvqfga1ZhUo9q8ZG/builds/6KoDLVfQ2mBRUJiMw/openapi.json
