How to Scrape Unlimited Google Maps Leads Using AI

Aymen KAymen K
10 min read

Finding quality leads for your business is often expensive and time-consuming. Commercial lead generation tools can cost hundreds of dollars monthly, and manual research takes forever.

In this guide, I'll walk you through building an AI-powered lead generation tool that scrapes business data from Google Maps at a fraction of the cost of commercial solutions. You'll learn how to extract business information, enhance it with AI scraping, and compile everything into ready-to-use lead lists โ€” all for about $0.2 per 1000 leads! ๐Ÿ’ฐ

๐Ÿ’ก See it in action!

By the end, you'll have a powerful tool to generate leads for any business niche in any location. Whether you're running a marketing agency, doing sales outreach, or conducting market research, this tool will save you both time and money.

Let's dive in! ๐Ÿ”ฅ

๐Ÿ’ก Access the project in my Github repository now!

๐Ÿค” Why Build Another Lead Generation Tool?

When I decided to start selling AI solutions for businesses, I needed up-to-date contact information and business details to conduct a successful outreach campaign. During my first look, I immediately noticed two clear options:

  • Having a subscription to a database provider like Apollo or Zoominfo, but for me this was too expensive as most of them charge $50-300/month

  • The obvious second option is Manual research which is completely free but for a good outreach at scale, it's incredibly time-consuming

I made some research and found some good scrapers on Apify like Apify's Google Maps scrapers or even Apollo Lead scraper which offer a pay-per-use more affordable pricing model costing from $2-10 per 1000 results.

This seemed good enough for me, but when I looked at the Apify Google Maps scraper, I noticed they were using a simple logic in the background - they get business details from the Google Maps API and then crawl each business details page to get more contact information. As I had experience with the Serper API, I thought why not try to build my own local business scraper tool.

That's why I created this tool โ€” to give you:

๐Ÿ”น Comprehensive data extraction - Not just names and addresses, but emails and social profiles too

๐Ÿ”น Cost-efficiency - About $0.2 per 1000 leads (50x cheaper than alternatives!)

๐Ÿ”น Full customization - Search any business type in any location

๐Ÿ”น AI-powered enhancement - Uses LLMs to intelligently extract contact information or other business details

๐Ÿ”น Ready-to-use format - Export directly to Excel for immediate use in your campaigns

๐Ÿ› ๏ธ How The Tool Works

Our lead generator follows a three-step process that combines traditional API calls with AI-powered data enrichment:

1๏ธโƒฃ Initial Data Collection

First, we use the Serper Maps API to collect basic business information from Google Maps.

Serper Maps API

This is handled by our search_places function, which formats and sends the API request:

def search_places(query, coords, num_pages=1):
    payload = []
    lat, lon = coords['lat'], coords['lon']

    # Create payload for each page
    for page in range(1, num_pages + 1):
        payload.append({
            "q": query,
            "ll": f"@{lat},{lon},13z", # Format the location string for Serper API
            "page": page
        })

    headers = {
        'X-API-KEY': os.getenv("SERPER_API_KEY"),
        'Content-Type': 'application/json'
    }

    # Send request and return results
    response = requests.post(
        "https://google.serper.dev/maps", 
        headers=headers, 
        data=json.dumps(payload)
    )

Also as you can notice, the Serper API requires specific longitude and latitude of the place we want to search for, so we can't directly provide a city name like "New York" for example.

To get around this, we use the free Nominatim API which returns the coordinates of a place/city from its name. This is done using the get_coordinates function:

def get_coordinates(city):
    """
    Convert a city name to latitude and longitude coordinates.

    Args:
        city (str): Name of the city to geocode

    Returns:
        tuple: (latitude, longitude) if successful, (None, None) if not
    """
    try:
        response = requests.get(
            "https://nominatim.openstreetmap.org/search", 
            params={"q": city, "format": "json"}, 
            headers={"User-Agent": USER_AGENTS[2]}
        )
        data = response.json()
        if data:
            return {"lat": data[0]['lat'], "lon": data[0]['lon']}
        else:
            return None
    except Exception as e:
        print(f"Error getting coordinates: {e}")
        return None

Up to this point We get the basic business data - name, address, phone number, website URL, and reviews. But we need more information for effective outreach...

2๏ธโƒฃ AI-Powered Data Enrichment

The real magic happens in the data enrichment phase. For each business with a website, we:

  1. Scrape the website content using Playwright

  2. Use AI (LLMs) to extract and analyze key contact information mainly email and social media links

Here's how the business information processing works:

