Tutorial

How to Build a Google Maps Cold Email Pipeline

Build a lead generation pipeline using Google Maps data from SERP results. Find local businesses, extract contacts, and personalize cold emails.

Google Maps listings contain rich business data: name, address, phone, website, hours, ratings, and reviews. Instead of scraping Google Maps directly (which violates ToS and breaks constantly), this tutorial extracts Maps data from SERP results. Search for a business category and location, and the SERP returns local pack results with all the data you need for cold email outreach. Each search costs $0.005.

Prerequisites

  • Python 3.9+ installed
  • requests library installed
  • A Scavio API key from scavio.dev

Walkthrough

Step 1: Search for local businesses via SERP

Search for a business category and location. SERP results include both organic listings and local pack data with business details.

Python
import requests, os, re

SCAVIO_KEY = os.environ['SCAVIO_API_KEY']

def find_local_businesses(category: str, city: str, count: int = 10) -> list:
    query = f'{category} near {city}'
    resp = requests.post('https://api.scavio.dev/api/v1/search',
        headers={'x-api-key': SCAVIO_KEY, 'Content-Type': 'application/json'},
        json={'query': query, 'country_code': 'us', 'num_results': count})
    resp.raise_for_status()
    data = resp.json()
    businesses = []
    # Extract from organic results
    for r in data.get('organic_results', []):
        domain = r['link'].split('/')[2] if '/' in r['link'] else ''
        # Skip aggregator sites
        if any(a in domain for a in ['yelp.com', 'yellowpages.com', 'angi.com', 'thumbtack.com']):
            continue
        phones = re.findall(r'\(?\d{3}\)?[-.\s]?\d{3}[-.\s]?\d{4}', r.get('snippet', ''))
        businesses.append({
            'name': r['title'].split(' - ')[0].split(' | ')[0].strip(),
            'website': r['link'],
            'domain': domain,
            'phone': phones[0] if phones else '',
            'snippet': r.get('snippet', ''),
            'category': category,
            'city': city
        })
    return businesses

leads = find_local_businesses('dentist', 'Austin TX')
print(f'Found {len(leads)} businesses')
for l in leads[:5]:
    print(f'  {l["name"]}: {l["domain"]} {l["phone"]}')

Step 2: Enrich leads with website analysis

Search for each business to find additional signals: whether they have a blog, social media presence, or outdated website design.

Python
import time

def enrich_lead(lead: dict) -> dict:
    domain = lead['domain']
    if not domain:
        return lead
    # Search for the business to find additional info
    resp = requests.post('https://api.scavio.dev/api/v1/search',
        headers={'x-api-key': SCAVIO_KEY, 'Content-Type': 'application/json'},
        json={'query': f'site:{domain}', 'country_code': 'us', 'num_results': 5})
    pages = resp.json().get('organic_results', [])
    lead['page_count'] = len(pages)
    lead['has_blog'] = any('blog' in p.get('link', '').lower() for p in pages)
    lead['has_booking'] = any(w in ' '.join(p.get('title', '') for p in pages).lower()
                              for w in ['book', 'schedule', 'appointment'])
    # Estimate website quality from page count
    if lead['page_count'] <= 2:
        lead['website_quality'] = 'minimal'
    elif lead['page_count'] <= 5:
        lead['website_quality'] = 'basic'
    else:
        lead['website_quality'] = 'established'
    return lead

# Enrich top leads
for i, lead in enumerate(leads[:3]):
    leads[i] = enrich_lead(lead)
    print(f'  {lead["name"]}: {lead["page_count"]} pages, '
          f'blog={lead.get("has_blog", False)}, '
          f'quality={lead.get("website_quality", "unknown")}')
    time.sleep(0.3)

Step 3: Generate personalized cold email templates

Use the enrichment data to create personalized email templates for each lead. Reference their specific website situation.

Python
def generate_email(lead: dict) -> str:
    name = lead['name']
    quality = lead.get('website_quality', 'unknown')
    has_blog = lead.get('has_blog', False)
    has_booking = lead.get('has_booking', False)
    # Personalized opening based on website analysis
    if quality == 'minimal':
        opening = (f'I noticed {name} has a fairly simple website. '
                   'Many of your competitors in the area have sites with '
                   'online booking and patient reviews that help them stand out.')
    elif not has_booking:
        opening = (f'I visited the {name} website and noticed you do not have '
                   'an online booking system. Adding one typically increases '
                   'new patient inquiries by 20-30%.')
    elif not has_blog:
        opening = (f'{name} has a solid website, but I noticed there is no blog. '
                   'Local dental practices with blogs rank for 3-5x more '
                   'keywords than those without.')
    else:
        opening = (f'I have been following {name} online and your web presence '
                   'is strong. I think there is an opportunity to improve '
                   'your local search visibility even further.')
    email = f"""Subject: Quick question about {name}'s website

Hi,

{opening}

I help {lead['category']}s in {lead['city']} get more patients through 
their website. Would a 10-minute call this week make sense?

Best regards"""
    return email

for lead in leads[:2]:
    if lead.get('website_quality'):
        email = generate_email(lead)
        print(email)
        print('---')

Python Example

Python
import requests, os, re, time

SCAVIO_KEY = os.environ['SCAVIO_API_KEY']

def find_leads(category, city):
    resp = requests.post('https://api.scavio.dev/api/v1/search',
        headers={'x-api-key': SCAVIO_KEY, 'Content-Type': 'application/json'},
        json={'query': f'{category} near {city}', 'country_code': 'us', 'num_results': 10})
    leads = []
    for r in resp.json().get('organic_results', []):
        domain = r['link'].split('/')[2] if '/' in r['link'] else ''
        if not any(a in domain for a in ['yelp.com', 'yellowpages.com']):
            phones = re.findall(r'\(?\d{3}\)?[-.\s]?\d{3}[-.\s]?\d{4}', r.get('snippet', ''))
            leads.append({'name': r['title'].split(' - ')[0], 'domain': domain,
                          'phone': phones[0] if phones else ''})
    return leads

for l in find_leads('dentist', 'Austin TX')[:5]:
    print(f'{l["name"]}: {l["domain"]} {l["phone"]}')

JavaScript Example

JavaScript
const SCAVIO_KEY = process.env.SCAVIO_API_KEY;

async function findLeads(category, city) {
  const resp = await fetch('https://api.scavio.dev/api/v1/search', {
    method: 'POST',
    headers: { 'x-api-key': SCAVIO_KEY, 'Content-Type': 'application/json' },
    body: JSON.stringify({ query: `${category} near ${city}`, country_code: 'us', num_results: 10 })
  });
  return (await resp.json()).organic_results?.filter(r => {
    const domain = new URL(r.link).hostname;
    return !['yelp.com', 'yellowpages.com'].some(a => domain.includes(a));
  }).map(r => ({
    name: r.title.split(' - ')[0],
    domain: new URL(r.link).hostname,
    phone: (r.snippet || '').match(/\(?\d{3}\)?[-.\s]?\d{3}[-.\s]?\d{4}/)?.[0] || ''
  })) || [];
}

findLeads('dentist', 'Austin TX').then(l => l.forEach(x => console.log(`${x.name}: ${x.phone}`)));

Expected Output

JSON
Found 7 businesses
  Austin Dental Care: austindentalcare.com (512) 555-0123
  Smile Solutions: smilesolutionsatx.com (512) 555-0456
  Capital City Dental: capitalcitydental.com

  Austin Dental Care: 3 pages, blog=False, quality=basic
  Smile Solutions: 8 pages, blog=True, quality=established

Subject: Quick question about Austin Dental Care's website

Hi,

I visited the Austin Dental Care website and noticed you do not have
an online booking system. Adding one typically increases
new patient inquiries by 20-30%.

I help dentists in Austin TX get more patients through their website.
Would a 10-minute call this week make sense?

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.

Python 3.9+ installed. requests library installed. A Scavio API key from scavio.dev. 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 a lead generation pipeline using Google Maps data from SERP results. Find local businesses, extract contacts, and personalize cold emails.