# UK Company Data — Directors, Owners & Firmographics (`foxlabs/uk-company-data`) Actor

Look up any UK company and get directors, persons with significant control (beneficial owners), and firmographics. 5M+ companies from the official Companies House API.

- **URL**: https://apify.com/foxlabs/uk-company-data.md
- **Developed by:** [Berkan Kaplan](https://apify.com/foxlabs) (community)
- **Categories:** Business, Lead generation
- **Features:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $4.00 / 1,000 company records

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

## UK Company Data — Companies House Officers, PSC & Filings 🇬🇧

**foXLabs European company-registry series:** [France](https://apify.com/foxlabs/france-company-data) · [Estonia](https://apify.com/foxlabs/estonia-company-data) · [Latvia](https://apify.com/foxlabs/latvia-company-data) · [Czechia](https://apify.com/foxlabs/czech-company-data) · [Poland](https://apify.com/foxlabs/poland-company-data) · [Slovakia](https://apify.com/foxlabs/slovakia-company-data) · [Slovenia](https://apify.com/foxlabs/slovenia-company-data) · [Denmark](https://apify.com/foxlabs/denmark-company-data) · [Norway](https://apify.com/foxlabs/norway-company-data) · [Finland](https://apify.com/foxlabs/finland-company-data)

🎉 Turn the Companies House register into clean, structured UK company records — look companies up by company number or name, or discover them by SIC industry section, and get status, SIC codes, registered office, accounts and confirmation-statement dates, current and resigned officers, persons with significant control and the latest filings, one row per company.
Built for KYB / KYC, due diligence, compliance and B2B research on UK companies.

### 🔍 What is UK Company Data — and when should you use it?

Give this Actor **company numbers** in any Companies House format (`00445790`, `SC117119`, `NI032978`, …), **company names** or a **SIC 2007 section**, and it returns one record per company from the official **Companies House Public Data API** (`api.company-information.service.gov.uk`). A Companies House API key is built in, so you need no account or key of your own. Every run reads the register live.

**Use it when you need:** a register check for KYB or onboarding (status, type, incorporation date, registered office, SIC codes, overdue accounts or confirmation statements); the current board and its history; persons with significant control and their nature of control; the charges registered against the company; a list of UK companies in one SIC section, optionally only those with a given status.

**Use something else when:** you need accounts figures (turnover, profit, balance sheet) — this Actor returns accounts types and dates, not the numbers.

### 🤖 Use with AI agents

**Already on the Apify MCP server?** Ask for this Actor by name: `foxlabs/uk-company-data`.

**Your agent can pay for its own runs.** This Actor is pay-per-event with agentic payments, so an agent can discover it, run it and settle the bill over **x402 (USDC on Base)** or **Skyfire** — no Apify account or API token of its own. Billing is the same either way: per delivered company record — rows that only report a failed lookup are free from 25 September 2026.

Otherwise paste this into Claude, ChatGPT, Cursor or any MCP-enabled assistant:

```
I want UK company records from the Apify Actor `foxlabs/uk-company-data`.
Input: `companyNumbers` is a list of Companies House numbers in any format, e.g. `00445790` or `SC117119` (exact match); `companyNames` is a list of names (matched against Companies House's top 5 search results; a name without a real match returns an error row); or set `sicCode` to a SIC 2007 section letter (e.g. `JC` for IT services) to discover companies, optionally with `statusFilter` (e.g. `active`). `maxResults` caps the rows.
Start with: {"companyNumbers":["00445790"]}
Ask me what to look up, run the Actor, then summarise the rows as a table.
```

The machine-readable API, MCP config and OpenAPI definition live at `apify.com/foxlabs/uk-company-data.md`.

### 📋 Overview

Everything you need to turn the Companies House register into structured data — in one Actor, with no login, cookies or API key of your own.

**Why teams pick this Actor:**

- ✅ **Official register** — the Companies House Public Data API, with a key built in.
- 🧑‍💼 **Officers** — current appointments (name, role, appointment date, nationality, country of residence, occupation, address), resigned ones in their own list.
- 🏛️ **Persons with significant control** — name, kind, nature of control in plain words, notified and ceased dates, identification of corporate PSCs.
- 🏦 **Charges** — the charges on the register (up to 50 listed per company) with status, dates, classification, persons entitled and secured details, plus total and outstanding counts that cover all of them.
- 📅 **Filing deadlines** — last and next accounts, confirmation statements, overdue flags, insolvency-history flag.
- 🧭 **SIC discovery** — the 21 SIC sections plus manufacturing and information-and-communication subsections, optionally filtered by status.
- 💰 **Pay per record** — $0.004 per delivered company record, no subscription.
- 🤖 **Agent-ready** — MCP + x402 agentic payments.

### ✨ Features

- 🔍 **Three ways in** — `companyNumbers` (exact, any Companies House format), `companyNames` (matched against the top 5 Companies House search results), `sicCode` (Companies House advanced search).
- 🧹 **Status filter** — `statusFilter` keeps one status (`active`, `dissolved`, `liquidation`, …): sent to Companies House in SIC discovery, applied after the lookup for numbers and names.
- 🗂️ **Filing history** — the 10 most recent filings with category, type, date, description, page count and a document link, plus the total number of filings.
- 📜 **Previous names** — former company names with the dates they were used.
- 🔑 **Your own key (optional)** — `api_key` if you want your own Companies House rate limit.

### 🎬 Quick Start

```bash
curl -X POST "https://api.apify.com/v2/acts/foxlabs~uk-company-data/runs?token=YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"companyNumbers":["00445790"]}'
```

### 🚀 Getting Started (3 steps)

1. **Choose your targets** — company numbers for exact matches, names for convenience, or a SIC section to discover companies.
2. **Filter if you need to** — `statusFilter` (e.g. `active`) and `maxResults` (default 100).
3. **Run and export** — download the dataset as JSON, CSV, Excel, HTML or XML.

### 📥 Input

```json
{"companyNumbers":["00445790"]}
```

| Field | Type | Description |
|---|---|---|
| `api_key` | string | Optional Companies House API key. A key is built in; add your own if you want your own rate limit. |
| `companyNumbers` | array | Companies House numbers, e.g. `00445790`, `SC117119`, `NI032978` — exact match. Every format is read: 8 digits, or a letter prefix such as SC, NI, OC, SO, NC, LP, SL, NL, FC or R. Spaces and dashes are removed and missing leading zeros restored (`445790` → `00445790`); an entry that cannot be a company number gets its own `error` row. |
| `companyNames` | array | Names searched on Companies House; one of the top 5 results is used only if it really matches the name (see How names are matched). Name rows carry `query` and `name_match_score`. |
| `sicCode` | string | SIC 2007 section or subsection, e.g. `JC` (IT & software services) or `F` (construction). The Actor sends the section's 5-digit SIC codes to Companies House advanced search and pages through the results, 50 at a time, up to `maxResults`. |
| `statusFilter` | string | Keep one status: `active`, `dissolved`, `liquidation`, `administration`, `receivership`, `voluntary-arrangement`, `converted-closed`, `insolvency-proceedings`, `registered`, `removed`. In SIC discovery it is sent to Companies House; for numbers and names, companies with another status are skipped (no row), and a name prefers a matching search result with that status. |
| `maxResults` | integer | Cap on companies (default 100, max 1,000). `0` removes the cap for numbers and names, but SIC discovery then returns nothing. |
| `maxConcurrency` | integer | Companies fetched in parallel (default 3, max 10). The run-wide rate limit sets the speed, so a higher value does not make a run faster. |
| `proxyConfiguration` | object | Apify Proxy settings (default: Apify Proxy on). |

**Speed and time limit.** Every Companies House request of a run goes through one rate limit — at most 480 requests in any 5 minutes, 80% of the 600 Companies House allows per API key. With all sections a company takes 5 requests, so the Actor looks up about 90 companies per 5 minutes: a 1,000-company run by number or SIC discovery takes about 55 minutes, inside the default 60-minute timeout. A name adds one search request, so 1,000 companies by name take about 66 minutes — set the run timeout to 75 minutes for that, or the Actor stops 90 seconds before its limit and lists the companies it did not reach.

**How names are matched.** Each name goes to the Companies House company search, and the top 5 results are compared with it word by word after both are normalised: upper case, a possessive 's dropped, `&` read as `and`, punctuation removed, and a leading `The` and a trailing `Ltd`, `Limited`, `PLC`, `LLP`, `LP` or `CIC` ignored. The result that contains the largest share of your words wins (ties: the result with the fewest extra words, then an active company before one with any other status, then Companies House's order; with `statusFilter` set, the best acceptable result with that status is taken first), and it is used only if it contains every word of your name or its Dice score — 2 × shared words ÷ (your words + its words) — is at least 0.8. Otherwise the row is `matched: false` with an `error` that names the closest result and its score; it is a failed lookup, so it is not charged from 25 September 2026. Rows found by name carry `query` (your name) and `name_match_score` (the Dice score, 0–1); rows looked up by number do not.

**More recipes**

- Name look-up: `{"companyNames":["Tesco","Zzqx Nonexistent Widgets Qwv"]}` — in our test `Tesco` resolved to TESCO PLC (00445790) with `name_match_score` 1, and the made-up name returned a no-match row naming the closest result, AESTHETIC WIDGETS LTD (score 0.33), which was not charged.
- Scottish and Northern Irish numbers: `{"companyNumbers":["SC117119","NI032978"]}` — SSE PLC and SX3 LIMITED in our test.
- A company with many charges: `{"companyNumbers":["01777777"]}` — British Airways Plc: 898 charges, 730 outstanding, the newest 50 listed.
- Sector list: `{"sicCode":"JC","statusFilter":"active","maxResults":2}` — returned two companies with Companies House status `active` and SIC 62020. Without `statusFilter`, `{"sicCode":"JC","maxResults":2}` returned two dissolved Northern Ireland companies first.

### 📤 Output

One row per company. Rows with `matched: true` are delivered company records. A lookup that fails still writes a row with `matched: false` and an `error` — `Not a Companies House company number …` for an entry that cannot be one, `Company not found in Companies House.` when a number's profile could not be read, `No company name matching …` (your name in `query`, the closest result's score in `name_match_score`) when no search result really matches a name, `Companies House did not answer the name search …` when the search got no answer — so a failed lookup never disappears silently. From 25 September 2026 these rows are not charged. Companies skipped by `statusFilter` produce no row. If Companies House does not answer one of the section requests — officers, PSC, filing history or charges — even after its retries, the row is still a company record: that section is `null` (unknown, not empty) and `unavailable_sections` / `unavailable_note` name it. A 404 from Companies House is a real empty list. If a run nears its time limit, the Actor stops starting companies 90 seconds early, keeps every row it has and adds one summary row — `record_type: "summary"`, a `message`, `companies_left` and `left_to_look_up` (the numbers or names it did not reach). The summary row is not a company record.

