Tutorial

How to Build an n8n Google Maps Email Extraction Pipeline

Build an n8n workflow that extracts business emails from Google Maps search results. Automated lead generation with search API and email validation.

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.

JSON
// 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.

JavaScript
// 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 combinations

Step 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.

JavaScript
// 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.

JavaScript
// 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.

JavaScript
// 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 leads

Python Example

Python
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

JavaScript
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

JSON
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

Related Tutorials

Frequently Asked Questions

Most developers complete this tutorial in 15 to 30 minutes. You will need a Scavio API key (free tier works) and a working Python or JavaScript environment.

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. A Scavio API key gives you 250 free credits per month.

Yes. The free tier includes 250 credits per month, which is more than enough to complete this tutorial and prototype a working solution.

Scavio has a native LangChain package (langchain-scavio), an MCP server, and a plain REST API that works with any HTTP client. This tutorial uses the raw REST API, but you can adapt to your framework of choice.

Start Building

Build an n8n workflow that extracts business emails from Google Maps search results. Automated lead generation with search API and email validation.