async def process_businesses(excel_file):
    # Load the Excel file into a DataFrame
    df, file_path = load_excel_data(excel_file)

    # Process each business
    for index, row in tqdm(df.iterrows(), desc="Processing businesses", unit="business"):
        name = row.get("name", "")
        url = row.get("website", "")
        location = row.get("address", "")

        if not url:
            continue

        # Get business info
        info = await get_business_info(url, name, location)

        # Update the business information
        update_business_data(df, index, info)

For each business, we extract detailed information with our AI analysis function:

async def get_business_info(business_url, business_name, business_location):
    # Scrape the main website
    content, links = await scrape_website(business_url, extract_links=True)
    if not content:
        return {}

    social_links = find_relevant_links(links)
    emails = extract_emails_from_content(content)

    # Analyze the identified links using AI
    links_result = await analyze_business_links(
        social_links, business_name, business_location, business_url
    )

    if emails:
        emails_result = await analyze_business_emails(
            emails, business_name, business_location, business_url
        )

    # If no email found but we have a contact link, try scraping that too
    if (not emails) and social_links.get('contact'):
        contact_url = social_links['contact'][0]
        if contact_url != business_url:
            contact_content, _ = await scrape_website(contact_url, extract_links=False)
            emails = extract_emails_from_content(contact_content)
            if emails:
                # Re-analyze with new emails
                emails_result = await analyze_business_emails(
                    emails, business_name, business_location, business_url
                )

    return {
        'facebook': links_result.get('facebook', ''),
        'twitter': links_result.get('twitter', ''),
        'instagram': links_result.get('instagram', ''),
        'contact': links_result.get('contact', ''),
        'email': " || ".join(emails_result.get('emails', '')),
    }

๐Ÿš€ What this function does:

  1. Scrape the main website page to extract all relevant links and the page content.

  2. Analyze the identified links using AI to determine the correct social media and contact links.

  3. Extract email addresses from the page content using regex.

  4. Analyze the extracted emails using AI to identify the valid email addresses.

  5. If no email is found and there's a contact page link, scrape that page as well and extract any email addresses from it.

  6. Return a dictionary containing the social media links, contact links, and the email addresses found.

๐Ÿ’ก Pro Tip

Instead of providing the full scraped page content to the AI, we first use regex to extract all the page links and email addresses. Then we instruct an AI agent to identify which are the correct social media and contact links, and which are the valid email addresses.

This approach helps save money on LLM calls and provides more accurate results by reducing the chance of hallucinations.

For example, for the email extraction we use the following regex pattern to identify all email addresses within the page content:

EMAIL_PATTERN = re.compile(r"[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}")
def extract_emails_from_content(content: str):
    """
    Extracts email addresses from content.
    """
    emails = set(email.lower() for email in EMAIL_PATTERN.findall(content))
    return list(emails)

Then we use AI analysis to intelligently find the correct email addresses that match the business:

async def analyze_business_emails(
    emails: List[str], 
    business_name: str, 
    business_location: str, 
    business_url: str
):
    system_prompt = f"""
Identify all relevant business contact emails. Prioritize general contact addresses (such as info@ or contact@) and emails of key personnel that use the business's domain. Exclude department-specific ones (e.g., press, events) unless no main contact is available.

If no domain-based business emails are found, provide any available emails, including personal or free-domain addresses (e.g., Gmail, Yahoo) as fallback contacts.

## Business Information
- Business Name: {business_name}
- Business Location: {business_location}
- Business Website URL: {business_url}

**If only a single valid email is found, just return it.**
"""

    # Create user message with all the context
    user_message = f"Potential emails: {list(emails)}"

    # Invoke LLM to get structured response
    response = await ainvoke_llm(
        model=os.getenv("LLM_MODEL", "gpt-4.1-mini"),
        system_prompt=system_prompt,
        user_message=user_message,
        response_format=EmailsResponse,
        temperature=0.1
    )

    return response

3๏ธโƒฃ Data Export & Organization

Finally, all the enhanced business data is saved back to the Excel file in a clean, organized format ready for your outreach campaigns.

The whole flow is orchestrated by the main function:

async def main(location, search_query, num_pages):
    print(f"\n๐Ÿ” Starting lead generation for '{search_query}' in '{location}'")

    # Step 1: Get coordinates from location
    coords = get_coordinates(location)
    if not coords:
        print("Could not get coordinates for the location. Exiting.")
        return

    # Step 2: Search for places using Serper Maps API
    places_data = search_places(search_query, coords, num_pages)
    if not places_data:
        print("No places found. Exiting.")
        return

    # Step 3: Save places data to Excel
    excel_filename = f"data_{search_query}_{location}_{get_current_date()}.xlsx"
    save_places_to_excel(places_data, excel_filename)

    # Step 4: Process businesses to get detailed information and update Excel file
    await process_businesses(excel_filename)