| Field | Description |
|---|---|
| `company_number` / `company_name` | Company number and registered name |
| `company_status` / `company_status_detail` | Status (`active`, `dissolved`, …) and Companies House's status detail |
| `company_type` / `subtype` | Company type (`ltd`, `plc`, …) and subtype |
| `incorporation_date` / `dissolution_date` | Incorporation date and Companies House's date of cessation (ISO). Two companies with status `active` also carried a cessation date in our test, so read it together with `company_status` |
| `sic_codes` | SIC 2007 codes |
| `registered_address` / `registered_in` | Registered office address and jurisdiction |
| `previous_company_names[]` / `previous_names_count` | Former names with the dates they were used |
| `foreign_company_details` / `branch_company_details` | Details for overseas companies and UK branches; `null` for the others |
| `accounts_type` / `accounts_period_start` / `accounts_period_end` / `accounting_reference_date` / `last_accounts_date` / `next_accounts_due` / `next_accounts_due_on` / `next_accounts_overdue` | Accounts: type and period of the last accounts, accounting reference date (day/month), next due date, overdue flag |
| `last_confirmation_statement` / `next_confirmation_statement` / `next_confirmation_due` / `confirmation_overdue` / `annual_return` / `last_members_list_date` | Confirmation statements, and the annual returns that preceded them |
| `has_insolvency_history` / `can_file` / `undeliverable_registered_office` / `registered_office_in_dispute` | Register flags |
| `officers[]` / `officers_count` / `officers_truncated` / `officers_text` | Current officers — `name`, `role`, `appointed_on`, `resigned_on`, `nationality`, `country_of_residence`, `occupation`, `address`; the number of distinct people; `officers_truncated` is `true` when Companies House holds more current officers than the Actor read (it reads up to 100 appointments) |
| `resigned_officers[]` / `resigned_officers_count` | Past appointments, with `resigned_on` |
| `persons_with_significant_control[]` / `psc_count` / `psc_text` | PSCs (up to 100) — `name`, `kind`, `natures_of_control`, `notified_on`, `ceased_on`, `nationality`, `country_of_residence`, `address`, `identification` |
| `has_charges` / `charges[]` / `charges_count` / `charges_outstanding` / `charges_text` / `charges_note` | Charges from the Companies House charges register, read for every company — `charge_number`, `status`, `created_on`, `satisfied_on`, `classification`, `persons_entitled`, `amount_secured` and the fixed / floating-charge flags, newest first (up to 50 listed); `charges_count` counts every charge, `charges_outstanding` those neither satisfied nor part-satisfied; `has_charges` is `true` when at least one charge is registered. `charges_note` appears when the list is cut at 50 (the counts still cover every charge) or when the charges request failed — the other charge fields are then `null`, not 0 |
| `registered_email` / `website` | Empty for all 8 companies in our test runs |
| `recent_filings[]` / `filing_categories` / `total_filings` | The 10 most recent filings — `category`, `type`, `date`, `description`, `pages`, `document_url` — their categories, and the total number of filings |
| `unavailable_sections` / `unavailable_note` | Only when Companies House did not answer a section request after its retries: which sections (`officers`, `psc`, `filing_history`, `charges`) are `null` — unknown, not empty — and why |
| `record_type` / `message` / `companies_left` / `left_to_look_up` | Only on the summary row written when a run stopped 90 seconds before its time limit |
| `matched` / `error` | `true` for a company record; `false` plus `error` for a failed lookup |
| `query` / `name_match_score` | Name look-ups only: the name you searched, and the Dice score (0–1) of the company it matched — or, on a no-match row, of the closest result. Rows looked up by number do not have these fields |
| `source` / `source_url` / `scraped_at` | `UK Companies House`, the company's page on find-and-update.company-information.service.gov.uk, timestamp |

