DEV Community

Agents001-code
Agents001-code

Posted on

Track new UK companies every month for lead lists

Every month several tens of thousands of new companies are registered at Companies House. Each one needs a bank account, an accountant, insurance, a website, payroll, software and premises. If you sell any of those, a list of companies incorporated last month in your sector and your area is about as warm as a cold list gets.

Companies House makes all of this public. The register can be searched one company at a time on its website, and the whole register of live companies is published once a month as a free download. The download is large, though: several big CSV files with every live UK company in them. Filtering it down to "software companies in Leeds and Manchester incorporated in September" takes some work every month.

This tutorial shows how to get that filtered list automatically each month with the UK Companies House Scraper on the Apify Store, plus how to switch to a daily feed if you want companies on the day they register. I built the Actor.

Where the data comes from

The Actor has two modes, depending on whether you give it a Companies House API key.

Without a key (default) With your free API key
Source Free Company Data Product, a monthly snapshot of every live UK company Companies House Public Data API
Freshness Updated monthly, within 5 working days of month end Live, including companies registered today
"Last N days" counts back from The snapshot date Today
Filing events (accounts, charges, insolvency) No Yes

The monthly snapshot is enough for a monthly lead list, and it needs no account or key at all. That is the setup used in this tutorial.

Each record is company level: number, name, status, type, incorporation date, registered office address split into parts (including postcode area and district), SIC codes with descriptions, and the next accounts and confirmation statement due dates. The Actor never collects officers or persons with significant control, and it drops "care of" address lines, which can name a person.

The Actor is independent and is not affiliated with or endorsed by Companies House or the UK Government.

Step 1: Run it once in the Apify Console

Open the Store page (link at the end) and click Start with no input. You get up to 1,000 UK companies incorporated in the 30 days before the latest monthly snapshot.

Then narrow it down with these fields:

  • Incorporated in the last N days (incorporatedWithinDays): default 30. Without a key this counts back from the snapshot date.
  • SIC codes (sicCodes): SIC 2007 codes or prefixes. 62 covers all computer programming and consultancy, 62012 is business and domestic software development, 56 covers restaurants and other food service, 68 covers real estate.
  • Registered office postcode starts with (postcodePrefixes): postcode areas, districts or sectors, such as M, LS, EC1 or SW1A 1. EC1 also matches EC1A and EC1V, but M1 does not match M14.
  • Company types (companyTypes): Companies House codes such as ltd, llp and plc, plus community-interest-company.
  • Company status (companyStatuses): active for most lead lists.
  • Maximum records (maxItems): default 1,000. 0 means no limit.

For this tutorial I ran it locally with SIC 62, postcodes M and LS, and type ltd, capped at 50 records. It read the snapshot dated 1 October 2026, used an incorporation window of 2 September to 1 October, and returned 50 records in about 11 seconds. A run over the whole snapshot with no cap can take up to about 2 minutes, because it reads up to 7 large files.

Step 2: Schedule it monthly with onlyNew

The snapshot updates once a month, so a monthly run is the natural fit.

  1. Switch on Only records not seen in previous runs (onlyNew). The Actor remembers which company numbers it has already returned for your filters, in a key-value store in your Apify account. The next run only returns companies it has not given you before, and you are only charged for those.
  2. Set Incorporated in the last N days to 45. A rolling window is stored as the number of days, so your monthly runs keep one history, and the overlap with last month means a company that appeared late in the register is still caught once.
  3. Set Maximum records to 0 so a large month is not cut short.
  4. Save the input as a task. In Schedules, create a schedule with cron 0 8 10 * * and timezone Europe/London. That runs on the 10th of each month, after the snapshot has been published.

Changing a filter starts a fresh history.

If you want new companies daily, register for a free REST API key on the Companies House Developer Hub and paste it into Your Companies House API key. The field is secret, and Apify stores it encrypted. Then use incorporatedWithinDays: 2, onlyNew: true and a weekday schedule. Adding or changing the key does not reset the history.

Step 3: Pull the list with Python

Install the official client with pip install apify-client and copy your Apify API token from the Integrations section of the Console.

import csv
import os
from collections import Counter

from apify_client import ApifyClient

client = ApifyClient(os.environ["APIFY_TOKEN"])

run_input = {
    "incorporatedWithinDays": 45,
    "sicCodes": ["62", "63"],
    "postcodePrefixes": ["M", "LS", "S", "B"],
    "companyTypes": ["ltd"],
    "companyStatuses": ["active"],
    "onlyNew": True,
    "maxItems": 0,
}

