You built the list. Four hundred rows, all in the right county, all in the right trade. Now you open it and realise you have no idea which twenty are worth an hour of your time.
That is what this build is for. It takes each row, finds the business online, reads what is there, and puts a number and a sentence next to it so you can sort the sheet and start at the top.
The step where most guides start charging you
Companies House gives you a name and a registered address. It does not give you a website. So the first job is turning “PENNINGTON HEATING SERVICES LIMITED” into a URL, and this is the point where most tutorials tell you to sign up for a search API and start paying per query.
There is a cheaper way and it works more often than you would think.
Take the company name. Drop LIMITED, LTD, LLP, and anything in brackets. Strip apostrophes and full stops. Join what is left with hyphens and lower-case it. You get pennington-heating-services. Now try two candidates:
https://pennington-heating-services.co.uk
https://penningtonheatingservices.co.ukThen the same two on .com. Fetch each one and keep the first that answers with a 200 and has a page title sharing a distinctive word with the company name. That last check matters, because parked domains and squatters answer perfectly happily and will otherwise fill your sheet with rubbish.
On a UK list of limited companies this finds the site about half the time. Some businesses trade under a name nothing like their registered one, some have no site at all, and some are on a Facebook page. Half a list enriched for free beats a whole list you paid for, and the rows that fail are still there for you to look up by hand.
The build, nine nodes
1. Schedule Trigger or Manual Trigger. Manual while you are working on it.
2. Google Sheets, Get Rows. Read the list. Filter to rows where the website column is empty, so a rerun only works on what is new.
3. Loop Over Items. Batch size 1. This is what keeps you from firing four hundred requests at four hundred websites in the same second, which is both rude and a good way to get blocked.
4. Code node, called Candidates. Builds the list of URLs to try from the company name. Twelve lines, no dependencies:
const name = $json.company
.replace(/\b(LIMITED|LTD|LLP|PLC)\b/gi, '')
.replace(/\(.*?\)/g, '')
.replace(/[^\w\s-]/g, '')
.trim()
.toLowerCase();
const hyphen = name.split(/\s+/).join('-');
const solid = name.split(/\s+/).join('');
return [{ json: {
...$json,
candidates: [
`https://${hyphen}.co.uk`,
`https://${solid}.co.uk`,
`https://${hyphen}.com`,
`https://${solid}.com`,
],
words: name.split(/\s+/).filter((w) => w.length > 4),
}}];The words array is the check. Anything over four letters is distinctive enough to match against a page title, whereas “heating” on its own would match half the internet.
5. HTTP Request, called Try Candidates. Point it at the candidate list. Two settings that matter more than anything else in this node:
Turn on “Continue on Fail” under Settings, because three of your four candidates are supposed to fail and you do not want the run stopping every time. Set a timeout of about eight seconds, because a dead domain that hangs will otherwise hold up the whole batch.
Under Options, set a real User-Agent. Something like LoadOff prospect check (max@yourdomain.co.uk). A request with a browser’s user agent that clearly is not a browser is the thing that gets you blocked, and a contact address means anybody annoyed by you can say so rather than reaching for a firewall rule.
6. Code node, called Pick Winner. Keeps the first response that both loaded and has a title containing one of your distinctive words. If none do, it returns the row with the website left empty, which is a result rather than a failure.
7. HTML node, called Extract. Point it at the winning page. Pull the <title>, the meta description, any mailto: links and anything shaped like a UK phone number. The HTML node does CSS selectors, so a[href^="mailto:"] gets the emails in one line.
8. Information Extractor. This is where the model goes. Give it the page text and ask for a small, fixed set of fields:
score 0 to 10
reason one sentence, quoting something from the page
size_signal sole trader | small team | established firm | unclear
services up to five, as they describe themWrite the scoring rule in plain English in the prompt, in your own words, and be specific about what a 9 looks like and what a 2 looks like. “Score 9 if they list commercial work and have more than one engineer named. Score 2 if the site is a single page with a mobile number and nothing else.” Vague instructions get you a column of sevens.
Ask for the reason and read a few. It is the fastest way to find out the model is scoring on something daft, and it takes about a minute.
9. Google Sheets, Update Row. Write the website, the email, the phone, the score, the reason and today’s date back against the same row. Matching column: the company number, which is unique and never changes. Matching on the company name will eventually bite you.
What the score is for
Sorting. That is the whole job.
A model reading a homepage is guessing from thin evidence, and it will hand you a confident 8 for a business that closed last year and a 4 for your best future client because their site is from 2011. It has no idea which of those two builds the sort of work you want.
So use it to decide what order to work through the list in, and let a person overrule it whenever they feel like it. Do not use it to delete rows. Do not show it to the prospect. And if you find yourself defending a score in a meeting, the score has been promoted past its competence.
The reason column is the guard. A number alone is unarguable, which is exactly what makes it dangerous. A number with a sentence next to it can be looked at and disagreed with, which is what you want from something you did not write yourself.
Manners, and the law
Read robots.txt before you fetch anything. If a site says stay out, stay out: it costs you one row and it is the difference between a polite integration and a nuisance.
Space the requests. The Loop Over Items node with a batch of one already does most of this, and adding a short Wait node inside the loop makes it certain. There is no prize for finishing in nine seconds.
On the legal side: fetching public pages a business chose to publish is ordinarily fine. The moment you extract a named person’s email address, you are holding personal data under UK GDPR, which means you need a lawful basis, a note of where it came from, and the ability to delete it when somebody asks. A source and a collected_on column cost nothing and answer both questions.
And remember what the list build guide said about who you may email. Limited companies and LLPs are corporate subscribers under PECR and can be emailed with an opt-out. Sole traders and most partnerships need consent first, and they are exactly the sort of business whose website you will have found with this workflow.
Where it goes wrong
The failure you will actually hit is the parked domain. Someone registered yourprospect.co.uk years ago, it serves a holding page, the fetch succeeds, and the model dutifully scores an advert for domain hosting. The title-word check catches most of these. Reading the first twenty rows by hand catches the rest, and you only have to do that once.
The other one is subtler. When the workflow finds no website, it is tempting to treat that as a bad sign. It is not. Plenty of very good small firms have no website and all their work by word of mouth, and they are frequently the ones who most need what you are selling. Leave those rows in and mark them as unenriched, not as scored zero.
Published 28 August 2026. Written by the people who run this sort of thing for clients, on n8n, including our own lead pipeline.
Next in finding new customers: The hiring-signal watcher. Or go back to all 4 in this category.