Tutorial · United Kingdom

Companies House API in Python

The UK register has a free, genuinely good REST API. Here is how to use it from Python — and an honest account of the point where it stops being enough.

Authentication

Companies House uses HTTP Basic auth with your API key as the username and an empty password. That trips people up, because most APIs use a bearer token. Register a free application on the Companies House developer hub to get a key.

import os, requests

CH = 'https://api.company-information.service.gov.uk'
AUTH = (os.environ['CH_API_KEY'], '')   # key as username, password EMPTY

r = requests.get(f'{CH}/company/00445790', auth=AUTH, timeout=20)
r.raise_for_status()
print(r.json()['company_name'])         # TESCO PLC

Search, then fetch

Search returns candidates; the profile endpoint returns the record. Always confirm on company_number rather than name — UK names are not unique and change often.

def search(q, limit=5):
    r = requests.get(f'{CH}/search/companies',
                     params={'q': q, 'items_per_page': limit},
                     auth=AUTH, timeout=20)
    r.raise_for_status()
    return [{'number': i['company_number'],
             'title': i['title'],
             'status': i.get('company_status')} for i in r.json().get('items', [])]

def profile(number):
    r = requests.get(f'{CH}/company/{number}', auth=AUTH, timeout=20)
    r.raise_for_status()
    return r.json()

for c in search('tesco'):
    print(c['number'], c['status'], c['title'])

Officers and PSC

Two separate endpoints, both paginated. PSC — Persons with Significant Control — is the UK beneficial-ownership register, and it is unusually open: most EU equivalents closed to the public after the 2022 CJEU ruling, so this is real ownership data you can actually read.

def paged(path, per_page=50):
    """Companies House paginates with start_index; it does not return a next link."""
    start, out = 0, []
    while True:
        r = requests.get(f'{CH}{path}',
                         params={'items_per_page': per_page, 'start_index': start},
                         auth=AUTH, timeout=20)
        if r.status_code == 404:      # some companies have no PSC resource at all
            return out
        r.raise_for_status()
        items = r.json().get('items', [])
        out += items
        start += per_page
        if len(items) < per_page:
            return out

officers = paged('/company/00445790/officers')
psc      = paged('/company/00445790/persons-with-significant-control')
print(len(officers), 'officers,', len(psc), 'PSC records')

Three things that will bite you

  1. Company numbers are strings, not integers. 00445790 is Tesco; 445790 is a 404. Zero-pad to 8 characters, and never store them as an int.
  2. There is no check digit. Unlike a Spanish NIF or a Norwegian organisasjonsnummer, a UK company number cannot be validated offline — you can only check the shape, then look it up.
  3. Rate limiting is 600 requests per five minutes, and it returns 429 with a Retry-After. Honour it; hammering through it gets keys suspended.

Shape-checking before you spend a call is worth doing — the identifier validator does it in the browser for UK numbers and for the European identifiers that do carry a checksum.

When the free API is enough — and when it isn't

Being straight about this: if you only need the UK, the Companies House API is free, well documented and complete. Use it. There is no honest argument for paying for a wrapper around a free government API.

Where it runs out is at the edges of its scope:

NeedCompanies House
UK company, officers, PSCYes — free and authoritative
Any non-UK countryNo — a separate integration per country
Sanctions or export-control screeningNo
Insolvency outside the UKNo
One schema across countriesNo — each registry has its own shape

The same lookup through Prometiam, with more countries behind the same call and the same response shape:

r = requests.get('https://api.prometiam.com/functions/v1/risk-api/companies/search',
                 params={'name': 'tesco', 'country': 'GB'},
                 headers={'Authorization': f'Bearer {os.environ["PROMETIAM_API_KEY"]}'},
                 timeout=20)
# same call shape for every other country the API covers — one schema, one key

Free trial is 500 credits over 14 days with a card required and nothing charged for 14 days. If the UK is all you need, stay on Companies House — that is the right call and we would rather say so.

FAQ

Is the Companies House API free?
Yes, with a free API key. It is one of the better open government APIs in Europe.

Why does my auth fail?
Almost always because the key is being sent as a bearer token. It is HTTP Basic, key as username, password empty.

Can I validate a UK company number offline?
Only its shape — 8 digits, or two letters and six digits. There is no check digit, so existence requires a lookup.

What is the rate limit?
600 requests per five minutes, returning 429 with Retry-After.

Does Companies House cover other countries?
No, UK only. Each other registry is a separate integration with its own shape — which is the problem a multi-country API exists to solve.

Frequently asked questions

How do you authenticate with the Companies House API?
HTTP Basic auth with your API key as the username and an EMPTY password - not a bearer token, which is the most common cause of 401s. Register a free application on the Companies House developer hub to obtain a key.
Can a UK company number be validated offline?
Only its shape: 8 digits, or two letters followed by six digits such as SC123456. Companies House numbers carry no check digit, unlike a Spanish NIF or a Norwegian organisasjonsnummer, so whether the company exists can only be confirmed by a lookup.
What is the Companies House API rate limit?
600 requests per five minutes. Exceeding it returns HTTP 429 with a Retry-After header, which should be honoured - persistently exceeding the limit can get an API key suspended.
Is the free Companies House API enough?
If you only need the UK, yes - it is free, well documented and authoritative, and there is no good reason to pay for a wrapper around it. It runs out when you need other countries, sanctions or export-control screening, insolvency outside the UK, or one schema across several registries.