Skip to content

Repository files navigation

Local Lead Machine (n8n + AnyAPI)

Data provided by AnyAPI. AnyAPI supplies the Google Maps, website, review, contact-enrichment, and Google Search data used throughout the workflow.

An n8n workflow that finds local businesses to sell to, and gives you a specific, provable reason to call each one.

Latest: agentic research + Excel

The current workflow is local-lead-machine-agent.workflow.json. It discovers 100+ local businesses, requires repeated review evidence for the problem you solve, and then sends only qualified businesses through a second agent for contact and company research.

One measured end-to-end n8n run screened 120 businesses, found 14 evidence-qualified prospects, and delivered 12 usable leads by email plus a professional Excel lead board. The run found 6 email addresses, 11 phone numbers, and 8 official contact pages for $0.42448 in AnyAPI data.

See LATEST.md for setup, Data Table schemas, the measured run, and API calls.

Which JSON should I import?

If you came from the Reddit post, import the current workflow: local-lead-machine-agent.workflow.json. The other two JSON files are earlier versions kept for reference:

Workflow Status What it does
local-lead-machine-agent.workflow.json Current 32-node research pipeline with evidence qualification, contact enrichment, email, and Excel delivery
local-lead-machine-pro.workflow.json Legacy 26-node website and review audit with an LLM-generated opener
local-lead-machine.workflow.json Legacy lite 13-node scheduled website and review audit without an LLM

Not a lead list. Anyone can pull a list off Google Maps. This opens every prospect's website and reads their worst reviews, then hands you a sentence like:

Every one of your eight worst reviews is unanswered, the most recent is from last month, and there is no analytics on your site.

You type in the kind of local business you want to reach. It costs about two thirds of a cent per qualified lead.

What it actually found

One real run, nail salons across eight cities in the Phoenix metro, 124 businesses for $0.85. Everything below is measured, not estimated. The raw dataset is in data/phoenix-nail-salons.json.

82% of businesses with 30+ reviews have a negative review the owner never answered 89/108
31% have never answered any of their eight worst reviews 33/108
27% list no website on their Google profile 33/124
...and 9% of those do have one, just not linked to their profile 3/33
72% of the sites we could read have no analytics installed 60/83
66% have no contact form 55/83
59% have no structured data markup 49/83
16% have no way to book online 13/83
65% scored 40 or higher, i.e. worth a call 81/124
Only 3% had nothing wrong at all 4/124

Cost breakdown for that run: maps.contacts $0.372, maps.reviews $0.351, web.scrape $0.090, google.search $0.033.

The step everyone gets wrong

Selling the list is easy. Qualifying it is where the value is, and it is where the naive version quietly lies to you. Three examples from building this, all caught before shipping:

1. A narrow regex manufactures defects. The first version looked for booking vendors (Calendly, Acuity, Booksy, Vagaro, Mindbody) and reported that 73% of businesses had no online booking. Broadening it to also match booking links and booking calls to action put the real figure at 9%. Almost every business had a booking path; it just was not one of five scripts. A number that wrong survives exactly until the first person opens two of the sites.

2. Source HTML is not the rendered page. We scrape rawHtml. Measured: the same URL at waitFor: 0 and waitFor: 8000 returns byte-identical HTML, both billed, neither cached. So anything a tag manager injects at runtime is invisible. Chat widgets and Meta pixels were dropped as signals entirely, because "missing" was unfalsifiable. Analytics, structured data and forms stayed, because those live in source.

3. "No website" needs verifying, and the obvious check fails. Google Maps only tells you whether a website is listed on the profile. Googling the business and taking the first plausible domain gets it wrong: on 9 test businesses, 4 surfaced a same-name competitor (phoenixautoshop.com is not "Phoenix Auto Repair & Body Shop"). Matching the phone number on the candidate page against the Maps listing is what makes it reliable. That check is a stage in the workflow.

Every claim in the shipped dataset was then re-checked by scripts/verify.mjs, which re-fetches the evidence and re-asserts each claim with broader patterns than the pipeline uses, so a bug in the rules cannot hide. 26 claims across 10 leads, 0 false.

How it works