Sample — Tesco PLC, captured 2026-09-10 (lists trimmed to one entry):

```json
{
  "company_number": "00445790",
  "company_name": "TESCO PLC",
  "company_status": "active",
  "company_status_detail": null,
  "company_type": "plc",
  "subtype": null,
  "incorporation_date": "1947-11-27",
  "dissolution_date": null,
  "sic_codes": [
    "47110"
  ],
  "registered_address": "Tesco House, Shire Park, Kestrel Way, Welwyn Garden City, AL7 1GA, United Kingdom",
  "registered_in": "england-wales",
  "previous_company_names": [
    {
      "name": "TESCO STORES (HOLDINGS) PUBLIC LIMITED COMPANY",
      "ceased_on": "1983-08-25",
      "effective_from": "1981-12-14"
    }
  ],
  "previous_names_count": 2,
  "foreign_company_details": null,
  "branch_company_details": null,
  "accounts_type": "group",
  "accounts_period_start": "2025-02-27",
  "accounts_period_end": "2026-02-28",
  "accounting_reference_date": "26/02",
  "last_accounts_date": "2026-02-28",
  "next_accounts_due": "2027-08-26",
  "next_accounts_due_on": "2027-08-26",
  "next_accounts_overdue": false,
  "last_confirmation_statement": "2026-06-18",
  "next_confirmation_statement": "2027-06-18",
  "next_confirmation_due": "2027-07-02",
  "confirmation_overdue": false,
  "annual_return": null,
  "last_members_list_date": "2013-06-07",
  "has_charges": true,
  "has_insolvency_history": false,
  "can_file": true,
  "undeliverable_registered_office": false,
  "registered_office_in_dispute": false,
  "registered_email": null,
  "website": null,
  "officers": [
    {
      "name": "TAYLOR, Christopher Jon",
      "role": "secretary",
      "appointed_on": "2025-04-14",
      "resigned_on": null,
      "nationality": null,
      "country_of_residence": null,
      "occupation": null,
      "address": "Tesco House, Shire Park, Welwyn Garden City, United Kingdom"
    }
  ],
  "officers_count": 11,
  "officers_truncated": false,
  "officers_text": "TAYLOR, Christopher Jon (secretary); BETHELL, Melissa (director); BODSON, Bertrand Jean Francois (director); FAIRBAIRN, Carolyn Julie (director); GILLILAND, Stewart Charles (director); KENNEDY, Christopher John (director); MURPHY, Gerard Martin (director); MURPHY, Ken (director)",
  "resigned_officers": [
    {
      "name": "AGER, Rowley Stuart",
      "role": "secretary",
      "appointed_on": null,
      "resigned_on": "2004-03-15",
      "nationality": "British",
      "country_of_residence": null,
      "occupation": null,
      "address": "Tesco House, Cheshunt"
    }
  ],
  "resigned_officers_count": 63,
  "persons_with_significant_control": [],
  "psc_count": 0,
  "psc_text": null,
  "charges": [
    {
      "charge_number": 9,
      "status": "outstanding",
      "created_on": "2009-11-04",
      "satisfied_on": null,
      "classification": "Account security agreement",
      "persons_entitled": "Tesco Trustee Company of Ireland Limited as Trustee of the Tesco Ireland Limited Senior Executive Pension Scheme",
      "amount_secured": "All sums due or to become due under the terms of the aforementioned instrument creating or evidencing the charge",
      "contains_fixed_charge": null,
      "contains_floating_charge": null,
      "floating_charge_covers_all": null
    }
  ],
  "charges_count": 9,
  "charges_outstanding": 2,
  "charges_text": "outstanding: All sums due or to become due under the terms of the aforementioned instrument creating or evidencing the charge — Tesco Trustee Company of Ireland Limited as Trustee of the Tesco Ireland Limited Senior Executive Pension Scheme | outstanding: All sums due or to become due under the terms of the aforementioned instrument creating or evidencing the charge — Tesco Ireland Pension Trustees Limited as Trustee of the Tesco Ireland Limited Pension Plan | fully-satisfied: All monies due or to become due from the company to the chargee under the terms of the aforementioned instrument creating or evidencing the charge — Tesco Ireland Pension Trustees Limited as Trustee of the Tesco Ireland Limited Executive Scheme | fully-satisfied: All monies due or to become due from the company to the chargee under the terms of the aforementioned instrument creating or evidencing the charge — Tesco Trustee Company of Ireland Limited as Trustee of the Tesco Ireland Limited Staff Scheme (The \"Trustee\") | fully-satisfied: All monies due or to become due from the company to the chargee on any account whatsoever under the terms of the aforementioned instrument creating or evidencing the charge — Rbs Aerospace Limited | fully-satisfied: All monies due or to become due from the company to the chargee under the deed and the payments and discharge of the guaranteed amounts (as defined) — Deutsche International Finance (Ireland) Limited | fully-satisfied: All monies due from the company to the chargee under the loan agreement — Deutsche Bank Ag | fully-satisfied: All monies due or to become due from the company to the chargee under or in connection with the bond the instrument and the subscription agreement (all as defined) — Cobroad Investments | fully-satisfied: All monies due or to become due from the company to cobroad investments under or in connection with a 0.125 per cent £428,909,319 deep discount bond 2012 of the company,the instrument dated 17/12/91 and the suscription agreement dated 17/12/91 — Cobroad Investments",
  "recent_filings": [
    {
      "category": "capital",
      "type": "SH06",
      "date": "2026-08-26",
      "description": "capital-cancellation-shares",
      "pages": 4,
      "document_url": "https://document-api.company-information.service.gov.uk/document/hSRySSiDDwOxswzyFngzgNfv1XcOZl3J25cacnI0UtY"
    }
  ],
  "filing_categories": [
    "capital"
  ],
  "total_filings": 8371,
  "matched": true,
  "source": "UK Companies House",
  "source_url": "https://find-and-update.company-information.service.gov.uk/company/00445790",
  "scraped_at": "2026-09-10T15:41:12.379Z"
}
```

