Complete 2026 guide to scraping business names, phone numbers, addresses, and ratings from Google Maps — with working Python code and proxy configurations that actually ship.
Daniel K.
Apr 15, 2026
Google Maps is the largest structured database of local businesses on the planet — more than 200 million listings covering every restaurant, shop, service provider, and professional on Earth. For sales teams, market researchers, and agencies, scraping Google Maps is one of the highest-ROI lead generation activities possible.
The problem: Google's anti-bot systems are among the most advanced on the web. A naive scraper gets blocked in under 100 requests. This guide walks through exactly how to scrape Google Maps reliably in 2026 — what data is actually available, the right proxy setup, working Python code, and the tactics that keep scrapers running at scale.
Google Maps exposes a surprising amount of structured data on public listings. Here's what's available without authentication:
| Field | Availability | Reliability |
|---|---|---|
| Business name | Every listing | Very high |
| Address | Every physical listing | Very high |
| Phone number | ~85% of businesses | High |
| Website URL | ~60% of businesses | High |
| Category | Every listing | Very high |
| Hours of operation | ~75% of businesses | Medium |
| Average rating | Listings with reviews | Very high |
| Review count | Listings with reviews | Very high |
| Individual reviews | Paginated, up to a cap | Medium |
| Price level ($ to $$$$) | Restaurants, services | Medium |
| Plus Code / coordinates | Every physical listing | Very high |
| Photos | Most listings | High |
| Popular times | Some businesses | Low |
For B2B lead generation — the most common use case — the four fields that matter most are business name, address, phone number, and website. These are the minimum required to enrich a contact list or feed a CRM.
Google offers the Places API as the official way to access this data. Before reaching for a scraper, understand the tradeoffs:
| Factor | Google Places API | Direct Scraping |
|---|---|---|
| Cost | $17-32 per 1,000 requests | Proxy costs only ($50-200/month) |
| Rate limits | Quota-based, billable | Limited by your proxy pool size |
| Result caps | 60 results max per search | Effectively unlimited |
| Data freshness | Cached, sometimes stale | Real-time from Maps UI |
| Fields available | Limited by field masks | Everything visible on the page |
| Legal status | Fully authorized | TOS violation (gray area) |
| Maintenance | Zero | Ongoing — selectors break |
The API is the right choice if you need 60 or fewer results per query, budget isn't a constraint, and you want zero maintenance. Scraping makes more sense when you need comprehensive coverage of a region (thousands of listings), unrestricted data fields, or real-time data at scale.
For most lead generation projects — "give me every plumber in Texas" — scraping is the only economically viable option. The API's 60-result cap per query would require tens of thousands of separate API calls to cover a state.
Google's anti-scraping stack is one of the most sophisticated on the web. Naive approaches fail within minutes because of:
Google aggressively throttles IPs that send too many requests. Typical thresholds are around 100-300 Maps requests per hour before CAPTCHA challenges appear, and a few hundred more before you get fully blocked. Datacenter IPs are limited much more aggressively — often blocked within 20-50 requests.
Google shows reCAPTCHA v2 and v3 challenges on suspicious traffic. Once you see one, your IP's reputation is compromised for hours. Solving them automatically is possible (through services like 2Captcha) but adds cost and latency.
Google Maps is a dynamic single-page app. Data is loaded via XHR requests to internal APIs, which require maintaining a valid browser session and cookies. Pure HTML scraping misses most of the structured data.
Google identifies traffic from datacenter ASNs (AWS, DigitalOcean, OVH) instantly and treats it as hostile by default. Combined with browser fingerprinting — TLS signature, canvas, WebGL — most scrapers fail on either the IP check or the fingerprint check.
Proxy choice is the foundation. Without clean IPs, no amount of code sophistication saves you.
| Proxy Type | Google Maps Success Rate | Throughput Per IP | Cost Efficiency |
|---|---|---|---|
| Free / Public | 0-2% | N/A | Unusable |
| Datacenter | 10-25% | 20-50 req/hr | Poor |
| Rotating Residential | 85-93% | 30-80 req/hr | Excellent |
| Static Residential / ISP | 75-88% | 80-120 req/hr | Good |
| Mobile 4G/5G | 95-98% | 50-100 req/hr | Expensive but reliable |
Rotating residential proxies offer the best cost-per-successful-request ratio for most Google Maps scraping. They give you access to a pool of clean IPs from real ISPs at a per-GB cost an order of magnitude lower than mobile proxies.
For aggressive scraping where uptime matters more than cost, mobile proxies remain the gold standard — Google cannot blanket-block carrier IPs without disrupting their own mobile user base.
Here's a complete scraper using Playwright for JS rendering and residential proxies for clean IPs. This handles the full flow: search → extract listings → get details → save.
pip install playwright playwright-stealth
playwright install chromium
PROXY = {
"server": "http://gate.spyderproxy.com:10000",
"username": "your_username",
"password": "your_password",
}
SEARCH_QUERY = "plumbers in Austin, Texas"
MAX_RESULTS = 200
import asyncio
import json
import random
from playwright.async_api import async_playwright
from playwright_stealth import stealth_async
async def scrape_google_maps(query, max_results=200):
results = []
async with async_playwright() as p:
browser = await p.chromium.launch(
headless=True,
proxy=PROXY,
args=["--disable-blink-features=AutomationControlled"],
)
context = await browser.new_context(
user_agent=(
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
"AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/124.0.0.0 Safari/537.36"
),
viewport={"width": 1920, "height": 1080},
locale="en-US",
)
page = await context.new_page()
await stealth_async(page)
# Navigate to Google Maps search
url = f"https://www.google.com/maps/search/{query.replace(' ', '+')}"
await page.goto(url, wait_until="networkidle")
# Wait for results list to load
await page.wait_for_selector('div[role="feed"]', timeout=30000)
# Scroll the results panel to load more listings
panel = page.locator('div[role="feed"]')
previous_count = 0
while len(results) < max_results:
# Extract currently visible listings
cards = await page.locator('div[role="feed"] > div > div[jsaction]').all()
for card in cards[previous_count:]:
data = await extract_listing(card)
if data:
results.append(data)
if len(results) >= max_results:
break
previous_count = len(cards)
# Scroll to load more
await panel.evaluate("el => el.scrollBy(0, el.scrollHeight)")
await asyncio.sleep(random.uniform(1.5, 3.5))
# Check if we've reached the end
new_cards = await page.locator('div[role="feed"] > div > div[jsaction]').count()
if new_cards == previous_count:
break
await browser.close()
return results
async def extract_listing(card):
"""Extract structured data from a single listing card"""
try:
name = await card.locator('.fontHeadlineSmall, [role="heading"]').inner_text()
# Get the rating and review count
rating_block = await card.locator('.fontBodyMedium span[role="img"]').first.get_attribute('aria-label') or ""
# Click into the listing for full details
await card.click()
await asyncio.sleep(random.uniform(1.5, 3.0))
# Extract details from the side panel
details = await card.page.locator('[role="main"]').first
phone = ""
address = ""
website = ""
category = ""
# Phone
phone_btn = details.locator('button[data-item-id^="phone"]')
if await phone_btn.count() > 0:
phone = await phone_btn.first.get_attribute('aria-label') or ""
# Address
addr_btn = details.locator('button[data-item-id="address"]')
if await addr_btn.count() > 0:
address = await addr_btn.first.get_attribute('aria-label') or ""
# Website
web_btn = details.locator('a[data-item-id="authority"]')
if await web_btn.count() > 0:
website = await web_btn.first.get_attribute('href') or ""
return {
"name": name.strip(),
"address": address.replace("Address: ", "").strip(),
"phone": phone.replace("Phone: ", "").strip(),
"website": website,
"rating_info": rating_block.strip(),
}
except Exception as e:
print(f"Error extracting: {e}")
return None
# Run it
results = asyncio.run(scrape_google_maps(SEARCH_QUERY, MAX_RESULTS))
with open("leads.json", "w") as f:
json.dump(results, f, indent=2)
print(f"Scraped {len(results)} businesses")
This scraper handles the core flow. For production use at scale, you'll want to add the patterns in the next section.
Getting one run of 200 listings is easy. Running thousands of queries per day without interruption takes more work.
Don't rotate per request — rotate per query. Each search query is a session that includes the search, scrolling, and detail views. Keeping the same IP through one complete query looks natural. Rotating in the middle of a query looks robotic.
Real users don't scroll with perfect cadence. Use random delays between 1.5-3.5 seconds between scrolls, vary the scroll distance, and occasionally scroll back up a bit before continuing down.
If you're scraping "plumbers in [city]" for every US city, don't run the queries sequentially in alphabetical order. Shuffle the query list. Mix in unrelated queries occasionally. Make the traffic look like organic usage.
Cycle through realistic combinations of user agent, viewport, and locale. A real population visiting Google Maps isn't all Chrome 124 on 1920x1080 — it's a mix of Chrome, Firefox, Edge, Safari, and dozens of screen sizes.
Google Maps data doesn't change minute-to-minute. Cache results for 24-48 hours and refresh incrementally. Every request you don't make is a request that can't trigger a block.
When you hit a CAPTCHA, don't fight it on that IP. Stop, rotate to a fresh IP, and cool down. Trying to solve CAPTCHAs from the same IP just reinforces the block.
Don't run 100 threads from one machine. Run distributed scrapers across regions — 10 workers × 10 concurrent queries each, spread across different cloud regions or serverless deployments. Horizontal scaling avoids the burst patterns that trip Google's volumetric detection.
Reviews and photos add an order of magnitude to scraping complexity because they're paginated and often require additional user interaction.
*.googleusercontent.com=w203-h152-k-no) with =w1200-h900-k-no for higher resolutionScraping Google Maps sits in a legal gray area. Key points to understand before proceeding:
For commercial scraping at scale, consult an attorney qualified in your jurisdiction. This guide is technical, not legal advice.
Direct scraping isn't always the best choice. Consider these alternatives:
Official, authorized, works within strict rate limits. Best for small-to-medium data needs and when you can't tolerate any TOS risk.
Services like Data Axle, D&B Hoovers, and ZoomInfo aggregate business data from many sources including Google Maps. Expensive but legally clean and pre-verified.
Alternative sources that cover similar data. Often easier to scrape and sometimes have different listings than Google. Combining sources gives better coverage than any single one.
OpenStreetMap has volunteer-contributed business data that's free and legally unrestricted. Coverage varies but for some categories (restaurants, cafes, tourist attractions) it's excellent.
With a well-configured scraper using residential proxies and proper pacing, 5,000-20,000 listings per day from a small proxy pool is achievable. With mobile proxies and horizontal scaling, 100,000+ per day is realistic at a higher cost.
Google blocks poorly-configured scrapers quickly — those using datacenter IPs, default user agents, or aggressive pacing. A well-designed scraper using residential proxies, stealth browsers, and realistic pacing can run indefinitely without getting blocked. It's an engineering problem, not an impossibility.
No-code tools like Apify, Octoparse, and PhantomBuster offer pre-built Google Maps scrapers. Costs range from $0.50 to $5.00 per 1,000 listings depending on the service. Faster to get started but usually more expensive than running your own scraper at volume.
Technically yes, but be aware that reviews are copyrighted by the reviewers, not by Google. Aggregating reviews for analysis (sentiment, keywords) is lower-risk than republishing them. Most commercial projects scrape review counts and average ratings, not the full text.
Rotating residential proxies offer the best cost-per-success ratio for most use cases. Mobile 4G/5G proxies give the highest success rates (95-98%) but at 3-5x the cost. Datacenter proxies fail on Google Maps within a handful of requests — avoid them entirely. See our proxy plans for options tailored to scraping.
The legal situation is nuanced. Scraping publicly-visible business information (name, address, phone, website) is generally considered lower-risk than scraping personal data or copyrighted content. However, it violates Google's TOS, which creates civil liability exposure. Consult a qualified attorney in your jurisdiction before running commercial scraping at scale.