Setup, once. A form takes your website URL, the local business type you want to reach, and the cities to cover. An LLM reads your own site to work out what you sell and which defects therefore signal need. You review it before it saves.

  1. Find them - one maps.contacts call per city returns up to 20 businesses with phone, website, scraped email, socials, rating and review count. Deduped on place id. $0.003 per business, billed per result.
  2. Split - businesses with no website listed go down their own path, because that is a different pitch.
  3. Read their site - web.scrape for the ones that have one, with a stealth retry when the first fetch hits a bot interstitial. HTML is reduced to booleans immediately and discarded. $0.0005 per site.
  4. Or check whether they really have none - google.search, then scrape the best non-directory result, then confirm by phone number.
  5. Read their worst reviews - maps.reviews with sort: lowestRanking. ownerResponse is an empty string when the owner never replied, so "they ignored this one" is exact rather than inferred. $0.0004 per review.
  6. Score - plain weights in a Code node. Every point traces to a link a human can click.
  7. Deliver - a Google Sheet with all of it, and an email with the top 10 and one sentence per lead.

Setup

  1. Import local-lead-machine-pro.workflow.json into n8n (Workflows > Import from File). local-lead-machine.workflow.json is a 13-node lite version using only the two most reliable signals and no LLM.
  2. Install the AnyAPI community node: Settings > Community Nodes > install n8n-nodes-anyapi. Create an AnyAPI API credential with a key from getanyapi.com and select it on every AnyAPI node. That one key covers the Maps lookups, the site scrapes, the Google search and the reviews.
  3. OpenRouter (pro only) - add a credential on the model node. The profiling and opener steps run fine on a fast model.
  4. Gmail + Google Sheets - connect your Google credentials, set the recipient and the target spreadsheet.
  5. Run the onboarding form once, then activate the schedule.

Swap in your own signals

The site rules are four regexes in one Code node. Change them.

If what you sell does not map to a website defect - staffing, linen, commercial cleaning, POS, food distribution - use complaint mining instead. maps.reviews accepts a reviewsFilterString, so you can search a business's own reviews for the problem you fix:

You sell Search their reviews for
Staffing slow, wait, short staffed
Linen / uniforms dirty, stained, sticky
POS / payments cash only, card machine, system down
Commercial cleaning dirty bathroom, smell
HVAC service too hot, freezing, AC

Measured: a filtered search that finds nothing costs $0.00005, because you are billed per result returned. You can run six phrases per business and only pay on a hit.

Honest caveats

  • This finds you a reason to call. It does not find you a customer.

  • Yield varies enormously by vertical. Nail salons and auto repair have three to four times the no-website rate of med spas and plumbers, who are marketing-sophisticated and mostly have competent sites. Do not assume the numbers above transfer. Measured across four Phoenix verticals:

    vertical no website site blocked scraper no analytics
    nail salon 27-33% 5-9% 72%
    auto repair 25% 44% 40%
    med spa 7% 21% 9%
    plumber 8% 9% 20%
  • Some sites block scrapers. 9% here, 44% for auto repair. Those get one stealth retry and are then skipped rather than guessed at.

  • The review count is a floor, not a total. We only fetch the 8 lowest-rated reviews, so "every one of their 8 worst is unanswered" means exactly that, not that they have only 8.

  • Businesses under 30 reviews are skipped for the review check. Measured: they have no negatives to find, so the call is pure waste.

  • A 124-business run takes about 15 minutes. It is a weekly job, not an interactive one.

  • web.scrape occasionally 502s. Retry is enabled on the nodes. At the time of writing it also rejects the html, links and explicit markdown formats, so the workflow asks for rawHtml.

Cost

Pay per request in USD, no subscription. The Phoenix run: $0.85 for 124 qualified leads, about $0.0068 each. Model costs depend on your OpenRouter choice.

Repo layout

Path What it is
local-lead-machine-pro.workflow.json The full workflow, 26 nodes
local-lead-machine.workflow.json Lite version, 13 nodes, no LLM
scripts/signals.mjs The signal rules. Shared verbatim with the workflow's Code nodes so they cannot drift
scripts/run.mjs Reference pipeline, mirrors the workflow band for band
scripts/verify.mjs Independent re-check of every claim in a dataset
scripts/recheck-sites.mjs Re-measures site signals after a rule change
data/phoenix-nail-salons.json The 124-business run, raw

License

MIT

About

An n8n workflow that finds local businesses to sell to, with a specific provable reason to call each one. Powered by AnyAPI: one key, USD pay-per-request.

Resources

Stars

27 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages