UK B2B Lead Verifier: Companies House and Email
Pricing
from $2.70 / 1,000 row judgeds
UK B2B Lead Verifier: Companies House and Email
Verify UK B2B leads against Companies House: each lead is marked ok, dissolved, in distress or not on the register, with company number, address and register link, plus email, website and phone checks. Paste columns, a CSV URL or the dataset of a lead-finder run.
Pricing
from $2.70 / 1,000 row judgeds
Rating
0.0
(0)
Developer
Pradio Actors
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
9 hours ago
Last modified
Categories
Share
What does UK B2B Lead Verifier do?
UK B2B Lead Verifier checks every lead on a bought UK list against the Companies House register and returns one verdict row per lead. Companies House is the UK's public register of companies. A lead that claims a UK company is matched on its name, number or website stem; its email domain, website and phone are checked alongside. Each judged row carries 33 fields. You get the verdict, a match score and the register page to open as proof. You get the matched company's number, status, registered address, dates, SIC codes, filing deadlines and officer count. And you get flags for disposable, role and free-provider emails. Paste the list in as columns, point it at a CSV or JSON file, or pick the dataset of a lead-finder run, then press Start.
On a measured run of 100 leads, all 100 came back judged: 70 verified ok, 4 read dissolved, 2 in_distress and 1 not_on_register. The other 23 read unverifiable, because no register entry matched them confidently. The run named the closest entry and declined to guess. A judged row costs $0.005.
Who uses UK B2B Lead Verifier
| Who | What they run it for |
|---|---|
| A founder or sales-ops lead who just paid for a list | Check every row before the first send instead of spot-checking a sample by hand. |
| An outbound team about to spend sender reputation | Suppress dissolved and in-distress companies before the campaign, not after the bounces. |
| A buyer disputing a bad list | The dead rows come back with the reason and the register link, ready to take to the seller for a refund. |
| A calling team working a phone column | Valid numbers come back with a line type; invalid ones are flagged before anyone dials. |
Features
- Register verdict on every UK-signalled lead. The claimed company name, company number or website stem is matched against Companies House. A UK signal is a company number, a +44 phone, a .uk domain or a UK legal form such as ltd or plc in the name. A row carrying no UK signal is never searched, so a non-UK company can never collect a false UK verdict.
- No check behind a paywall. Register status, MX records on the email domain, a live-website probe, a phone-number parse with line type, and the email flags below. No paid lookups and no API keys.
- Columns, a file or another run's dataset. Paste
emails,urls,companiesandphonesas parallel columns, or setleadListUrlto a CSV or JSON list. Or pick a lead-finder run's dataset indatasetIdand chain the two runs. - Email checks beyond MX. Every email is flagged as disposable (a throwaway-address domain), a role mailbox (info@, sales@, support@ and similar) or a free provider (gmail.com, outlook.com, btinternet.com and similar). No request is made: the address alone answers.
- Register detail for every matched company. SIC codes, whether the accounts or the confirmation statement are overdue, and the number of active officers, all from Companies House.
- Duplicates dropped before you pay. A lead that repeats an earlier lead's company number or email is dropped before judging, and
RUN_SUMMARYsays how many and on which key. - No login, no browser, no key. Every check is a plain logged-out GET or a DNS lookup. Per lead that is one MX lookup on the email domain, one read of the website and one register search. With register detail on, a matched company adds two more register reads: its data page and its officers page. The register's developer API needs a key; the public search this Actor reads does not.
- A lead is matched only when the names agree. The register's name must be the lead's claim, legal form aside, or a former name the register lists for the company. A near miss, two same-named companies, or a name that sits inside a longer one reads
unverifiablewith the closest entry named inreason. A dissolved or in-distress verdict needs the closest agreement of all. It also needs a website or email domain that does not point to another company, so a live lead is never marked dead on a guess. - Polite register reads. Lookups run one at a time with a pause between them, under the register's 120-requests-a-minute guideline. A refusal is backed off once, not hammered.
- Misses are free and named. A failed register read, a block, a timeout or a row with nothing to judge is pushed uncharged, with the reason on it.
What you can count on
- You pay for each
ROWthe run judged, and for nothing else. Anunverifiablerow is one of those and is billed: its email, website and phone checks ran on it, even when the register gave no answer for it. Only a miss is free: a row the run could not judge is pushed as an unchargedITEM_STATUSrow, so you see the miss and are never billed for it. - Every row is charged only after it is written to your dataset; a row you cannot see is never billed.
- A run that finds nothing returns one
NOTHING_TO_JUDGErow that says so, never an empty dataset. - A spending limit stops the run cleanly with a
STOPPED_EARLYrow saying how many rows were returned and how many were not. - Every run writes a
RUN_SUMMARYentry with rowsFetched, rowsPushed, rowsCharged and duplicatesDropped, so a short run and a broken one are told apart. - If the register changes what it answers, the run fails with the error in the log. It never returns rows full of nulls and calls it success.
- No value is invented: a field the register does not show is null, and this page says which.
Why this one
- The most-used alternative on this platform was run on 2026-09-25 with two of the company names from this Actor's example input. It is a lead finder, so it answers a different question: it returned 3 rows, and email, phone and domain were empty on all 3. This Actor's email and domain come from your own list, so they are not the win. What it adds is the register's word on each lead: in its example run it read Tesco PLC
okand Carillion PLCin_distress(in liquidation), each with the register page linked. - No start fee of ours. Start fees across the Store run from $0.00001 to $0.10 before the first row comes back. Apify's standard start charge applies ($0.00005 per event, one event per GB of run memory with a minimum of one, so one event per run at this Actor's 256 MB default), and this Actor declares no extra start event of its own.
- A miss is free and explained. The row still lands in your dataset, uncharged, with the failure named on it.
- The judged rate is measured, not promised. A list of 123 leads the product had never seen came back 123 of 123 judged. Judged is not verified:
unverifiableis a judged, billed verdict, and it was 23 of the 100 leads in the scale run. The list was worked in runs under the 100-lead cap and read without a browser. - Every matched row carries
evidence_url, the live register page behind the verdict. In the measured run it filled on all 76 matched rows of the 100.
What data does UK B2B Lead Verifier return?
One row per lead. A real dissolved verdict from a run, for a lead that gave its company number, looks like this:
{"email": "info@maplin.co.uk","domain": "maplin.co.uk","status": "dissolved","match_score": 98,"reason": "MAPLIN ELECTRONICS (HOLDINGS) LIMITED (04220419): dissolved on Companies House; the email domain accepts mail; the email is a shared role mailbox; the website answers","error": null,"found": true,"is_active": false,"claimed_company": "04220419","matched_name": "MAPLIN ELECTRONICS (HOLDINGS) LIMITED","normalised_status": "dissolved","status_as_of": "2026-09-25T13:25:32.003Z","phone_number_status": null,"phone_line_type": null,"website_answers": true,"email_domain_accepts_mail": true,"dead_reason": "dissolved on the register 2019-12-07","evidence_url": "https://find-and-update.company-information.service.gov.uk/company/04220419","company_status": "dissolved","company_number": "04220419","company_type": "ltd","date_of_creation": "2001-05-21","date_of_cessation": "2019-12-07","address": {"premises": "C/O Pricewaterhousecoopers Llp Central Square","address_line_1": "29 Wellington Street","locality": "Leeds","postal_code": "LS1 4DL"},"register_description": "04220419 - Dissolved on 7 December 2019","sic_codes": ["74990"],"officer_count": 3,"accounts_overdue": null,"confirmation_statement_overdue": null,"email_disposable": false,"email_role_account": true,"email_free_provider": false,"row_type": "ROW"}
The verdict vocabulary
status carries one of five charged verdicts, or a miss word on an uncharged row:
status | What it means |
|---|---|
ok | Matched on the register and active there. The website, email and phone checks sit beside the verdict and do not change it, so an ok row can still carry website_answers false. On a weaker match (match_score under 90) the verdict is about the entity matched_name names, so check it is yours. |
dissolved | Matched on the register; the entry shows it dissolved, by its status word or, when the entry carries no status word, by a closed company type such as converted-or-closed named on dead_reason. |
in_distress | Matched; the register shows liquidation, administration, receivership or similar. |
not_on_register | The UK register was searched and no entry matched the claim. It means "no UK company matched", not "the company does not exist". |
unverifiable | Charged. The register gave no verdict on the row, but the email, website and phone checks ran on it and their results are on the row, so it is billed like any other judged row. The register gives no verdict when the lead carried no UK signal to search on, when the register had already refused the run and was not asked for this row, when the matched entry carried no status the run could classify, or when no single register entry matched the claim closely enough to name it. On that last kind reason names the closest entry and why it was not taken. Its match_score is null. A row whose own register read was attempted and failed is a miss, not this verdict, and is free. |
fetch_failed, blocked, timeout, aborted, not_found, error | Misses: the run could not judge this row. Pushed as uncharged ITEM_STATUS rows so you see them, never billed. These are the only free rows. The status word error means an unexpected failure; it is not the error field, which carries the failure's detail. |
Two words are easy to confuse and worth a second look. not_on_register is a verdict: the register was searched and nothing matched. not_found is a miss: the row carried no email, website, company or phone to judge, and it is free.
Every field, in order
| Field | What it carries |
|---|---|
email | The email the lead row claimed, echoed back so the verdict lines up with your list. Null when the list carried no email column. |
domain | The domain the lead was judged on, taken from its website or else from its email address. |
status | The verdict, or the miss word on an uncharged row. |
match_score | Register-match confidence: how strongly the register answer supports the verdict, 0 to 100. A score of 95 or more is the claim itself, name or number, and 90 is a former name the register lists for the company. A score of 85 means one name starts with the other and the longer adds one distinctive word, and only an ok verdict is given on such a match; matched_name and evidence_url name and link the entity it is about. A dissolved or in-distress verdict always scores 90 or more. A fixed middling value on not_on_register; null on unverifiable, where a number would claim confidence the register never gave. |
reason | One sentence naming the matched company and what each check found, readable without opening the register. |
error | The failure's detail in words, such as HTTP 403, on an uncharged miss row whose status is fetch_failed, blocked, timeout or error. Not the same thing as the status word error, which names the kind of miss. Null on not_found and aborted misses, whose reason says why, and on every judged row. |
found | True when a register entry matched the claim. |
is_active | True when the matched company is active on the register right now, that is only when normalised_status is active. |
row_type | ROW for a judged lead, ITEM_STATUS for an uncharged miss, NOTHING_TO_JUDGE or STOPPED_EARLY on a status row. |
register_description | The register's own one-line description of the company, verbatim: such as "Incorporated on 27 November 1947", truncations included. |
address | The registered office address the register publishes, as an object of region, locality, postal code and address lines. Null when the register's entry carries none, as on the Bank of England's royal-charter entry. |
company_type | The register's type word for the company, verbatim: ltd, plc, converted-or-closed and the like. |
company_status | The register's own status word: active, liquidation, dissolved and so on. |
date_of_creation | The incorporation date the register shows. |
company_number | The register's permanent number for the matched company. |
date_of_cessation | The dissolution date, when the register's entry names one. A dissolved row whose entry carries no date stays null. |
claimed_company | The company your lead row claimed, echoed from the input: a name or a UK company number. |
matched_name | The register's legal title for the entry it matched, so a trading name still lands on the right company. |
normalised_status | The normalised register read. active, dissolved and in_distress: the matched company's state on the register. no_match: the register was searched and no entry matched. unchecked: the register gave no usable answer for this row. That is when the row gave no company name, number or company domain to search on, when the register had refused the run and was not asked, when the matched entry's status could not be classified, when no single entry matched closely enough, or when the lead is on a UK public-sector domain. A row whose own register read failed also reads unchecked, but its status names the miss (blocked, timeout or fetch_failed) and it is free. not_applicable: the row carried no UK signal, so the register was not asked. |
status_as_of | When the verdict was computed, ISO-8601. |
phone_number_status | valid or invalid as a number. Whether a line rings is not claimed. |
phone_line_type | The line type where the number parses: mobile, fixed_line, toll_free, voip and similar. |
website_answers | Whether the website answers HTTP at all. A 403 still counts as an answer; false means nothing answered. |
email_domain_accepts_mail | Whether the email's domain publishes MX records. The mailbox itself is never probed. |
dead_reason | On a dissolved or not_on_register verdict, which check killed the row, with the register date when there is one. An in_distress company (in liquidation or administration) is still on the register, not dissolved, and an unverifiable one was never confirmed, so they carry none. |
evidence_url | The register page to open to see the evidence behind the verdict. |
sic_codes | The matched company's SIC codes, five-digit strings such as 47110: what the company says it does. An empty list when the register holds none. |
officer_count | How many officer appointments are active, not resigned, from the count the register prints on the company's officers page. Only the number is read. |
accounts_overdue | True when the register's next accounts due date has passed. Null when the register shows no due date, as for a dissolved company. |
confirmation_statement_overdue | True when the register's next confirmation statement due date has passed. Null when the register shows none. |
email_disposable | True when the email is on a known disposable, throwaway address domain. |
email_role_account | True when the email is a shared role mailbox such as info@, sales@ or support@, not a named person's. |
email_free_provider | True when the email is on a free mailbox provider such as gmail.com or outlook.com, not a company domain. |
The four register-detail fields are null when no company matched, or when includeRegisterDetail is off. The three email flags are null when the row has no email.
Status rows carry four fields of their own: reason says why the run ended that way, and rowsFetched, rowsReturned and rowsRemaining give the counts.
On the measured run's 100 judged rows, the register fields filled on the rows the register matched. matched_name, company_number, company_type, company_status, register_description and evidence_url filled on 76 of 100: every ok, dissolved and in_distress row. address, date_of_creation, sic_codes and officer_count filled on 75, and the two overdue flags on 71. match_score filled on 77, every row but the 23 unverifiable ones. claimed_company, domain, status, reason, found, is_active, normalised_status and status_as_of filled on all 100. date_of_cessation filled on all 4 dissolved rows. dead_reason filled on 5: the 4 dissolved rows and the 1 not_on_register row.
One field stays null on every judged row by design, not by accident:
| Field | Why it stays null |
|---|---|
error | It fills only on an uncharged miss row, where it gives the failure's detail. A judged row has nothing to report. |
The Console's Overview view hides error; the All fields view and every export carry it.
How much does it cost?
The price is $0.005 per judged row, billed as the row-judged event, and only after the row is written to your dataset. A dead lead is still a judged row: a dissolved or not_on_register verdict is the answer you paid for, and an unverifiable row is billed too, because its email, website and phone checks ran on it even when the register gave no answer. Only ITEM_STATUS miss rows, status rows and dropped duplicates are never billed.
| Leads in the list | If every lead judges |
|---|---|
| 100 | $0.50 |
| 1,000 | $5.00 |
| 10,000 | $50.00 |
Coverage was measured on a different list from the run above: a set of 123 leads the product had never seen came back 123 of 123 judged, worked in runs under the 100-lead cap, with no browser. A lead the run cannot read comes back as a free row, so you pay only for judged leads, unverifiable ones included. A run judges at most 100 leads, so longer lists are worked in runs of 100. Apify also bills its standard apify-actor-start charge at $0.00005 per start event, one event per GB of run memory with a minimum of one. At this Actor's 256 MB default that is one event per run. This Actor adds no start fee of its own.
How do I use UK B2B Lead Verifier?
- Open the Actor page and press Start.
- Feed it leads one of three ways. Paste the
emails,urls,companiesandphonescolumns, keeping the rows parallel so position 1 in each array is the same lead. Or setleadListUrlto a CSV or JSON list. Or pick a lead-finder run's dataset indatasetId. - Leave
maxItemsat its default of 100, which is also the cap, or set a lower number of rows the run may return. - Press Start. Verdict rows land in the dataset as they are judged.
Example input, the same four leads the input form ships with:
{"emails": ["press.office@tesco.com", "info@carillion.co.uk", "info@maplin.co.uk", "j.smith@mailinator.com"],"urls": ["https://www.tesco.com", "https://www.carillion.co.uk", "https://www.maplin.co.uk", "https://zzqx-nonexistent-example.com"],"companies": ["TESCO PLC", "CARILLION PLC", "04220419", "ZZQX EXAMPLE NONEXISTENT LIMITED"],"phones": ["+44 1992 632222", "+44 20 7946 0958", "", ""],"maxItems": 100}
The same run through the Apify API is one call:
curl -X POST "https://api.apify.com/v2/acts/Pradio~b2b-leads/run-sync-get-dataset-items?token=YOUR_APIFY_TOKEN" \-H "Content-Type: application/json" \-d '{"emails": ["info@carillion.co.uk"], "companies": ["CARILLION PLC"]}'
Input
| Input | Type | Default | What it does |
|---|---|---|---|
emails | array | four example rows | The email each lead row claims. Its domain is checked for MX records; the mailbox is never probed. |
urls | array | four example rows | The website each lead claims, fetched once to see whether it answers. Its domain also helps match the company on the register. |
companies | array | four example rows | The company name or UK company number each row claims. A registered name or a number matches sharpest. |
phones | array | four example rows | The phone number each row claims, checked for validity and line type. |
leadListUrl | string | none | A CSV or JSON lead list read instead of, or as well as, the columns above. |
maxItems | integer | 100 | The most rows one run returns, capped at 100. Leads beyond the cap are not judged. |
datasetId | string | none | An Apify dataset to read leads from, such as a lead-finder run's output: one of yours, or a public dataset ID. |
datasetFieldMap | object | none | Which field of each dataset item holds the company, company number, email, phone and website, when the guesses miss. |
includeRegisterDetail | boolean | true | Read SIC codes, overdue filings and the officer count for each matched company. Off gives a faster run. |
The columns are parallel
emails[i], urls[i], companies[i] and phones[i] describe one lead. Any subset works. A lead with only a domain is still matched by the domain's name stem. A lead with only a phone still gets its number checked. A row with nothing usable comes back as a not_found miss, free.
leadListUrl
Point it at a CSV file or a JSON array of objects; an Apify dataset-items URL from a lead-finder run works directly. CSV headers are matched by the names list sellers actually use. The email column answers to email, e-mail or mail. The website answers to website, url or domain. The company answers to company, organisation or business. The phone answers to phone, telephone or mobile. A company number column answers to company_number, crn or registration_number. Only those columns are read. A contact's name, a job title or any other column in the file is never copied into a row. A list URL that answers with an error fails the run instead of returning an empty dataset.
Chain a lead-finder run with datasetId
Pick the dataset of any earlier run in datasetId, or give its ID, and each item becomes a lead. The run reads the items through the Apify API with its own access, one page at a time, until it holds as many distinct leads as maxItems allows. From each item it reads five values and nothing else: the company name, the company number, the email, the phone and the website. A contact's name, a job title or any other field is never copied into a row.
{"datasetId": "YOUR_LEAD_FINDER_DATASET_ID","maxItems": 100}
The run guesses where each value sits. The company is companyName, company_name or company, then title or name, but those two only when the item carries no person fields such as firstName or jobTitle. The company number is companyNumber or company_number. The email is email or the first of emails. The phone is phone or the first of phones. The website is website, url or domain. When your dataset names them differently, say where they are in datasetFieldMap. A path can reach into an object or a list, and an empty string skips that value:
{"datasetId": "YOUR_LEAD_FINDER_DATASET_ID","datasetFieldMap": {"company": "organization.name","email": "contact.emails[0]","url": "organization.website","phone": ""}}
A dataset ID that does not exist, or that the run may not read, fails the run with the ID in the error. It never returns an empty dataset as if the list were empty.
Email checks
Every email is read three ways from the address alone, with no request and no mailbox probe. email_disposable is true on a throwaway-address domain, checked against the public disposable-email-domains list (CC0), bundled with the Actor and dated 24 September 2026. email_role_account is true on a shared mailbox such as info@, sales@, accounts@ or noreply@. email_free_provider is true on a free mailbox such as gmail.com, hotmail.co.uk, btinternet.com or gmx.de. They run on every lead with an email, whatever the other inputs:
{"emails": ["info@acme-example.co.uk", "j.smith@gmail.com", "sales@mailinator.com"],"maxItems": 3}
Register detail
For every lead matched on the register, the run reads two more Companies House pages: the company's public data page and its officers page. They are read one at a time, with the same pause as the search. From them it fills sic_codes, accounts_overdue, confirmation_statement_overdue and officer_count. Only the officer count is kept; no officer's name, address or date of birth is read into a row. Turn it off for a faster run that returns the verdict and the company particulars only:
{"companies": ["00445790", "03782379"],"includeRegisterDetail": false}
Duplicate leads
Before judging, a lead that repeats an earlier lead's company number or email is dropped. Company numbers are compared upper-cased and zero-padded to eight digits, so 445790 and 00445790 are the same company. Emails are compared trimmed and case-folded. Two leads with the same company name and different emails are both kept. RUN_SUMMARY carries the count under leads: inputDuplicatesDropped, and inputDuplicatesByKey with company_number and email. Dropped duplicates are never billed.
{"emails": ["press.office@tesco.com", "Press.Office@tesco.com"],"companies": ["TESCO PLC", "TESCO PLC"]}
Worked examples
Use it to verify a UK lead list you pasted in before a campaign. Leads go in as parallel columns, the same shape as the input form's example:
{"emails": ["press.office@tesco.com", "info@carillion.co.uk"],"urls": ["https://www.tesco.com", "https://www.carillion.co.uk"],"companies": ["TESCO PLC", "CARILLION PLC"],"phones": ["+44 1992 632222", ""]}
Use it to find the disposable, role and free-mail addresses in a lead list. Each email is flagged from the address alone, and its domain is checked for MX records:
{"emails": ["info@carillion.co.uk", "j.smith@gmail.com", "sales@mailinator.com"],"maxItems": 3}
Use it to check whether UK companies are still trading. This returns verdicts and particulars only, without the extra register reads:
{"companies": ["00445790", "03782379", "04220419"],"includeRegisterDetail": false}
The output of a lead-finder run works too. Pick its dataset and the run reads the leads straight from it:
{"datasetId": "YOUR_LEAD_FINDER_DATASET_ID","maxItems": 100}
A bought list you hold as a CSV file works the same way. Host the file anywhere that answers a plain GET:
{"leadListUrl": "https://example.com/bought-leads.csv","maxItems": 100}
Output
Every row carries a row_type. ROW and ITEM_STATUS are the kinds of row a run returns per lead; NOTHING_TO_JUDGE and STOPPED_EARLY are single rows that close a run which did not simply fill:
row_type | What it means |
|---|---|
ROW | A judged lead, unverifiable included. Charged. |
ITEM_STATUS | A miss the run could not judge. Pushed so you see it, never billed. |
NOTHING_TO_JUDGE | The input gave the run no leads: empty columns and no leadListUrl, or a file whose rows carried none of the four columns. One row, uncharged. A zero result is an answer, not silence. |
STOPPED_EARLY | The charge limit was reached before every fetched row was pushed. The row carries rowsFetched, rowsReturned and rowsRemaining, so finishing and being cut short never produce the same dataset. |
Every run also writes a RUN_SUMMARY entry to the run's key-value store: rowsFetched, rowsPushed, rowsCharged, rowsUncharged, duplicatesDropped, startedAt and finishedAt. Under leads it carries the input's own counts: leadsRead, inputDuplicatesDropped, inputDuplicatesByKey and leadsJudged, plus datasetItemsRead when a dataset was read. It is the fast way to tell a short list from a broken run. When maxItems cuts a run short, rowsFetched against rowsPushed there shows what the list held against what came back.
What can you do with the data?
Take the dead rows back for a refund. Filter status to dissolved, in_distress and not_on_register, and dead_reason plus evidence_url itemise exactly which leads failed and on what evidence. On the measured 100-lead run that meant 4 dissolved rows, 2 in distress and 1 with no register match. That is the report a money-back guarantee asks for. Another 23 came back unverifiable, because no register entry matched them confidently. The run declines to guess, so a live company is never reported dead; give those leads a company number and run them again.
Clean a list before the first send. Suppress every row that is not ok, then read website_answers, email_domain_accepts_mail and phone_number_status on the survivors to drop any whose checks you would not send to. ok means the company is live on the register; the check fields are the deliverability read. 70 of the 100 measured leads verified active.
Re-verify an ageing list. Companies dissolve between the day a list is bought and the day it is worked; 4 of the 100 measured leads already had. Upload last quarter's extract again and status_as_of dates every verdict, so a stale row is caught before it is sent.
Triage a dialling list. phone_number_status drops numbers that do not parse. phone_line_type splits mobiles from fixed lines and VoIP, so the team dials the rows worth dialling first.
Use UK B2B Lead Verifier with AI agents
$claude mcp add --transport http apify "https://mcp.apify.com?tools=Pradio/b2b-leads"
Paste that line into your agent's setup and it can start runs and read verdict rows as tools.
Personal data
Attribution. The register evidence on every row is public sector information from Companies House. Contains public sector information licensed under the Open Government Licence v3.0, at https://www.nationalarchives.gov.uk/doc/open-government-licence/version/3/.
Transparency. Verdicts derive from the public Companies House register and from DNS and HTTP checks on the details each lead claims. The basis for processing is legitimate interests: checking that business data sold as accurate is accurate. A data subject may object and request deletion of their particulars. To do so, open an issue on the Actor's Issues tab asking for removal. Do not put the email address in the issue, which is public; name the company only, or nothing at all. The removal is then arranged privately from there.
What a row carries. A verdict row carries the verdict and the matched company particulars it needs: company name, number, status, registered office address, SIC codes, filing deadlines and a count of active officers. It does not echo officers' names, dates of birth or home addresses: the officers page is read for its count line only. PSC names are not read at all. Of the list you upload, or the dataset you pick, only the email, website, company, company number and phone values are read. A person's name or any other field never reaches a row.
How the register is read. Logged-out plain GETs, no credential and no bypass, one page per request, with maxItems capped at 100 rows a run. For a matched company the run also reads its public data page on data.companieshouse.gov.uk and its officers page, the same register. A refusal (403 or 429) is waited out once; a second one stops every register read for the rest of the run, and nothing retries past it. The remaining rows are still judged on their email checks, with normalised_status set to unchecked.
Controller. You choose which list to upload and why, so the controller for the personal data in it is you or your organisation. The register data is published for public use under the licence above.
Release notes
- 2026-09-25: stricter company matching. A lead is matched to a register entry only when the names agree. The register's name must be the lead's claim, legal form aside, or a former name the register lists. So "british aerospace" is BAE SYSTEMS PLC, not the dissolved Society of British Aerospace Companies. Two same-named entries or a name inside a longer one now read
unverifiable, neverdissolved. So does a lead whose domain points to another company, withreasonnaming the closest entry, and a lead on a UK public-sector domain,okincluded, withreasonsaying a public body is not a company on the register. A name followed by two or more extra words in the register's name (HM TREASURY UK SOVEREIGN SUKUK PLC for "hm treasury") scores under 80 and is not matched. A note in round or square brackets on your lead's name, such as "(2015-2022)" or "[old account]", is set aside when the name as written finds no match, so "abellio scotrail (2015-2022)" is ABELLIO SCOTRAIL LTD. A dissolved or in-distress verdict needs a match score of 90 or more. 0.2: an Apify dataset as input (datasetId, withdatasetFieldMapfor other field names), so a lead-finder run can be verified in the next step. Email flags for disposable domains, role mailboxes and free providers. SIC codes, overdue accounts and confirmation statements, and the active officer count for each matched company (includeRegisterDetail, on by default). Duplicate leads by company number or email are dropped before judging and counted inRUN_SUMMARY.0.1: first public build, a lead-list verifier on the Companies House register. Per-lead verdicts (ok, dissolved, in_distress, not_on_register, unverifiable) with the matched company's particulars and evidence link, MX, website and phone checks on the row, and a 100-lead cap per run.
Limits
- UK register only, and it only asks about UK-signalled leads. The register check is Companies House. A lead carrying a UK signal is searched. The signal is a company number, a +44 phone, a .uk domain or a UK legal form in the name. One that matches nothing reads
not_on_register, which means "no UK company matched", not "the company does not exist". A lead with no UK signal is never asked and readsunverifiablewithnormalised_statusnot_applicable. A non-UK row can still match a same-named UK company, since overseas groups do register UK subsidiaries and branches. When the row's own domain or phone contradicts the UK claim, no company is named. The row readsunverifiable, andreasonnames the same-named entry and says it may be a different company. A lead on a UK public-sector domain (gov.uk itself, .gov.uk, .nhs.uk, .ac.uk, .police.uk and similar) takes no company's verdict by name,okincluded. A government department, a council or a university is not a company on the register, so the row readsunverifiableandreasonsays so. - 100 leads a run.
maxItemsis capped at 100 and the run judges no more than that. A longer list is worked in runs of 100, each billed for its own judged rows. - The mailbox itself is never probed.
email_domain_accepts_mailis an MX check on the domain, so a full or dead inbox still reads true. - "Valid" stops at the number.
phone_number_statusandphone_line_typecome from parsing the number itself. Whether the line rings is not claimed, and no carrier lookup is made. - "Answers" is generous on purpose.
website_answersis true when anything answers HTTP, even a 403 or a parked page. False means nothing answered at all. - Free-mailbox emails never drive a match. A lead whose only company hint is a gmail address is never guessed at the register from the word "gmail".
- One refusal is backed off, not hammered. A 403 or 429 from the register pauses the run once. A second refusal marks the register down. The hit row is pushed free as
blocked. Later rows are judged on the other checks withnormalised_statusset tounchecked. - Reads are sequential. Register calls run one at a time with a pause between them, under the register's 120-a-minute guideline, so a large list takes a while. Register detail adds two reads per matched company, so a 100-lead run takes about three times as long with it on. That is the polite speed, not a fault.
- The disposable list is a dated copy.
email_disposablechecks the public list as bundled on 24 September 2026. A throwaway domain registered after that reads false until the copy is refreshed. - Errors run towards naming a miss, never towards a fake verdict. A check that fails says so on the row rather than guessing. A field the register does not publish is null rather than filled.
Troubleshooting
I uploaded 50 leads but only 47 rows came back. Where are the rest?
Two places to look. Leads repeating an earlier lead's company number or email are dropped before judging and never billed, so repeats collapse to one row each. And maxItems caps how many rows are pushed. RUN_SUMMARY splits it exactly: leads.inputDuplicatesDropped with the key under leads.inputDuplicatesByKey, then rowsFetched, rowsPushed and duplicatesDropped.
The dataset has a single row and it is not a verdict. Is the run broken?
No. That is a NOTHING_TO_JUDGE status row: the input gave it no leads. Check the columns are parallel arrays, or that leadListUrl answered and its headers were recognised.
A row says unverifiable with normalised_status not_applicable, but the company is real.
The row carried no UK signal: no company number, +44 phone, .uk domain or UK legal form in the name. The UK register was not asked. The checks that did run (email domain, website, phone) are on the row. If the company is UK, give it a .uk website, a +44 number or its Companies House name and it will be searched.
A row says unverifiable with normalised_status unchecked, but the company is real.
The register may not have been read for that row, usually because it refused an earlier request and the run stopped asking. Or the register answered with an entry whose status the run could not classify; company_status then carries the register's own word. The other checks still ran and are on the row. Re-run the list once the register is answering again. A third case: the register was searched and no single entry matched the claim closely enough, and reason names the closest entry and why. Give the lead its exact Companies House name or its company number and it will be matched.
The run ended with a STOPPED_EARLY row.
Your charge limit was reached. The status row carries rowsReturned and rowsRemaining; raise the limit and re-run to see the rest. Rows already returned stay billed, and nothing after them is.
Something looks wrong? Report a problem through the Issues tab on the Actor's Store page and it gets looked at.
FAQ
Can I use integrations with UK B2B Lead Verifier? Yes. The dataset exports as JSON, CSV, Excel and more, so a dead-row report drops straight into a spreadsheet or a CRM import. Apify integrations such as Zapier and Make can start a run when a new list lands.
Can I use UK B2B Lead Verifier with the Apify API? Yes. POST the same JSON the input form takes to the Actor's run endpoint; the curl in the how-to section is the whole call. Runs, datasets and schedules are all reachable through the standard API and client libraries.
Can I use UK B2B Lead Verifier through an MCP server? Yes. The snippet in the AI-agents section registers this Actor with a compatible agent, which can then start runs and read the verdict rows.
Is it legal to check a lead list this way? The register half of each verdict is Companies House's public company search, read with a plain logged-out GET. That is public sector information licensed under the Open Government Licence v3.0, with no login and no bypass. The input side is a list you already hold. What you may do with it sits between you and its seller.
Not affiliated
UK B2B Lead Verifier is an independent tool. It is not affiliated with, endorsed by or connected to Companies House, Apify or any lead-list provider. Company data comes from the public register under the Open Government Licence v3.0.