๐Ÿ“Œ Serper Maps API Pagination

When using Serper Maps API, each page of results contains 20 places, so to extract more than 20 places, we need to use the num_pages parameter to specify the number of pages to fetch. For example, if we want to extract 100 places, we can set num_pages=5.

๐Ÿš€ Features That Set This Tool Apart

  1. Multi-LLM Support - Using OpenRouter for LLM calls gives you the freedom to use any AI model you prefer (OpenAI, Claude, DeepSeek) through a simple interface

  2. Full Control and Customization - By default, the tool scrapes only the landing and contact page for each business, but you can change this behavior to crawl the entire website if you want and even extract other specific business details like products or services

๐Ÿ’ฐ Cost Breakdown

This tool is incredibly cost-efficient:

ServiceCost Per 1000 Leads
Serper Maps API~$0.15 (with free credits!)
LLM API (OpenAI/Claude mini)~$0.05
Total Cost~$0.20

Compare that to Apify's Google Maps scrapers at $5-10 per 1000 results, and you're saving up to 50x on costs!

๐Ÿ› ๏ธ Setting Up Your Own Lead Generator

Prerequisites

๐Ÿ’ก Serper API

When you register for a Serper API key, you get 2500 free credits. This will allow to test without any cost.

Step 1: Clone the Repository

git clone https://github.com/kaymen99/google-maps-lead-generator
cd google-maps-lead-generator
pip install -r requirements.txt

Step 2: Set Up Your Environment Variables

Create a .env file in the project root with your API keys:

SERPER_API_KEY="your_serper_api_key"
OPENROUTER_API_KEY="your_openrouter_api_key"

๐Ÿ“Œ AI Model Provider

You are not forced to use OpenRouter, you can use any LLM provider you want, just change the invoke_llm function in src/utils.py to use your preferred LLM provider.

Step 3: Customize and Run

Edit the parameters in main.py to specify your target location, search query, and number of pages:

if __name__ == "__main__":
    location = "Toronto"  # Change to your target location
    search_query = "Realtors"  # Change to your target business type
    num_pages = 1  # Each page contains 20 results, increase for more leads

    # Run main function
    asyncio.run(main(location, search_query, num_pages))

Then run the script:

python main.py

Step 4: Streamlit UI

To make the tool more accessible for non-technical users, I've created a simple Streamlit interface. This provides a clean, user-friendly way to generate leads without needing to touch any code.

To use the Streamlit app, just run:

streamlit run app.py

This will open a browser window with an interface that includes:

  • A sidebar where you can set your Serper API key and OpenRouter API key

  • Options to select which LLM model you want to use for data enrichment

  • The main panel where you can input your location, business type, and select the number of places to scrape

Google Maps Scraper UI

After configuring your settings and clicking "Start Lead Generation", the tool will collect and enrich the data. When complete, you'll see a preview of the results and have the option to download the Excel file.

๐ŸŒ Real-World Use Cases

This tool is perfect for:

๐Ÿ”น Sales Teams - Generate qualified lead lists for targeted outreach

๐Ÿ”น Real Estate Agents - Find property managers, contractors, or competing agents

๐Ÿ”น Marketing Agencies - Build prospect lists for client acquisition

๐Ÿ”น Recruiters - Identify potential companies to place candidates

๐Ÿ”น Market Researchers - Analyze business density and distribution in specific areas

๐ŸŽฏ Final Thoughts

Building your own lead generation tool gives you complete control over the process and drastically reduces costs. The combination of Serper Maps API and AI-powered enrichment provides a powerful, flexible solution that can adapt to your specific needs.

I'd love to hear how you use this tool and what enhancements you'd like to see! Feel free to contribute to the project on GitHub or reach out with questions.

๐Ÿ’ก Want to learn more? Follow my blog and check out my Github for more AI project & tutorials!

๐Ÿ“– Interested in AI scraping? Check out my other tutorial: Scrape Any Website Fast and Cheap with Crawl4AI to learn how to build your own custom scrapers in minutes!

Happy lead hunting! ๐Ÿš€

0
Subscribe to my newsletter

Read articles from Aymen K directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Aymen K
Aymen K

AI developer driven by a passion for creating intelligent solutions through AI automation and AI agents. Fascinated by the potential of AI to transform industries and solve complex problems. Constantly exploring new technologies and frameworks to build smarter, more efficient systems that push the boundaries of what AI can achieve.