How to Scrape Unlimited Google Maps Leads Using AI


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.
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:
Scrape the website content using Playwright
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:
Scrape the main website page to extract all relevant links and the page content.
Analyze the identified links using AI to determine the correct social media and contact links.
Extract email addresses from the page content using regex.
Analyze the extracted emails using AI to identify the valid email addresses.
If no email is found and there's a contact page link, scrape that page as well and extract any email addresses from it.
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
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
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:
Service | Cost 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
Python 3.8+ installed
Serper API key (Get one here)
OpenRouter API key (Get one here) or your preferred LLM API key
๐ก 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
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! ๐
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.