Developers

How to build a KYB agent for UK companies with an LLM and Companies House data

Updated By
Short answerA KYB agent for UK companies should run fixed rules first (status, age, overdue accounts, strike-off proposal, net assets and cash trend) and use an LLM only to explain the result. One call to /v1/company returns the register record and balance sheet history; on 1 October 2026, 93,785 active companies were overdue with their accounts.

What a KYB agent for UK companies does

A KYB (know your business) agent takes a company name or number, pulls the public record, applies your risk rules and hands a reviewer a short, sourced verdict. For UK companies most of the useful signal sits in two Companies House datasets: the register (status, incorporation date, accounts due dates, outstanding charges) and the filed accounts (net assets, cash, creditors). The agent's job is to read both consistently every time, not to make the final decision.

Typical uses are supplier onboarding, checking a new trade customer before granting credit terms and screening a list of counterparties overnight. If you only need a manual check, the supplier check guide covers the same ground by hand.

What the agent should check

A useful company check covers status, age, filing discipline and the direction of the balance sheet, then notes what it could not see. Each signal below is available from one API call, apart from the industry comparison and PSC details.

  • Status. Anything other than "Active" (liquidation, administration, dissolved) stops onboarding. See company status.
  • Strike-off proposal. "Active - Proposal to Strike off" means the registrar or the directors have started removing the company. On 1 October 2026, 423,006 companies carried this status and 58% of them were also overdue with accounts (strike-off data).
  • Age. A company incorporated in the last 12 months has no filed accounts to judge.
  • Accounts overdue. A next due date in the past on an active company. On 1 October 2026, 93,785 active companies (1.8%) were overdue (overdue accounts data).
  • Net assets and their trend. Negative net assets, or a fall of 30% or more between the last two balance sheets. 18.8% of active companies with machine-readable accounts report negative net assets (negative net assets data).
  • Cash against short-term creditors. Cash below creditors due within one year is common (only 35.3% of companies cover them with cash), so treat it as context, not a red flag on its own.
  • Net current assets. Turning negative between two filings suggests working capital pressure.
  • Industry comparison. Compare net assets and cash with the median for the company's SIC division on our industry pages. The API does not return a benchmark, so the agent uses the SIC code from the response.
  • Charges. The register gives a count of outstanding charges, not the lender or terms. See the charges register.
  • PSC. People with significant control are not in our data. Pull them from Companies House directly (what a PSC is).

Reference architecture

The reliable pattern is deterministic rules first, the LLM second and a person last. Rules give the same answer for the same data, which you can explain to an auditor; the LLM turns the facts into a readable note.

KYB agent: rules in code first, the LLM second, a person last KYB AGENT FOR UK COMPANIES: REFERENCE ARCHITECTURE Rules decide, the LLM explains, a person signs RULES IN CODE LLM PERSON clean PASS skips review 1 Resolve name to number search_by_name 2 Fetch record and up to 20 filings lookup_company 3 Score plain code, rule table PASS REFER STOP 4 Explain writes the note from the facts and flags, decision unchanged 5 Review signs off every REFER and STOP, notes why 6 Monitor approved ones watch_add get_alerts STOP status is not "Active", including a proposal to strike off REFER accounts overdue, under 12 months old, no iXBRL figures, negative net assets, net assets or cash down 30% or more NOTE cash below creditors due within one year, outstanding charges Same key and quota over REST (/v1/company) or MCP · thresholds are a starting point, tune them
Code resolves, fetches and scores the company, the LLM only explains the result, and a person signs off anything that is not a clean pass.
  1. Resolve. Name to company number with /v1/search-by-name (MCP tool search_by_name). Skip it when the customer gives you the number.
  2. Fetch. /v1/company?number=...&format=json (MCP tool lookup_company) returns the register record and up to 20 accounts filings, newest first.
  3. Score. Plain code applies the rule table below and produces a list of flags and a decision: pass, refer or stop.
  4. Explain. An LLM receives only the JSON facts and the flags and writes a short note. It must not change the decision.
  5. Review. A person signs off anything that is not a clean pass and records the reason.
  6. Monitor. Add approved companies to the watchlist with /v1/watchlist/add (MCP watch_add) and poll /v1/alerts?format=json (MCP get_alerts) for status changes, overdue accounts, new charges and falling net assets or cash.

Use the REST API when the check runs inside your own backend or a batch job. Use the MCP server when an analyst works in Claude, ChatGPT or Cursor and wants to ask follow-up questions. Both use the same key and the same quota.

