Coding

Scavio Companies House

Try it

Search the UK Companies House register by name, then pull a company's full register entry, its officers, and its filing history as structured JSON. 4 endpoints, 1 credit each.

What it does

Search the UK Companies House register by name, then pull a company's full register entry, its officers, and its filing history as structured JSON. 4 endpoints, 1 credit each.

The skill document

Companies House via Scavio

Search the UK register by company name, then read the full register entry, the officers current and resigned, and the complete filing history. All four endpoints return structured JSON from the official UK registry.

When to trigger

Use this skill when the user asks to:

  • Find a UK company's registration number from its name
  • Check whether a UK company is active, dissolved, or overdue on its accounts
  • List a company's directors and secretaries, current and resigned
  • Read a company's filing history and pull the filed PDFs
  • Run KYB or due-diligence checks on a UK counterparty
  • Look up SIC codes, incorporation dates, registered offices or previous names
  • Build UK B2B datasets or company-monitoring pipelines

Setup

Get a free API key at scavio.dev (50 free credits to get started, no card required):

export SCAVIO_API_KEY=sk_live_your_key

Every request is a POST with a JSON body and:

Authorization: Bearer $SCAVIO_API_KEY

Endpoints

Base URL: https://api.scavio.dev. All paths are under /api/v1/companieshouse. Every endpoint costs 1 credit.

EndpointCreditsWhat it returns
POST /api/v1/companieshouse/search1Start here. Name search returning the company number
POST /api/v1/companieshouse/company1Full register entry for one company
POST /api/v1/companieshouse/officers1Officers current and resigned, 35 per page
POST /api/v1/companieshouse/filing-history1Filings, most recent first, with the filed PDF

Note the last path is hyphenated: /filing-history.

Workflow

Search first. Everything except /search is keyed by the company number.

  1. Find the company: call /companieshouse/search with query. The register matches current and former names, so a rebranded company is still findable under its old one.
  2. Read the entry: call /companieshouse/company with company_number.
  3. People: call /companieshouse/officers with the same number.
  4. Filings: call /companieshouse/filing-history with the same number.

Company numbers are normalised for you

company_number is deliberately loose - there is no format check. The register 404s on /company/445790 and /company/sc090312 for companies that genuinely exist, so the transport zero-pads and upper-cases the value before sending it. That means a number copied off a letterhead, or one a spreadsheet stripped the leading zeros from, still resolves.

Registry prefixes supported: SC (Scotland), NI (Northern Ireland), OC / SO / NC (LLPs), FC (overseas), BR (UK establishment), CE (charitable incorporated organisation).

Pagination

The three paginated endpoints behave differently at the end of the list, and it matters.

  • /search - page 1-50, 20 results per page, hard-capped at page 50. The register serves a 1000-result window per term whatever hit count it prints: it will claim 10,000 hits for a broad term and then answer page 51 with an HTTP 416. Narrow the query rather than paging deeper.
  • /officers - page, 35 per page, no upper bound. Past the last page the register answers an ordinary 200 with an empty list.
  • /filing-history - page, no upper bound, same empty-200 behaviour past the end.

So on officers and filings, an empty page is the stop signal - and it is indistinguishable from a company that has no officers or no filings at all. Check page 1 before concluding anything.

/company returns a single object and takes no paging parameter.

Parameters

ParameterTypeDefaultDescription
querystringrequiredCompany name or fragment (1-200 chars, non-blank). Matches current and former names.
pageinteger11-50. 20 results per page; the register only serves the first 1000 matches.

Company (/company)

ParameterTypeDefaultDescription
company_numberstringrequired1-20 chars. Zero-padded and upper-cased for you.

Officers (/officers)

ParameterTypeDefaultDescription
company_numberstringrequired
pageinteger135 per page. No upper bound - past the last page is a 200 with an empty list.

Filing history (/filing-history)

ParameterTypeDefaultDescription
company_numberstringrequired
pageinteger1No upper bound - past the last page is a 200 with an empty list.

Examples

import requests

BASE = "https://api.scavio.dev"
# Your key from https://scavio.dev. Load it from your environment or secret
# store in real code - keep it out of source control.
API_KEY = "sk_your_key_here"
HEADERS = {"Authorization": f"Bearer {API_KEY}"}

# 1. Find the company number - matches former names too
hits = requests.post(f"{BASE}/api/v1/companieshouse/search", headers=HEADERS,
    json={"query": "Monzo Bank", "page": 1}).json()

# 2. Full register entry. Loose numbers are fine - "445790" and "sc090312" both work.
company = requests.post(f"{BASE}/api/v1/companieshouse/company", headers=HEADERS,
    json={"company_number": "09446231"}).json()

# 3. Officers, current and resigned
officers = requests.post(f"{BASE}/api/v1/companieshouse/officers", headers=HEADERS,
    json={"company_number": "09446231", "page": 1}).json()

# 4. Filings, most recent first (note the hyphen in the path)
filings = requests.post(f"{BASE}/api/v1/companieshouse/filing-history", headers=HEADERS,
    json={"company_number": "09446231", "page": 1}).json()

Officers and filings have no upper page bound, so page until a page comes back empty - that is the only stop signal:

def walk(path, company_number, max_pages=10):
    """1 credit per page. Stop on the first empty page."""
    pages = []
    for page in range(1, max_pages + 1):
        data = requests.post(f"{BASE}/api/v1/companieshouse/{path}", headers=HEADERS,
                             json={"company_number": company_number, "page": page}).json()["data"]
        if not data:
            break                     # empty page: past the end (or nothing to list at all)
        pages.append(data)
    return pages

Response shapes

Every response uses the envelope { data, response_time, credits_used, credits_remaining }.

  • search - company number, name, status, incorporation or dissolution date, registered office address, and the matched former names. 20 per page.
  • company - status, type, incorporation and dissolution dates, registered office, SIC codes, previous names, accounts and confirmation-statement due dates with overdue flags, and whether it has charges, insolvency history, officers or UK establishments. An FC company also returns home registry, legal form and governing law; a BR returns its parent; a CE returns the charity number.
  • officers - name, role, appointment and resignation dates, correspondence address, nationality, country of residence, month-and-year date of birth, and identity-verification status. 35 per page.
  • filing-history - date, filing type code (AA, CS01, SH03), description, register annotations and child documents, and a link to the filed PDF with its page count.

Counting active officers. officers_count is every appointment ever made and resignations_count is how many of those ended, so the number of currently-appointed officers is the difference, not officers_count. There is no server-side active/resigned filter - the register does that client-side - so filter on each officer's status in the response.

A filing still being processed carries a processing_note instead of a document. That is not an error and the document is not missing permanently.

Guardrails

  • Every call is 1 credit. A full profile (search + company + officers + filings) is 4.
  • Search is capped at page 50. The register serves a 1000-result window per term no matter what hit count it prints, and page 51 is an HTTP 416. Never promise an exhaustive list from a broad term - narrow the query.
  • On officers and filing history, an empty page means either "past the end" or "there are none". Do not report "no directors" from an empty page 3 - check page 1.
  • Do not compute active officers as officers_count. Subtract resignations_count, or count rows whose status says active.
  • A missing document with a processing_note is a filing the register has not finished processing. Say that, rather than "the document is unavailable".
  • Officer records include a partial date of birth and a correspondence address. This is public register data, but treat it with care: do not compile it into profiles of private individuals beyond what the user actually asked for.
  • Never fabricate company numbers, officer names, dates or filing codes. Only return API data, and quote the company number alongside the name so the user can verify.

Failure handling

  • 400 means an invalid or missing parameter, e.g. a blank query. Fix and retry.
  • 401 means the API key is invalid or missing. Check SCAVIO_API_KEY.
  • 404 means the company number does not resolve. The transport already pads and upper-cases, so re-check the number via /search rather than reformatting it yourself.
  • 429 means rate or usage limit exceeded. Wait before retrying. See rate limits.
  • 502 / 503 mean upstream is temporarily unavailable - wait a few seconds and retry.
  • If a name search returns nothing, try a shorter fragment - the register matches former names, so an old trading name is worth a second attempt.
  • If SCAVIO_API_KEY is not set, prompt the user to export it before continuing.

Python SDK

langchain-scavio has no Companies House tool - use the Scavio SDK directly:

pip install scavio==0.15.0
from scavio import ScavioClient

client = ScavioClient()  # reads SCAVIO_API_KEY

hits = client.companies_house.search("Monzo Bank")
company = client.companies_house.company("09446231")
officers = client.companies_house.officers("09446231", page=1)
filings = client.companies_house.filing_history("09446231", page=1)

JavaScript / TypeScript:

npm install scavio@0.15.0
import { Scavio } from "scavio";

const scavio = new Scavio(); // reads SCAVIO_API_KEY
const hits = await scavio.companiesHouse.search({ query: "Monzo Bank" });
const filings = await scavio.companiesHouse.filingHistory({ company_number: "09446231" });

Related skills

Enrich a list of companies (names, domains, or LinkedIn URLs) with firmographic data like industry, employee count, headquarters, and founding year. Use when the user wants to enrich, look up, or research a batch of companies.

1 installs

Handelsregister AI (handelsregister.ai). Use this skill for ANY Handelsregister AI request — searching and reading data. Whenever a task involves Handelsregister AI, use this skill instead of calling the API directly.

Search for people at a company by domain and role keywords. Returns obfuscated profiles (first name, last-name initial, title, company). No email addresses....

1 installs

Retrieve overseas shareholders, executives and ultimate beneficial owners by company ID. Analyze equity structure for due‑diligence, investment research and competitor background checks. Trigger: ultimate beneficial owner check, corporate equity structure lookup, overseas shareholder research, company due‑diligence, related‑party transaction screening, identify actual company controllers

2 installs1 stars