DEV Community

Olly
Olly

Posted on

Find local business leads for free with n8n, OpenStreetMap and Gemini

If you sell websites, booking systems or local SEO, your best prospects are the businesses down the road: the salon with no online booking, the plumber whose site still isn't on HTTPS, the café with no website at all. The usual way to find them is a paid Google Places scraper or an afternoon of copy-pasting from Maps.

There's a free alternative that most people overlook: OpenStreetMap. Its public Nominatim search returns named businesses with their address, phone, opening hours and website tags, and its usage policy allows light, polite use. This post walks through an n8n workflow that turns "hairdresser in Bath" into a Google Sheet of scored leads, each with a website check and a cold-email opener written by Google Gemini (free tier).

What you end up with

A form that asks for a business category, a town and how many results you want, and a Leads tab like this:

lead_id | found_at | search | name | category | address | postcode | city | phone | email | other_emails | website | facebook | instagram | linkedin | other_socials | opening_hours | has_website | site_status | site_issues | site_score | fit_score | priority | reasons | pitch_angle | opener | subject_line | osm_url | maps_url | status
Enter fullscreen mode Exit fullscreen mode

Every row has a 0-100 fit score, two or three factual reasons, and a one-to-two sentence opener that mentions something true about that business. Nothing is ever sent: you read the sheet and decide who to contact.

The architecture

n8n Form (category, town, max results)
  -> Set: what you sell, your ideal customer, a contact e-mail for the User-Agent
  -> HTTP: Nominatim, find the town -> Code: build a bounding box
  -> Wait 1 second (Nominatim's usage policy: max 1 request/second)
  -> HTTP: Nominatim, search the category inside that box (extratags + addressdetails)
  -> Code: keep real businesses, dedupe, read contact tags
  -> IF has a website -> HTTP: fetch the homepage -> Code: emails, phones, socials, quick site checks
  -> Basic LLM Chain + Gemini + Structured Output Parser: fit score, reasons, opener
  -> Google Sheets (append or update by lead_id) -> results page on the form
Enter fullscreen mode Exit fullscreen mode

Step 1: Find the town, then search inside it

Nominatim is two calls. First, look up the town and take its bounding box:

GET https://nominatim.openstreetmap.org/search?q=Bath, UK&format=jsonv2&limit=1
Enter fullscreen mode Exit fullscreen mode

Nominatim returns boundingbox as [south, north, west, east], but the viewbox parameter wants west,north,east,south, so a tiny Code node reorders it:

const b = $json.boundingbox;
if (!b) return [{ json: { found: false, message: `Couldn't find "${s.city}" on OpenStreetMap. Try adding the country, e.g. "Bath, UK".` } }];
return [{ json: { found: true, category: s.category, city: s.city,
  viewbox: `${b[2]},${b[1]},${b[3]},${b[0]}` } }];
Enter fullscreen mode Exit fullscreen mode

Then search the category inside that box with bounded=1, extratags=1 (that's where website, phone and opening_hours live) and addressdetails=1. If the town isn't found, the form shows a friendly "town not found" page instead of an error.

Two rules from the Nominatim usage policy are built in: a real User-Agent with your contact e-mail (set once in the settings node), and a 1-second Wait between calls.

Step 2: Throw away the noise

A category search also returns bus stops, car parks and buildings with a name. The clean-up Code node keeps a result only if it is a business-like OSM class (amenity, shop, craft, office, healthcare...) or has a website or phone tag, drops results whose address is outside the town you asked for (a town's box can reach into the next one), and dedupes on the OSM id and on name + postcode.

Step 3: Look at the website, cheaply

For businesses with a website, an HTTP node fetches the homepage (with "continue on error", because some sites block bots), and a Code node reads it without any AI:

  • e-mails from mailto: links and the visible text (filtering out image names and tracking domains)
  • tel: links
  • Facebook, Instagram, LinkedIn, TikTok profile links
  • quick quality checks: HTTPS, a mobile viewport, an online booking link, and how fresh the copyright year is

Those checks become site_issues and a site_score. If the site blocks automated requests, the workflow records that and tells the AI to say nothing about the site's quality, so it never claims a working website is broken.

Step 4: Let the AI judge fit, not invent facts

The prompt gets what you sell, your ideal customer and only the facts collected above. The rules are what make the output usable:

- fit_score 0-100: how likely this business needs AND can pay for what I sell.
  A missing website or website problems only raise the score when what I sell fixes them.
- reasons: 2-3 short, factual reasons based only on the facts above.
- opener: 1-2 sentences that mention one specific, true detail and link it to what I sell.
  Under 45 words, no flattery cliches, no exclamation marks, no invented names, reviews or numbers.
- subject_line: under 7 words, plain, no clickbait.
Enter fullscreen mode Exit fullscreen mode

A Structured Output Parser makes Gemini return JSON (fit_score, reasons, pitch_angle, opener, subject_line), and a Code node merges it with the business data and sets priority (high from a score of 70, which you can change).

Running it

  1. Import the workflow JSON (link below) into n8n 2.x.
  2. Add a free Google Gemini API key (Google AI Studio) and a Google Sheets credential.
  3. Create a Leads tab with the header row above and put the Sheet ID, what you sell and your contact e-mail in Set lead finder settings.
  4. Open the form URL and search "dentist" in your town.

Keep searches modest (the free version caps at 40 results per search) and you stay well inside OpenStreetMap's fair-use rules. OSM coverage varies by area, so treat it as a strong starting list, not a complete directory.

Free workflow and Pro version

The free workflow is in my GitHub repo of n8n templates: https://github.com/OlsOls4/n8n-ai-workflow-templates/tree/main/templates/local-business-lead-finder

If you prospect every week, there's a Pro version that splits big towns into area tiles, skips businesses already in your sheet, follows the contact page when the homepage has no e-mail, runs saved searches on a weekly schedule and e-mails you the hot leads: https://payhip.com/b/5jZJr (more templates: https://payhip.com/OllysAutomationTemplates).

Questions or a category that returns odd results? Leave a comment with your n8n version and I'll take a look.

Top comments (0)