Rule table

These thresholds are a sensible starting point, not a standard; tune them to your risk appetite and record why.

SignalThresholdOutcomeWhy
StatusNot "Active"StopLiquidation, administration or dissolution
Strike-off proposalStatus "Active - Proposal to Strike off"StopThe company may be dissolved within months
Accounts overdueaccounts.next_due before todayReferMissed statutory deadline, often an early distress sign
AgeIncorporated less than 12 months agoReferNo accounts history to assess
No figuresaccounts array emptyReferPDF-only filer or no accounts yet; read the filing
Negative net assetsLatest net_assets below 0ReferLiabilities exceed assets
Net assets fallDown 30% or more, previous above £10,000ReferSame rule as our watchlist alerts
Cash fallDown 30% or more, previous above £10,000ReferLiquidity weakening
Net current assetsTurned negative between filingsReferWorking capital pressure
Cash vs creditorscash below creditors_within_one_yearNoteCommon, context only
Chargescharges_outstanding above 0NoteSecured lending exists; check who holds it

Working example in Python

The script below fetches one company with format=json, applies the rules and prints a decision with flags. It uses one request per company, so the free key covers about 50 checks a month.

import os
import sys
from datetime import date, timedelta

import requests

BASE = "https://data.globalmindsetmarketing.co.uk"
HEADERS = {"Authorization": f"Bearer {os.environ['UKCD_KEY']}"}


def fetch(number):
    r = requests.get(f"{BASE}/v1/company",
                     params={"number": number, "format": "json"},
                     headers=HEADERS, timeout=30)
    if r.status_code == 404:
        return None
    r.raise_for_status()
    return r.json()


def fell(prev, cur, field):
    a, b = prev.get(field), cur.get(field)
    return a is not None and b is not None and a > 10_000 and 0 <= b < a * 0.7


def check(data, today=None):
    today = today or date.today()
    c, accounts = data["company"], data["accounts"]
    flags = []
    status = c.get("status") or ""

    if status == "Active - Proposal to Strike off":
        flags.append(("stop", "Proposal to strike off"))
    elif status != "Active":
        flags.append(("stop", f"Status: {status or 'unknown'}"))

    due = (c.get("accounts") or {}).get("next_due")
    if status == "Active" and due and date.fromisoformat(due) < today:
        flags.append(("refer", f"Accounts overdue since {due}"))

    inc = c.get("incorporated")
    if inc and date.fromisoformat(inc) > today - timedelta(days=365):
        flags.append(("refer", f"Incorporated {inc}, under 12 months old"))

    if not accounts:
        flags.append(("refer", "No machine-readable accounts: read the filing"))
    else:
        cur = accounts[0]["figures"]
        na, cash = cur.get("net_assets"), cur.get("cash")
        cred = cur.get("creditors_within_one_year")
        if na is not None and na < 0:
            flags.append(("refer", f"Negative net assets: {na:,} GBP"))
        if len(accounts) > 1:
            prev = accounts[1]["figures"]
            if fell(prev, cur, "net_assets"):
                flags.append(("refer", "Net assets fell 30% or more"))
            if fell(prev, cur, "cash"):
                flags.append(("refer", "Cash fell 30% or more"))
            p_nca, c_nca = prev.get("net_current_assets"), cur.get("net_current_assets")
            if p_nca is not None and c_nca is not None and p_nca >= 0 > c_nca:
                flags.append(("refer", "Net current assets turned negative"))
        if cash is not None and cred is not None and cash < cred:
            flags.append(("note", "Cash below creditors due within one year"))

    if (c.get("charges_outstanding") or 0) > 0:
        flags.append(("note", f"{c['charges_outstanding']} outstanding charge(s)"))

    levels = {level for level, _ in flags}
    decision = "stop" if "stop" in levels else "refer" if "refer" in levels else "pass"
    return {"company_number": c["company_number"], "name": c.get("name"),
            "decision": decision, "flags": flags,
            "latest_balance_sheet": accounts[0]["balance_sheet_date"] if accounts else None,
            "sic_codes": c.get("sic_codes", []), "source": data["source"]}


if __name__ == "__main__":
    data = fetch(sys.argv[1])
    if data is None:
        sys.exit("Company not found")
    result = check(data)
    print(result["name"], result["decision"].upper())
    for level, text in result["flags"]:
        print(f"  [{level}] {text}")

