A Google Places scraper is any tool that extracts local business data - names, addresses, phone numbers, websites, ratings - from Google's place results. You can build one with a headless browser, rent a hosted actor, or skip scraping entirely and call a business-data API that returns those fields plus deduped emails as clean JSON.
What Is a Google Places Scraper?
A Google Places scraper is a program that collects business listings from Google's local data - the same records you see in Google Maps and the local pack - and turns them into rows or JSON you can use in software. "Google Places" here means Google's place entities: a business name, its address, coordinates, phone, website, category, rating, and review count.
There are two very different things people call a "Google Places scraper," and conflating them causes most of the confusion:
- A browser-based scraper that loads Google Maps pages and parses the visible HTML. This is the literal meaning of scraping, and it is the fragile, terms-risky path.
- A data pipeline built on the official Google Places API (or a business-data API layered on top of it) that requests structured records instead of parsing pages. This is not really "scraping" at all, and it is the reliable path.
If your end goal is a repeatable feed of local business records for a CRM, an AI agent, a market map, or a lead list, you almost never want option 1. This guide walks through all the real options honestly, then shows the JSON-first path in full.
What Data Can You Get From Google Places?
Google's place records are rich for discovery and identity, but thin for outreach. Here is what is realistically available per business and what is not:
| Field | Available from Google Places | Notes |
|---|---|---|
| Business name | Yes | Core identity field. |
| Address | Yes | Formatted address and coordinates. |
| Phone number | Yes | National and international formats. |
| Website URL | Yes | When the business has listed one. |
| Category / place type | Yes | Such as dentist, cafe, roofing_contractor. |
| Rating and review count | Yes | Plus individual reviews and summaries. |
| Business status and hours | Yes | Operational, temporarily closed, opening hours. |
| Contact email | No | Not a Google Places field. You must visit the website to get it. |
| Social profiles | No | Not a Google Places field. |
| Named decision-makers | No | Not a Google Places field. |
That last block is the whole reason lead-gen, sales, and recruiting teams keep looking for a "scraper" in the first place: they need emails and contacts, and Google Places simply does not return them. Any workable solution has to add a website-enrichment step on top of place discovery. We cover the fastest way to extract emails from any website separately.
The Three Ways to Scrape Google Places
There are exactly three practical approaches. They trade off control, effort, reliability, and compliance risk very differently.
| Approach | What it is | Effort to maintain | Emails included | Terms risk |
|---|---|---|---|---|
| DIY browser scraper | Your own Playwright / Puppeteer crawler on Google Maps pages | High and ongoing | Only if you build a second crawler | Higher (scrapes Google Maps Content) |
| Hosted scraping actor | A rented Google Maps actor or scraping service | Medium (you manage runs, retries, cost) | Sometimes, quality varies | Higher (still scrapes Maps) |
| Business-data API | One request in, structured JSON out, enrichment built in | Low (provider owns it) | Yes, deduped and verified | Lower (official sourcing + robots-aware enrichment) |
Option 1: Build a DIY Google Places Scraper
The do-it-yourself route uses a headless browser to load Google Maps search results and parse the DOM. In a quick test it looks easy:
import { chromium } from "playwright";
const browser = await chromium.launch();
const page = await browser.newPage();
await page.goto("https://www.google.com/maps/search/dentist+in+Austin+TX");
// Wait for the results feed to render.
await page.waitForSelector('div[role="feed"]');
// Parse each card. Selectors like these change without warning.
const cards = await page.$$('div[role="feed"] > div');
for (const card of cards) {
const name = await card.$eval("a", (a) => a.getAttribute("aria-label"));
// ...dig phone, address, rating out of fragile nested nodes,
// scroll to load more, dismiss consent modals, solve CAPTCHAs,
// and rotate proxies when Google starts blocking you.
}
The problem is not writing the first version. It is that this code is a liability the moment it ships:
- Google changes class names and DOM structure regularly, and every change silently breaks your parser.
- You will hit consent walls, rate limits, CAPTCHAs, and IP bans, so you now own a proxy pool.
- The HTML gives you no emails, so you need a second crawler to visit each website anyway.
- You still have to dedupe, normalize addresses, and validate output.
For a one-off, time-boxed research task this can be acceptable. For anything that has to run next month, the scraper becomes the product, and keeping it alive is the job.
Option 2: Rent a Hosted Scraping Actor
Hosted "Google Maps scraper" actors (for example an Apify actor id like compass/crawler-google-places) move the browser maintenance to a vendor. You configure a search, run the actor, and download results. This is less fragile than a homegrown crawler, but the fundamentals do not change: it is still scraping Google Maps Content, so the terms posture is the same, run costs and retries are yours to manage, and email quality depends entirely on whether and how the actor visits business websites. If you are weighing these platforms specifically, see Apify vs Clay vs Bright Data vs biz collect.
Option 3: Call a Business-Data API (the JSON Path)
The third option stops scraping altogether. A business data API starts from official Google Places discovery, then enriches each business from its own website, and hands you structured JSON. You send one request; the provider owns the browsers, proxies, dedupe, and enrichment. This is what biz collect does, and it is the path the rest of this guide details, because it is the only one that returns emails and stays low-maintenance.
Is It Legal to Scrape Google Places?
This is not legal advice, but the risk categories are clearly different. Google's Google Maps Platform Terms of Service restrict exporting, extracting, or scraping Google Maps Content for use outside the services, and restrict caching Google Maps Content except as expressly permitted. In plain terms: loading Google Maps pages and harvesting the content is exactly the behavior those terms constrain, whether you do it with your own browser or a hosted actor.
Using the official Google Places API under its own terms is a different posture, and so is a business-data API that sources from Places officially and only enriches from each business's own public website. There is also a data-protection layer: business contact data can be personal data depending on jurisdiction, so your workflow needs clear acceptable-use boundaries regardless of the sourcing method. biz collect publishes an acceptable use policy and a security overview so you can evaluate it properly, and its scraper honors each site's robots.txt by default (more on that below). For the full comparison of the official API, scrapers, and the terms trade-offs, read Google Places API vs scrapers, and for the exact clauses - including what you may cache and store - see what the Google Places API terms allow.
Does Google Places Give You Emails?
No. The Google Places API returns place fields such as name, address, phone, website, rating, and hours, but a contact email is not a Places result. To get emails you have to visit each business website and extract them yourself, then dedupe and verify. That enrichment step - not the place discovery - is the hard, valuable part, and it is exactly what a business-data API folds into the same request. When scrape_emails is enabled, biz collect fetches each business's website, pulls contact emails, dedupes them, ranks them best-contact-first, and verifies deliverability (syntax plus MX lookup) before returning them.
How to Get Google Places Data as JSON (Without a Scraper)
Here is the full JSON-first workflow. It is three moves: create a search job, poll it, read structured business records. No browser, no proxies, no selectors.
20+
structured fields returned per business, including deduped and verified emails
Step 1: Create a Search Job
POST a location and keywords. The request is deliberately small:
curl -X POST "https://bizcollect.dev/api/v1/search" \
-H "Authorization: Bearer $BIZ_COLLECT_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"location": "Austin, TX",
"keywords": ["dentist"],
"radius_km": 15,
"scrape_emails": true
}'
The API geocodes the location, runs Google Places Text Search for each keyword inside the radius, dedupes the places, and (because scrape_emails is true) queues website enrichment. It returns immediately with a job id:
{
"job_id": "job_01hxyz9c8m",
"status": "queued",
"poll_url": "/api/v1/jobs/job_01hxyz9c8m",
"location": "Austin, TX",
"resolved_location": "Austin, Travis County, Texas, United States",
"radius_km": 15,
"keywords": ["dentist"],
"scrape_emails": true,
"coverage": "standard",
"credits_charged": 20,
"credits_remaining": 180
}
Note resolved_location: you can verify the search landed where you intended before you rely on the results. A standard one-keyword, one-page search costs 20 credits, so your 200 signup credits are 10 searches to prototype with.
Step 2: Poll the Job
Poll the job until status is completed (or failed, in which case the credits are auto-refunded):
curl "https://bizcollect.dev/api/v1/jobs/job_01hxyz9c8m" \
-H "Authorization: Bearer $BIZ_COLLECT_API_KEY"
Step 3: Read Structured Business Records
The completed job carries a businesses array. Each record is stable, typed JSON - here is one entry, trimmed to the fields most workflows use:
{
"job_id": "job_01hxyz9c8m",
"status": "completed",
"progress": 100,
"results_count": 18,
"emails_scraped_count": 12,
"location": "Austin, TX",
"keywords": ["dentist"],
"businesses": [
{
"id": "b_7f2a1c",
"name": "Cedar Park Family Dental",
"address": "1234 N Lamar Blvd, Austin, TX 78701",
"phone": "+1 512-555-0142",
"website": "https://cedarparkfamilydental.com",
"website_is_directory": false,
"emails": ["hello@cedarparkfamilydental.com"],
"email_details": [
{
"email": "hello@cedarparkfamilydental.com",
"confidence": "high",
"has_mx": true,
"is_role_account": true,
"contact": null
}
],
"social_links": [
{ "platform": "instagram", "url": "https://instagram.com/cedarparkdental" },
{ "platform": "facebook", "url": "https://facebook.com/cedarparkdental" }
],
"rating": 4.8,
"user_rating_count": 326,
"business_status": "OPERATIONAL",
"primary_type": "dentist",
"lat": 30.3072,
"lng": -97.7431
}
]
}
Every field name above comes straight from the API. emails is ranked best-contact-first; email_details tags each address with a deliverability confidence (high / medium / low / unknown) and whether its domain has MX records. social_links is an array of { platform, url } objects, not bare strings. When contact resolution runs, email_details[].contact and a business-level people[] roster surface named, salutation-ready people. The full schema, including reviews, opening hours, and coverage modes, is in the API docs.
Do It in One Call from Code
In a script or backend job, poll in a loop:
const BASE = "https://bizcollect.dev/api/v1";
const headers = {
Authorization: `Bearer ${process.env.BIZ_COLLECT_API_KEY}`,
"Content-Type": "application/json",
};
const start = await fetch(`${BASE}/search`, {
method: "POST",
headers,
body: JSON.stringify({
location: "Austin, TX",
keywords: ["dentist"],
radius_km: 15,
scrape_emails: true,
}),
});
const { job_id } = await start.json();
// Poll until the job finishes.
let job;
do {
await new Promise((r) => setTimeout(r, 3000));
job = await (await fetch(`${BASE}/jobs/${job_id}`, { headers })).json();
} while (job.status !== "completed" && job.status !== "failed");
console.log(job.businesses);
For AI Agents: One Synchronous Call
If an AI agent or tool-calling model is doing the work, the poll loop is friction. Set wait: true and the request blocks server-side until the job finishes, then returns the full results inline - one call, one JSON response, the same schema you would have polled for:
curl -X POST "https://bizcollect.dev/api/v1/search" \
-H "Authorization: Bearer $BIZ_COLLECT_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"location": "Austin, TX",
"keywords": ["dentist"],
"scrape_emails": true,
"wait": true
}'
That single-call shape, plus the OpenAPI 3.1 spec and /llms.txt, is why this works cleanly as an agent tool. See how to build an AI lead generation agent and the MCP server for business data for the agent patterns. Prefer to be pushed instead of polling? Register a webhook and get a signed job.completed event. Prefer a file? Hit the export endpoint for CSV, XLSX, or JSON.
Google Places Scraper vs Business-Data API
Side by side, for a workflow that has to keep running:
| Criterion | DIY / hosted Google Places scraper | biz collect business-data API |
|---|---|---|
| Setup | Build crawler + proxy pool, or configure an actor | One POST request |
| Maintenance | You fix selectors and blocks forever | Provider owns it |
| Emails | Second crawler you build, quality varies | Built in, deduped, MX-verified |
| Named contacts | Not included | people[] + per-email contact |
| Output | Whatever you parse | Stable, typed JSON (20+ fields) |
| Compliance | Scrapes Google Maps Content | Official Places sourcing + robots-aware enrichment |
| Agent-friendly | Poor without a wrapper | wait: true, OpenAPI, llms.txt |
| Cost model | Proxies, retries, failed runs, engineering time | Credits per search; free to start |
The robots.txt point is worth underlining. In the default respectful scrape mode, the enrichment step fetches each site's robots.txt and honors it per RFC 9309, skipping sites (or paths) that disallow the crawler. That is a materially different posture from a raw scraper that ignores site rules.
Which Should You Use?
Choose a DIY or hosted Google Places scraper if the task is small, exploratory, manually reviewed, and you accept the maintenance and terms trade-offs.
Choose a business-data API like biz collect if you need local business data as reliable JSON, you want emails and named contacts (not just place records), you are feeding a CRM, an automation, or an AI agent, and you would rather not own headless browsers and proxies. For most production workflows, that is the correct answer - and if you arrived here from a Maps-specific angle, the Google Maps scraper alternative guide covers the same JSON path from that side.
You can start free with 200 signup credits and no credit card, read the schema in the docs, and check limits and paid plans on the pricing page.
Frequently asked questions
- Is there a Google Places scraper that returns emails?
- Google Places itself never returns emails, and most raw scrapers only get place fields. biz collect fills the gap: it discovers businesses from Google Places, then enriches each one from its website to return deduped, MX-verified contact emails as JSON when scrape_emails is enabled.
- How do I scrape Google Places without a headless browser?
- Call a business-data API instead of parsing pages. With biz collect you POST a location and keywords to /api/v1/search, poll /api/v1/jobs/{job_id} (or set wait:true for one synchronous call), and read structured business records. No browser, proxies, or selectors to maintain.
- Is it legal to scrape Google Places?
- This is not legal advice, but Google Maps Platform Terms restrict exporting, extracting, scraping, and caching Google Maps Content, which is what page scrapers do. Using the official Places API under its terms, or an API that sources from Places officially and only enriches from each business's own public website, is a different posture. Always review your own obligations and any personal-data rules.
- What is the Google Places API and how is it different from a scraper?
- The Google Places API is Google's official service for place search and details (name, address, phone, website, rating, hours). A scraper parses Google Maps HTML instead. The API is reliable but returns no emails or social profiles; a scraper is fragile and carries terms risk. A business-data API layers website enrichment on top of official Places sourcing.
- How much does scraping Google Places cost?
- A DIY or hosted scraper costs proxies, retries, failed runs, and ongoing engineering time that is hard to forecast. biz collect uses credits: a standard one-keyword, one-page search is 20 credits, and you start with 200 signup credits plus 20 daily, no credit card, so you can prototype for free.
- Can I use a Google Places scraper inside n8n, Make, or an AI agent?
- Yes. Because biz collect is a plain JSON API with an async job model, a synchronous wait:true mode, an OpenAPI 3.1 spec, and llms.txt, it drops into n8n, Make, Zapier, and tool-calling agents far more cleanly than a browser scraper. Point one HTTP step at /api/v1/search and read the JSON.





