# Google Maps Scraper Goat (`goat255/google-maps-extractor`) Actor

This Google Maps scraper extracts business leads at scale: names, phones, emails, websites, structured addresses, ratings, every full review (not just the count), popular times, GPS, and social profile links found on each business website. Pure HTTP, no browser, no Google API key.

- **URL**: https://apify.com/goat255/google-maps-extractor.md
- **Developed by:** [Goutam Soni](https://apify.com/goat255) (community)
- **Categories:** Lead generation, Real estate, Travel
- **Stats:** 31 total users, 0 monthly users, 44.8% runs succeeded, 0 bookmarks
- **User rating**: 5.00 out of 5 stars

## Pricing

from $3.00 / 1,000 leads

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Google Maps Scraper - Business Leads, Emails, Reviews & Socials

This Google Maps scraper extracts business leads at scale: names, phones, emails, websites, full addresses (structured), ratings, **review counts**, GPS coordinates, Knowledge Graph IDs, **and 10 social platforms**. No browser needed, runs on pure HTTP for maximum speed and minimal cost.

### What this Google Maps scraper does

Enter search queries and locations. The actor scrapes Google Maps results with automatic pagination, then optionally visits each business website to harvest emails and social profiles across 10 networks.

Each query × location pair runs in parallel for fast bulk extraction.

### Extracted fields

#### From the Maps search response (always)

| Field | Description |
|-------|-------------|
| `name` | Business name |
| `nameLocal` | Name in the local language (e.g. Hindi, Arabic) |
| `category` | Categories (semicolon-separated) |
| `categoryList` | Categories as an array |
| `addressFull` | Full formatted address |
| `addressNeighborhood` | Neighborhood / short locality |
| `addressJoined` | Address parts comma-joined (legacy form) |
| `structuredAddress` | `{neighborhood, addressLine1, addressLine2, city, state, postalCode, countryCode}` |
| `regionDisplay` | Human-readable region (e.g. "Jaipur, Rajasthan, India") |
| `countryCode` | ISO country code |
| `phone` | Phone in display format |
| `phoneE164` | Phone in E.164 format (e.g. `+917665552345`) |
| `website` | Business website URL |
| `averageRating` | Star rating (1.0 - 5.0) |
| **`reviewCount`** | **Total number of reviews** |
| `latitude`, `longitude` | GPS coordinates |
| `placeID` | Google Place ID (`ChIJ...`) |
| `kgID` | Knowledge Graph ID (`/g/...`) |
| `cID` | Customer ID in hex (`0x...:0x...`) |
| `cidDecimal` | CID as a decimal string |
| `ownerUserID` | Google account ID of the GMB owner |
| `profilePhoto` | URL of the business profile photo |
| `timezone` | IANA timezone (e.g. `Asia/Calcutta`) |
| `languagePrimary` | Primary language (e.g. `en`) |
| `languageSecondary` | Secondary language (e.g. `hi`) |
| `0_Sunday`...`6_Saturday` | Working hours per weekday |

#### From the Place Details endpoint (when `Extract Place Details` is enabled)

Adds 1 HTTP request per business. Enriches every record with:

| Field | Description |
|-------|-------------|
| `description` | Full business description ("From the owner") |
| `plusCode` | Google Plus Code (e.g. "VQ7Q+G8 Jaipur") |
| `displayedReviewCount` | Canonical review count shown on the place page |
| `reviewsPerStar` | Counts per star: `{"1": 11, "2": 1, "3": 1, "4": 6, "5": 145}` |
| `reviewTopics` | Topic chips from reviews (`pre-wedding shoot`, `professional team`, etc.) |
| `popularTimes` | Hourly busy percentages per day of week |
| `features` | Service options, accessibility, amenities, crowd attributes |
| `inlineReviews` | First batch of reviews (8) embedded in the place page |
| `previewQuotes` | Short review snippets shown in the place panel |
| `owner` | `{name, userID, link}` to the owner's Maps contributor page |
| `canonicalMapsURL` | Canonical place URL on Google Maps |
| `categoryIDs` | Machine-readable category IDs with weights |
| `addrNeighborhood`, `addrLine1`, `addrLine2`, `addrCity`, `addrPostalCode`, `addrState`, `addrCountryCode` | Granular address parts |

#### From the Reviews endpoint (when `Extract Full Review Text` is enabled)

**Marquee feature: every review comes back in full, not just the review count.**

Adds 1+ HTTP requests per business (paginated, default page size 10). Each review record contains:

| Field | Description |
|-------|-------------|
| `reviewID` | Stable Google review identifier |
| `authorName` | Reviewer display name |
| `authorPhoto` | Reviewer profile photo URL |
| `authorProfileURL` | Reviewer's Maps contributor page |
| `authorID` | Google account ID |
| `authorReviewCount` | Total reviews this author has written |
| `authorPhotoCount` | Total photos this author has uploaded |
| `rating` | 1-5 star rating |
| `text` | Full review text |
| `relativeTime` | "3 months ago" |
| `timestamp` | Unix seconds since epoch |
| `photos` | URLs of photos posted with the review |
| `ownerReply` | `{text, relativeTime, timestamp}` when owner replied |

Configurable via `maxReviewsPerPlace` (default 20, set to 0 for every review).

#### From the website crawl (when `Extract Emails & Social Profiles` is enabled)

| Field | Description |
|-------|-------------|
| `email` | Business email addresses (domain-prioritized) |
| `instagram` | Instagram profile URLs |
| `facebook` | Facebook page URLs |
| `linkedin` | LinkedIn page URLs |
| `twitter` | Twitter/X profile URLs |
| `youtube` | YouTube channel URLs |
| `tiktok` | TikTok profile URLs |
| `threads` | Threads profile URLs |
| `whatsapp` | WhatsApp click-to-chat URLs |
| `telegram` | Telegram username URLs |

All website-crawl fields are arrays. The crawler decodes obfuscated email addresses, parses `mailto:` and `whatsapp:` href schemes, and (with deep search enabled) follows `/contact`, `/about`, `/team`, `/help`, etc. up to one level.

### Output example

```json
{
    "name": "Jaipur Pixel Photography",
    "nameLocal": "जयपुर पिक्सेल फोटोग्राफी",
    "category": "Wedding photographer;Aerial photographer;Photo booth",
    "categoryList": ["Wedding photographer", "Aerial photographer", "Photo booth"],
    "addressFull": "P-60-B, Chandra Nagar, Muktanand Nagar, Gopal Pura Mode, Jaipur, Rajasthan 302018, India",
    "structuredAddress": {
        "neighborhood": "Chandra Nagar, Muktanand Nagar, Gopal Pura Mode",
        "addressLine1": "P-60-B",
        "addressLine2": "P-60-B",
        "city": "Jaipur",
        "postalCode": "302018",
        "state": "Rajasthan",
        "countryCode": "IN"
    },
    "phone": "+91 76655 52345",
    "phoneE164": "+917665552345",
    "website": "https://example.com/",
    "email": ["info@example.com"],
    "averageRating": 4.7,
    "reviewCount": 489,
    "instagram": ["https://www.instagram.com/jaipurpixel"],
    "facebook": ["https://www.facebook.com/jaipurpixel"],
    "whatsapp": ["https://wa.me/917665552345"],
    "latitude": 26.8637701,
    "longitude": 75.7883492,
    "timezone": "Asia/Calcutta",
    "placeID": "ChIJKwwR99a1bTkRoZJquuye2NU",
    "kgID": "/g/11hj4wlv0_",
    "cID": "0x396db5d6f7110c2b:0xd5d89eecba6a92a1",
    "1_Monday": "7 AM–7 PM"
}
```

### Pricing

This actor uses **pay-per-event** pricing. You only pay for results you receive.

| What you pay for | Price |
|-----------------|-------|
| Per business lead | **$0.005** |

This includes all available data: business info, working hours, structured address, Knowledge Graph IDs, profile photo, and, when enabled, emails and 10-platform social profiles. No hidden fees.

**Example costs:**

- 100 leads without emails: **$0.50**
- 500 leads with emails + socials: **$2.50**
- 1,000 leads with full data: **$5.00**

Set a spending limit before each run to control your budget. The actor stops automatically when your limit is reached.

> Residential proxies recommended (available on all Apify plans including free). The actor automatically falls back to datacenter or direct connection if residential isn't available. Email extraction uses direct connections at no extra proxy cost.

### How it works

1. Constructs optimized API requests to Google Maps (no browser overhead)
2. Automatically paginates to collect all matching results
3. Optionally visits each business website to extract emails and social profiles across 10 platforms
4. Deduplicates results across queries using Google Place IDs

**Email extraction** crawls each business website and its contact/about pages to find:

- Email addresses (prioritizes emails matching the business domain)
- Social media profiles (Instagram, Facebook, LinkedIn, Twitter/X, YouTube, TikTok, Threads, WhatsApp, Telegram)
- Direct `mailto:` and `whatsapp:` href links
- Obfuscated email addresses (decoded automatically)

### Tips

- **Unlimited results:** Set max results to 0
- **Parallel queries:** Multiple query × location pairs run concurrently
- **Better emails:** Enable both email extraction and deep search
- **Geographic precision:** Use direct Google Maps URLs with zoom level
- **Budget control:** Set a spending limit; the actor stops when reached

### Architecture & customization

The actor is built on a **declarative, schema-driven** extraction model. Adding a new field, swapping an extraction source, or fixing a broken index after a Google response change is typically a one-line edit in a single file. See `docs/EXTRACTORS.md` and `docs/SCHEMA_UPDATES.md` for details.

# Actor input Schema

## `queries` (type: `array`):

Business types or keywords to search on Google Maps (e.g. "coffee shop", "plumber").

## `locations` (type: `array`):

Geographic locations to combine with each query (e.g. "New York, NY"). Each query runs for each location.

## `searchUrls` (type: `array`):

Provide direct Google Maps search URLs instead of queries + locations. When provided, queries and locations are ignored.

## `maxResultsPerQuery` (type: `integer`):

Maximum number of results to extract per query-location pair. Set to 0 for unlimited.

## `maxConcurrency` (type: `integer`):

Number of parallel search workers. Higher values are faster but use more proxy bandwidth.

## `extractDetails` (type: `boolean`):

For each business, fetch its Place Details page to enrich with: full business description, Plus Code, more granular structured address (neighborhood/city/state/postal/country), reviews-per-star breakdown (1★/2★/3★/4★/5★ counts), review topic chips, popular times (hourly busy %), feature attributes (wheelchair accessible, LGBTQ-friendly, etc.), owner profile, canonical Maps URL, and category IDs. Adds 1 HTTP request per result.

## `maxDetailsConcurrency` (type: `integer`):

Number of parallel Place Details workers.

## `extractReviews` (type: `boolean`):

Fetch every review's full text per place — includes author name, profile photo, rating, relative time, exact timestamp, review text, photos posted with the review, and any owner reply (with reply text and time). Adds 1+ HTTP requests per place (paginated). Marquee feature — most Apify Maps actors only return review COUNT.

## `maxReviewsPerPlace` (type: `integer`):

Maximum number of reviews to fetch per business. Set to 0 for unlimited (every review). Default 20 covers most use cases at low cost.

## `maxReviewsConcurrency` (type: `integer`):

Parallel review fetchers. Lower than search/details to avoid rate-limiting on the reviews endpoint.

## `collectEmails` (type: `boolean`):

Visit each business website to extract email addresses and social media profiles across 10 platforms (Instagram, Facebook, LinkedIn, Twitter/X, YouTube, TikTok, Threads, WhatsApp, Telegram).

## `deepEmailSearch` (type: `boolean`):

When email extraction is enabled, also crawl contact/about pages on each website for more thorough results. Slower but finds more emails.

## `maxEmailConcurrency` (type: `integer`):

Number of parallel email extraction workers.

## `emailTimeout` (type: `integer`):

Timeout in milliseconds for fetching each business website during email extraction.

## `language` (type: `string`):

Language code for Google Maps results (ISO 639-1).

## `country` (type: `string`):

Country code for Google Maps results (ISO 3166-1 alpha-2).

## `proxyConfiguration` (type: `object`):

Residential proxies recommended for best results.

## Actor input object example

```json
{
  "queries": [
    "restaurants"
  ],
  "locations": [
    "New York, NY"
  ],
  "maxResultsPerQuery": 20,
  "maxConcurrency": 10,
  "extractDetails": false,
  "maxDetailsConcurrency": 10,
  "extractReviews": false,
  "maxReviewsPerPlace": 20,
  "maxReviewsConcurrency": 5,
  "collectEmails": false,
  "deepEmailSearch": true,
  "maxEmailConcurrency": 20,
  "emailTimeout": 15000,
  "language": "en",
  "country": "us",
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}
```

# Actor output Schema

## `results` (type: `string`):

Extracted business leads with name, phone (raw + E.164), email, website, full + structured address, rating + review count, social profiles across 10 platforms, profile photo, timezone, language, and GPS coordinates

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "queries": [
        "restaurants"
    ],
    "locations": [
        "New York, NY"
    ],
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ]
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("goat255/google-maps-extractor").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "queries": ["restaurants"],
    "locations": ["New York, NY"],
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
    },
}

# Run the Actor and wait for it to finish
run = client.actor("goat255/google-maps-extractor").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "queries": [
    "restaurants"
  ],
  "locations": [
    "New York, NY"
  ],
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}' |
apify call goat255/google-maps-extractor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,goat255/google-maps-extractor"
        }
    }
}

```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/WwcUNlzZIOUXkmpUR/builds/vP0HNGfDuWMkzGeDY/openapi.json