### 💼 Use cases

**1. KYB and onboarding** — check a UK company before you sign.
*Input:* its company number. *Output:* status, type, incorporation date, registered office, SIC codes, overdue flags. *Use:* confirm it is live and up to date with its filings.

**2. Ownership and control checks** — who controls the company.
*Input:* company numbers. *Output:* persons with significant control and their nature of control. *Use:* ownership checks for KYB.

**3. Board history** — who joined and who left.
*Input:* company numbers. *Output:* current and resigned officers with dates — Tesco: 11 current and 63 resigned appointments in our test. *Use:* spot recent board changes.

**4. Sector lists** — companies by SIC section.
*Input:* `sicCode` with `statusFilter`. *Output:* full profiles of the companies found. *Use:* build a prospect or market list.

**5. Secured-lending checks** — who holds security over the company.
*Input:* company numbers. *Output:* the registered charges (up to 50 listed) with status, persons entitled and secured details, and the total and outstanding counts — ASDA Stores: 32 charges, 26 outstanding, in our test. *Use:* see outstanding secured debt before you lend, sell or sign.

### 🔗 Integration

**JavaScript / Node.js**

```javascript
import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: 'YOUR_TOKEN' });
const run = await client.actor('foxlabs/uk-company-data').call({"companyNumbers":["00445790"]});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items[0]);
```