Run it with python kyb_check.py 10623966. Field names come straight from the JSON response: company.status, company.incorporated, company.accounts.next_due, company.charges_outstanding and, in each accounts[].figures object, net_assets, cash, net_current_assets and creditors_within_one_year. A figure is null when the company did not file it. Setup and the full response shape are in the API quickstart.

For the narrative step, pass result and the raw data to your LLM with an instruction to explain the flags in plain English, cite the balance sheet date and keep the decision unchanged. Store the JSON, the note and the reviewer's sign-off together.

The same check in Claude with MCP

With the MCP server connected, Claude can run the whole check from a prompt, which suits an analyst reviewing one company at a time. Setup takes a minute in the Claude connector guide. A prompt that keeps the rules in charge:

You are a KYB assistant for UK companies. For the company I name:
1. Use search_by_name if I give a name, then lookup_company.
2. Apply these rules exactly and list every rule that fires:
   STOP if status is not "Active" (including "Active - Proposal to Strike off").
   REFER if accounts next due is before today, if incorporated less than
   12 months ago, if there are no iXBRL figures, if net assets are negative,
   or if net assets or cash fell 30% or more between the last two
   balance sheets (previous value above 10,000 GBP).
   NOTE if cash is below creditors due within one year or charges are outstanding.
3. Give the decision (PASS, REFER or STOP), then a five-line summary with
   balance sheet dates and figures as filed.
4. Say what you could not check: PSC, officers, sanctions, ID verification.
Do not guess figures. If a field is missing, say so.
If the decision is PASS, offer to add the company with watch_add.

Later, "show my alerts" calls get_alerts, which reports high, medium and info alerts for every watched company, sorted by severity.

Honest limits

A company check is one part of KYB, not the whole of it. Our data does not cover:

  • Sanctions and PEP screening. Use a dedicated screening provider and the UK sanctions list.
  • Identity verification of directors, PSCs or the person you are dealing with.
  • Officers and PSC search. Not in our API; use the Companies House service.
  • Credit scores or limits. Figures are as filed by the company and are not a credit rating.
  • PDF-only accounts. Many large groups file PDFs, so the accounts array is empty for them.
  • Live status. The register snapshot is refreshed monthly; confirm status on Companies House before a final decision.

If you are a regulated business under the Money Laundering Regulations 2017, regulation 28 requires you to obtain and verify the company's name, company number and registered office address, determine its constitution and board of directors, take reasonable measures to understand its ownership and control structure, and identify its beneficial owners. Regulation 28(9) says you cannot meet the beneficial owner requirement by relying solely on information delivered to the registrar, and regulation 28(11) requires ongoing monitoring of the relationship. HMRC guidance also expects a risk-based approach to verification. An automated company check supports these steps but does not complete them.

Pricing for an agent

A KYB agent uses one request per company lookup and one more for a name search. The free key gives 50 requests a month and a 5-company watchlist. API Pro is £39 a month for 5,000 requests and a watchlist of 50 companies with alerts (pricing). Get a free key from the developer docs and run the script above on your first supplier.

Sources

FAQ

Can an LLM make the KYB decision on its own?

It should not. Language models can misread a figure or apply a threshold inconsistently, and you need to explain every decision later. Let code apply the rules and produce the pass, refer or stop result, let the model write the explanation from the same facts, and keep a person responsible for anything that is not a clean pass.

Is a Companies House check enough for AML customer due diligence?

No. Regulation 28 of the Money Laundering Regulations 2017 requires you to verify the company, understand its ownership and control and identify beneficial owners, and you cannot rely solely on the register for beneficial owners. You also need sanctions screening, identity checks where required and ongoing monitoring under your own risk assessment.

Why do some companies return no financial figures?

The figures come from machine-readable iXBRL accounts. Companies that file only PDF accounts, which includes many large groups, and companies that have not filed accounts yet return an empty accounts array. The register record, status and due dates are still returned, and the agent should refer these cases to a person who reads the filing.

How do I keep checking a supplier after onboarding?

Add its company number to the watchlist with POST /v1/watchlist/add or the watch_add tool, then poll /v1/alerts with format=json. Alerts cover status changes, overdue accounts, newly published accounts, new charges, negative net assets and falls of 30% or more in net assets or cash. Free keys watch 5 companies, API Pro watches 50.

Try it with a free key. 50 requests a month, REST and MCP, no card. Company register, iXBRL financial history since 2008 and watchlist alerts.

Get a free API key