# Starts the run and waits for it to finish. Caps spend at $3 for this run.
run = client.actor("agents001/uk-companies-house-feed").call(
    run_input=run_input,
    max_total_charge_usd=3,
)
if run is None or run.status != "SUCCEEDED":
    raise SystemExit("Run failed to start or finish")

fields = [
    "companyNumber", "companyName", "incorporationDate", "postTown",
    "postcodeDistrict", "sicCodes", "sicDescriptions", "companyUrl",
]
by_area = Counter()

with open("new_uk_companies.csv", "w", newline="") as f:
    writer = csv.DictWriter(f, fieldnames=fields, extrasaction="ignore")
    writer.writeheader()
    for c in client.dataset(run.default_dataset_id).iterate_items():
        row = dict(c)
        row["sicCodes"] = "; ".join(c["sicCodes"])
        row["sicDescriptions"] = "; ".join(c["sicDescriptions"])
        writer.writerow(row)
        by_area[c["postcodeArea"]] += 1

for area, count in by_area.most_common():
    print(f"{area}: {count} new companies")
Enter fullscreen mode Exit fullscreen mode

call() returns None if the run could not be started or followed, so the check matters. iterate_items() pages through the dataset for you. SIC codes come as a list because a company can declare up to four.

What the output looks like

Two real records from that local run, trimmed. The full record also has the registered office address lines and county.

{
  "recordType": "company",
  "companyNumber": "17486618",
  "companyName": "AVERO SAAS LIMITED",
  "companyStatus": "active",
  "companyType": "ltd",
  "companyTypeLabel": "Private Limited Company",
  "incorporationDate": "2026-09-28",
  "postTown": "MANCHESTER",
  "postcodeArea": "M",
  "postcodeDistrict": "M12",
  "sicCodes": ["62012"],
  "sicDescriptions": ["Business and domestic software development"],
  "accountsNextDueDate": "2028-06-28",
  "confirmationStatementNextDueDate": "2027-10-11",
  "companyUrl": "https://find-and-update.company-information.service.gov.uk/company/17486618",
  "dataSource": "free-company-data-product",
  "dataAsOf": "2026-10-01",
  "isNew": true
}
Enter fullscreen mode Exit fullscreen mode
{
  "companyNumber": "17492640",
  "companyName": "1ST CYBER LTD",
  "incorporationDate": "2026-09-30",
  "postTown": "LEEDS",
  "postcodeArea": "LS",
  "postcodeDistrict": "LS14",
  "sicCodes": ["62020"],
  "sicDescriptions": ["Information technology consultancy activities"],
  "dataAsOf": "2026-10-01"
}
Enter fullscreen mode Exit fullscreen mode

A few fields worth knowing:

  • dataAsOf is the snapshot date when there is no key. Records from the live API have dataSource set to companies-house-api.
  • postcodeArea and postcodeDistrict are ready for territory routing, so you do not need to parse postcodes yourself.
  • accountsNextDueDate and confirmationStatementNextDueDate are useful for accountants: a new company's first accounts deadline is already known.

What it costs

The Actor uses pay-per-event pricing with platform usage included: $3.00 per 1,000 records on the Free plan, lower on higher paid Apify plans (down to $2.00 per 1,000). You only pay for records saved to your dataset, so filters and onlyNew keep the bill small.

A monthly list of 400 new tech companies across four city areas costs $1.20 a month. A list of every new company in one postcode area across all sectors, say 2,000 a month, costs $6. Set a maximum charge per run and the Actor stops cleanly when it is reached. Check the Store page for current pricing.

What to do with the data

  • CRM: use the Apify app in Zapier, Make or n8n to create a company in HubSpot, Salesforce or Pipedrive when the monthly task finishes. Use companyNumber as the unique key.
  • Google Sheets: add the Google Sheets Import & Export Actor (lukaskrivka/google-sheets) as an Actor integration on the task, in append mode, so the team gets one growing sheet.
  • Email: the Gmail integration can send the month's list as an attachment.
  • Territory reports: count new companies by postcodeArea and SIC prefix each month to see where your market is growing.

Limitations

  • Without a key the data is monthly, so a company registered on the 2nd of the month appears in the snapshot published early the next month. Use a key for daily data.
  • The snapshot holds live companies only. Dissolved companies need the API.
  • SIC codes are what the company declared at registration. Many new companies pick a broad or wrong code, so expect some noise.
  • Some small companies use a home address as their registered office. That address is on the public register, but treat it with care. If you use the data for marketing you remain responsible for complying with UK GDPR and PECR.
  • The Actor does not return directors or shareholders, and has no email addresses or phone numbers.

Try it

https://apify.com/agents001/uk-companies-house-feed

Top comments (0)