**Python**

```python
from apify_client import ApifyClient
client = ApifyClient('YOUR_TOKEN')
run = client.actor('foxlabs/uk-company-data').call(run_input={"companyNumbers": ["00445790"]})
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)
```

Written for `apify-client` 3 for Python, where `call()` returns a run object; with version 1 or 2 it returns a dict — write `run['defaultDatasetId']` there.

**Automation (n8n / Zapier / Make):** schedule or webhook → HTTP request to the Actor API with your `companyNumbers` → handle the JSON dataset → push to a sheet, CRM or dashboard.

### 📊 Pricing

Pay-per-event: **$0.004 per delivered company record** (≈ $4 per 1,000 companies), plus Apify's run-start event of $0.00005 per GB of memory. **From 25 September 2026, rows that only report a failed lookup (`error`) are free.** Until then Apify still counts them as dataset items. The summary row a run writes when it stops before its time limit is treated the same way. Companies skipped by `statusFilter` produce no row and are not charged. **A record whose officers, PSC, filing-history or charges request failed is still charged; `unavailable_note` says which part is missing.** [Current pricing](https://apify.com/foxlabs/uk-company-data).

### ❓ FAQ

**Where does the data come from?** From the Companies House Public Data API: the company profile, officers, persons with significant control, charges and filing history of each company, advanced search for SIC discovery and the company search for names. A Companies House key is built in.

**Company number or name?** A number is exact. A name is matched as described under How names are matched: in our test `Tesco` resolved to TESCO PLC (00445790) with `name_match_score` 1, `Tesco Stores` to TESCO STORES LIMITED, and `Zzqx Nonexistent Widgets Qwv` returned a no-match row naming AESTHETIC WIDGETS LTD (score 0.33). Some names are registered more than once, and an active registration wins the tie: `British Airways` resolves to BRITISH AIRWAYS PLC 01777777 rather than the closed NF002699 that Companies House lists first, and `Marks and Spencer` to MARKS AND SPENCER P.L.C. 00214436 rather than the closed NF001553. `Sainsbury's` resolved to SAINSBURY'S LIMITED (SC171417), because J SAINSBURY PLC is not among Companies House's top 5 results for that spelling. Pass the company number when the exact entity matters.

**Where do the charges come from?** From the Companies House charges register, which the Actor reads for every company. It does not rely on the profile's `has_charges` flag, which read `false` for all 7 companies we tested, Tesco and ASDA Stores included. Checked on 2026-09-10 against the Companies House charges pages: Tesco 9 charges (2 outstanding, 7 satisfied), ASDA Stores 32 (26 outstanding, 6 satisfied) — the same numbers as in the rows. Up to 50 charges are listed per company, newest first, and the counts always cover all of them: British Airways Plc has 898 charges on the register, and its row lists 50 with `charges_count: 898`, `charges_outstanding: 730` and a `charges_note` saying the list is cut. If Companies House does not answer the charges request, the row stays a company record but `has_charges`, `charges`, `charges_count`, `charges_outstanding` and `charges_text` are `null` — unknown, not zero — and `charges_note` says so.

**What happens when Companies House does not answer?** Every request is retried with a bounded back-off. Companies House allows 600 requests per 5 minutes per API key and the built-in key is shared by everyone using this Actor, so each run keeps to 480 requests per 5 minutes (see Speed and time limit). A rate-limited request (429) waits 20, 40, 80 and 160 seconds, at most 300 seconds in all; a 5xx or network error waits 0.6 to 4.8 seconds; other errors are not retried. If officers, PSC, filing history or charges still get no answer, that section is `null` and `unavailable_sections` / `unavailable_note` name it; the record is still charged. A 404 is Companies House saying there is nothing, so that section is an empty list.

**Why is the PSC list empty for Tesco?** The Actor returns what Companies House holds: Tesco and Marks and Spencer had no PSC on the register in our test, while SX3 LIMITED showed its corporate owner with 75–100% of shares and votes and the right to appoint directors.

**How does SIC discovery work?** The section letter is mapped to its 5-digit SIC 2007 codes (for `JC`: 62011, 62012, 62020, 62030, 62090, 63110, 63120, 63910, 63990), sent to Companies House advanced search and paged 50 at a time until `maxResults`. Results come in Companies House's order.

**What if I start it with no input?** The run stops with an error asking for `companyNumbers`, `companyNames` or `sicCode`.

**Can I export to CSV / Excel / JSON?** Yes — directly from the Apify dataset.

### 🐛 Troubleshooting

- **`Not a Companies House company number …`** — the entry is neither 8 digits nor a letter prefix followed by digits (e.g. `SC117119`); correct it and re-run.
- **`charges_count` is `null`** — Companies House did not answer the charges request during the run, and `charges_note` says so; re-run to fill the charge fields.
- **`unavailable_sections` is set** — Companies House did not answer that section request even after the retries; its fields are `null` and the rest of the record was read. Re-run to fill them.
- **`Company not found in Companies House.`** — the number is not on the register, or Companies House did not answer; check the number and re-run.
- **`No company name matching …`** — none of Companies House's top 5 results contains your words closely enough; the error names the closest one. Check the spelling, use the registered name, or pass the company number.
- **A name returns another registration than you meant** — some names are registered more than once (for example two BRITISH AIRWAYS PLC entries, one of them closed). The active one is preferred, but pass the company number when the exact entity matters.
- **SIC discovery returns nothing** — set `maxResults` to at least 1.
- **Rate limits** — the built-in key is shared by everyone using this Actor, and Companies House allows 600 requests per 5 minutes per key. Each run keeps to 480 per 5 minutes; if another run on the same key still causes a 429, the Actor waits 20, 40, 80 and 160 seconds before it gives up on that request. With your own `api_key` your runs do not share the limit with other users; the speed stays the same.
- **The last row is a summary row** — the run reached its time limit; `left_to_look_up` lists the companies it did not reach. Raise the run timeout, or run again with that list.

### ⚖️ Is it legal to scrape this data?

The Actor reads the public Companies House register through its official API. Officer and PSC names, nationalities, countries of residence, occupations and addresses are personal data under the UK GDPR — process them only for a lawful purpose such as KYC or due diligence. This Actor is not affiliated with Companies House.

### 🤝 Support & contact

