Google Maps is one of the richest sources of local business data for B2B lead generation. An n8n pipeline can automate the process: search for businesses in a target niche and location, extract contact information from their websites, validate emails, and push qualified leads to your CRM. This tutorial builds the complete pipeline using n8n's HTTP Request node with the Scavio search API. Each search query costs $0.005, making a 500-business extraction run cost $2.50.
Prerequisites
- n8n instance running (self-hosted or cloud)
- A Scavio API key from scavio.dev
- Basic familiarity with n8n workflow editor
- A CRM or spreadsheet to receive leads
Walkthrough
Step 1: Set up the search query node
Create an HTTP Request node in n8n that queries the Scavio API for Google Maps results. Use the maps platform parameter to get business listings with addresses and phone numbers.
// n8n HTTP Request Node configuration:
// Method: POST
// URL: https://api.scavio.dev/api/v1/search
// Headers:
// x-api-key: {{ $env.SCAVIO_API_KEY }}
// Content-Type: application/json
// Body (JSON):
{
"query": "plumbers in Austin TX",
"platform": "maps",
"country_code": "us"
}Step 2: Loop through multiple search queries
Use a SplitInBatches node to iterate through a list of niches and locations. Feed each combination into the search node.
// Generate query list in a Code node:
const niches = ['plumbers', 'electricians', 'HVAC contractors'];
const cities = ['Austin TX', 'Dallas TX', 'Houston TX'];
const queries = [];
for (const niche of niches) {
for (const city of cities) {
queries.push({ json: { query: `${niche} in ${city}` } });
}
}
return queries;
// Output: 9 query combinationsStep 3: Extract business data from results
Parse the Maps results to extract business name, address, phone, website, and rating. Use a Code node to normalize the data.
// n8n Code node to extract business data:
const results = $input.first().json;
const businesses = (results.local_results || []).map(b => ({
name: b.title || '',
address: b.address || '',
phone: b.phone || '',
website: b.website || '',
rating: b.rating || 0,
reviews: b.reviews || 0,
query: results.search_metadata?.query || ''
}));
return businesses
.filter(b => b.website)
.map(b => ({ json: b }));Step 4: Find email addresses from websites
For each business with a website, search for their contact page to find email addresses. Use a second search query targeting the domain.
// n8n HTTP Request node for email discovery:
// URL: https://api.scavio.dev/api/v1/search
// Body:
{
"query": "site:{{ $json.website }} contact email",
"country_code": "us"
}
// Then in a Code node, extract emails from snippets:
const data = $input.first().json;
const snippets = (data.organic_results || [])
.map(r => r.snippet || '')
.join(' ');
const emailRegex = /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/g;
const emails = [...new Set(snippets.match(emailRegex) || [])];
return [{ json: { ...($input.first().json), emails } }];Step 5: Push to CRM or Google Sheets
Send validated leads to your CRM. This example uses the Google Sheets node but works with HubSpot, Pipedrive, or any CRM with an n8n integration.
// n8n Google Sheets node configuration:
// Operation: Append Row
// Sheet: Lead Pipeline
// Columns mapping:
// A: {{ $json.name }}
// B: {{ $json.address }}
// C: {{ $json.phone }}
// D: {{ $json.website }}
// E: {{ $json.emails[0] || '' }}
// F: {{ $json.rating }}
// G: {{ $json.reviews }}
// H: {{ $json.query }}
// I: {{ new Date().toISOString() }}
// Cost summary for 9 queries (3 niches x 3 cities):
// Search queries: 9 x $0.005 = $0.045
// Email discovery: ~50 domains x $0.005 = $0.25
// Total: ~$0.30 for 50+ validated leadsPython Example
import os, re, requests, time
API_KEY = os.environ['SCAVIO_API_KEY']
ENDPOINT = 'https://api.scavio.dev/api/v1/search'
def search(body: dict) -> dict:
resp = requests.post(ENDPOINT,
headers={'x-api-key': API_KEY, 'Content-Type': 'application/json'},
json=body)
resp.raise_for_status()
return resp.json()
def extract_leads(niche: str, city: str) -> list:
data = search({'query': f'{niche} in {city}', 'platform': 'maps', 'country_code': 'us'})
leads = []
for b in data.get('local_results', []):
if not b.get('website'):
continue
# Find emails from website
email_data = search({'query': f'site:{b["website"]} contact email', 'country_code': 'us'})
snippets = ' '.join(r.get('snippet', '') for r in email_data.get('organic_results', []))
emails = list(set(re.findall(r'[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}', snippets)))
leads.append({'name': b.get('title'), 'phone': b.get('phone'),
'website': b['website'], 'emails': emails})
time.sleep(0.3)
return leads
leads = extract_leads('plumbers', 'Austin TX')
for lead in leads[:5]:
print(f'{lead["name"]}: {lead["emails"] or "no email found"}')JavaScript Example
const API_KEY = process.env.SCAVIO_API_KEY;
async function search(body) {
const resp = await fetch('https://api.scavio.dev/api/v1/search', {
method: 'POST',
headers: { 'x-api-key': API_KEY, 'Content-Type': 'application/json' },
body: JSON.stringify(body)
});
return resp.json();
}
async function extractLeads(niche, city) {
const data = await search({ query: `${niche} in ${city}`, platform: 'maps', country_code: 'us' });
const leads = [];
for (const b of (data.local_results || []).filter(b => b.website)) {
const emailData = await search({ query: `site:${b.website} contact email`, country_code: 'us' });
const text = (emailData.organic_results || []).map(r => r.snippet || '').join(' ');
const emails = [...new Set(text.match(/[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/g) || [])];
leads.push({ name: b.title, phone: b.phone, website: b.website, emails });
}
return leads;
}
extractLeads('plumbers', 'Austin TX').then(leads => {
leads.slice(0, 5).forEach(l => console.log(`${l.name}: ${l.emails.length ? l.emails[0] : 'no email'}`));
});Expected Output
Austin Plumbing Pros: info@austinplumbingpros.com
Capital City Plumbers: contact@capitalcityplumbers.com
Reliable Plumbing Austin: no email found
Lone Star Plumbing: service@lonestarplumbing.com
Premier Plumbing Co: no email found
Cost: 9 search queries + ~50 email lookups = ~$0.30 total