- 🌐 **Website:** [data.foxlabs.com.tr](https://data.foxlabs.com.tr)
- 📧 **Email:** info@foxlabs.com.tr
- 🐛 **Issues:** open a ticket in the Actor’s **Issues** tab
- 🧰 **More clean B2B data actors:** [Fox Labs on Apify](https://apify.com/foxlabs)

### Changelog

#### 0.1.20 — 2026-10-08 — README: the Python example works with the current Apify client

- The README's Python example read the results with `run['defaultDatasetId']`. With the current Apify client for Python (version 3) `call()` returns a run object, not a dict, and that line raised `TypeError: 'Run' object is not subscriptable` — after the run itself had finished. The example now reads `run.default_dataset_id`; with client version 1 or 2, keep `run['defaultDatasetId']`. Checked with apify-client 3.2.1, 2.5.1 and 1.12.2.
- No code, output field or pricing change.

#### 0.1.2 — 2026-09-10 — name look-ups are matched, not taken blindly

- **Fixed: a name could return an unrelated company.** The first Companies House search result used to be taken whatever it was — a made-up name came back as AESTHETIC WIDGETS LTD and was charged. Each of the top 5 results is now scored against your name, and one is used only if it contains every word of the name or its Dice score is at least 0.8 (see How names are matched).
- **Active registrations first.** When the same name is registered more than once, the active company wins the tie: `British Airways` now resolves to BRITISH AIRWAYS PLC 01777777, not the closed NF002699 listed first, and `Marks and Spencer` to MARKS AND SPENCER P.L.C. 00214436. With `statusFilter` set, a matching result with that status is taken first.
- **No match, no charge.** A name without a real match returns a `matched: false` row whose `error` names the closest result and its score; it is not charged.
- **New fields on name rows:** `query` and `name_match_score`. Rows looked up by number do not have them; no field was renamed or removed.

#### 0.1.1 — 2026-09-10 — failed lookups become free (per-record billing) + README corrected + charges and company-number formats fixed + no false empty sections + run-wide rate limit

- **You will pay only for delivered company records.** The Actor now charges a new `company-record` event for each delivered company (`matched: true`). Rows that only report a failed lookup — an entry that is not a company number, a number whose profile could not be read, a name without search results — are pushed without a charge, and so is the summary row of a run that stopped before its time limit.
- **Timing:** Apify applies pricing changes after a 14-day notice. From **25 September 2026** the pricing switches from the per-dataset-item event to `company-record` at the same price ($0.004). Until then billing is exactly as before; the code detects the switch by itself.
- **Partial records are charged.** A record whose officers, PSC, filing-history or charges request failed is still charged; `unavailable_note` says which part is missing.
- **Run-wide rate limit.** Every Companies House request of a run shares one limit of 480 requests per 5 minutes (80% of the 600 Companies House allows per key), so the shared built-in key is never pushed over its limit: about 90 companies per 5 minutes, about 55 minutes for 1,000 companies by number.
- **Clean stop.** 90 seconds before its time limit the run stops starting companies, keeps every row and writes one summary row with the companies it did not reach.
- **Fixed: charges.** The charges register is now read for every company. Before, it was read only when the profile's `has_charges` flag was `true` — and that flag was `false` even for Tesco (9 charges) and ASDA Stores (32), so every row showed no charges. `has_charges` now means at least one charge is registered, and `charges_outstanding` counts the charges that are neither satisfied nor part-satisfied (it held the part-satisfied count before). Checked against Companies House for Tesco and ASDA Stores.
- **No false zero, no silent cut.** If a section request (officers, PSC, filing history, charges) gets no answer, that section is `null` — unknown, not empty — and `unavailable_sections` / `unavailable_note` name it; a 404 is a real empty list. Up to 50 charges are listed, newest first; the counts always cover every charge, and `charges_note` says when the list is cut (British Airways Plc: 898 charges, 50 listed).
- **Retries.** A rate-limited request (429) is retried after 20, 40, 80 and 160 seconds (at most 300 seconds in all), a 5xx or network error after 0.6 to 4.8 seconds; other errors are not retried.
- **Fixed: company numbers.** Every Companies House format is read — `SC…`, `NI…`, `OC…`, `R…` and the rest — and missing leading zeros are restored. Before, only 8-digit numbers were read and every other entry was dropped without a row; an entry that cannot be a company number now gets an `error` row.
- **Output fields:** `charges_note`, `unavailable_sections`, `unavailable_note` and the summary row's `record_type`, `message`, `companies_left`, `left_to_look_up` are new and appear only when needed; no field was renamed or removed.
- **README corrected.** The previous README showed inputs this Actor does not read (`queries`, `maxResultsPerQuery`, `includeRaw`) with placeholder values, and claimed things it does not do: whole-registry calls, formation monitoring, relevance-ranked search, registry contact fields, camelCase rows with a `query` field, PSC and charges as counts only with the lists "planned", and free failed lookups before this billing change. This README is written from the code and from test runs on 2026-09-10.

#### 0.1 — 2026-09-07

- README rebuilt and AI-agent payments (x402) enabled. That README described inputs this Actor does not have and said twelve columns had been removed; they had not — the Actor still returns them (see 0.1.1).

#### 0.0

- Initial release: data from the UK Companies House register (official open API) by name or registry ID.

# Changelog

This Actor's version history is a separate document: https://apify.com/foxlabs/uk-company-data/changelog.md

# Actor input Schema

## `api_key` (type: `string`):

A default API key is included. Provide your own key at https://developer.company-information.service.gov.uk if you need higher rate limits.

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

Companies House numbers, one per line — e.g. 00445790 (England & Wales), SC117119 (Scotland), NI032978 (Northern Ireland), OC… (LLPs). Exact match. Spaces are ignored and missing leading zeros are added; an entry that cannot be a company number gets an error row.

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

Search Companies House by name. Returns first match. Best-effort — use company number for guaranteed results.

## `sicCode` (type: `string`):

Select a UK SIC industry section to discover companies in that sector. Leave empty for direct lookup with company numbers/names.

## `statusFilter` (type: `string`):

Return only companies with this status. In SIC discovery it filters the search server-side; with company numbers/names it filters the returned results.

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

Hard cap on results.

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

Parallel requests. Default 3 for rate limits.

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

Apify Proxy used for Companies House API requests. Leave on the default group unless you have a reason to change it.

## Actor input object example

```json
{
  "api_key": "",
  "companyNumbers": [
    "00445790"
  ],
  "companyNames": [],
  "sicCode": "",
  "statusFilter": "",
  "maxResults": 100,
  "maxConcurrency": 3,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": []
  }
}
```

# Actor output Schema

## `dataset` (type: `string`):

No description

# 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 = {
    "api_key": "",
    "companyNumbers": [
        "00445790"
    ],
    "companyNames": [],
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": []
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("foxlabs/uk-company-data").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 = {
    "api_key": "",
    "companyNumbers": ["00445790"],
    "companyNames": [],
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": [],
    },
}

# Run the Actor and wait for it to finish
run = client.actor("foxlabs/uk-company-data").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 '{
  "api_key": "",
  "companyNumbers": [
    "00445790"
  ],
  "companyNames": [],
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": []
  }
}' |
apify call foxlabs/uk-company-data --silent --output-dataset

```

## MCP server setup

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

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/zpy3lfBg58HxzbkzI/builds/2NOgVG6dMBWuOu1Gl/openapi.json
