# B2B Google Maps Lead Scraper (`riad_h/gmaps-lead-scraper`) Actor

Scrape B2B leads from Google Maps with phone, email, social links, ratings, reviews, and coordinates. Network-interception powered, proxy-ready, streaming output.

- **URL**: https://apify.com/riad\_h/gmaps-lead-scraper.md
- **Developed by:** [Riad Hossain](https://apify.com/riad_h) (community)
- **Categories:**
- **Stats:** 2 total users, 1 monthly users, 92.9% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.01 / 1,000 results

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.

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

## B2B Google Maps Lead Scraper

Extract verified B2B leads from Google Maps — business name, phone, website, emails, social profiles, ratings, reviews, and GPS coordinates — in a single high-throughput Actor. Network-interception first, with graceful DOM fallback when Google changes its UI.

### What you get

For every business matching your query, the Actor returns a structured record:

| Field | Always returned | Notes |
|---|---|---|
| `business_name` | ✅ | Full business name |
| `category` | ✅ | Primary Google Maps category (`Dental clinic`, `Plumber`, etc.) |
| `address` | ✅ | Full + parsed street / city / state / postal\_code / country |
| `phone` | ✅ | Direct phone line when public |
| `website` | ✅ | Business homepage URL |
| `rating` & `review_count` | ✅ | Google Maps aggregate |
| `location.lat / lng` | ✅ | GPS coordinates |
| `place_id` | ✅ | Google Maps `ChIJ...` identifier — useful for de-duplication |
| `emails` | `extract_depth: full` | Generic (`info@`, `contact@`) and personal inboxes, classified |
| `social_links` | `extract_depth: full` | Facebook, LinkedIn, Instagram, Twitter |

All results are streamed directly to the dataset — no in-memory batching, so you can pull 5,000 leads in one run without exhausting RAM.

### Why this scraper is faster than DOM-only scrapers

Google Maps renders hundreds of DOM nodes per card, and Google rewrites the DOM tree on every UI redesign — selectors break weekly. Instead of fighting the DOM, this Actor intercepts the internal `/maps/rpc/` JSON endpoints that Google itself uses to populate the page. The result:

- **3–5× faster** than selector-based scrapers on the same query
- **Resistant to UI redesigns** — the RPC schema is far more stable than the DOM
- **Falls back to DOM parsing automatically** when interception yields nothing, so the Actor keeps working even if Google changes its protocol

### How much does it cost to scrape Google Maps?

Pricing is **pay-per-event** — you only pay for what you scrape.

| Event | Free tier | Paid tier | Triggered when |
|---|---|---|---|
| `place-scraped` | $0.004 | $0.003 | One business card is fully scraped |
| `contact-scraped` | $0.002 | $0.002 | Phone or website is present on the listing |
| `lead-enriched` | $0.10 | $0.005 | Email or social link extracted (only with `extract_depth: full`) |

**Typical budgets:**

- 1,000 places, basic depth → **~$4**
- 1,000 places, full enrichment with emails → **~$5–$10** (depending on how many listings publish emails)
- 10,000 places, basic depth → **~$40**

Apify also bills a flat `apify-default-dataset-item` event (~$0.0001) per row written to the dataset.

### Tutorial: scrape 100 dental clinics in Houston

1. **Open the Actor** → click **Try for free** (Apify gives you $5 monthly free credit).
2. Fill in the input form:
   - **Search queries**: `["dental clinics"]`
   - **Location**: `Houston TX`
   - **Max results**: `100`
   - **Extract depth**: `basic` (fast) or `full` (adds emails + socials)
   - **Enrichment**: toggle `extract_emails` / `extract_socials` if you picked `full`
   - **Proxy**: leave default (Apify's residential proxy is used automatically)
3. Click **Start** and watch the live log + dataset fill in real time.
4. Export the dataset as **JSON**, **CSV**, **Excel**, or **HTML table**.

#### Sample input (JSON)

```json
{
  "search_queries": ["dental clinics", "orthodontists"],
  "location": "Houston TX",
  "max_results": 100,
  "extract_depth": "full",
  "enrichment": { "extract_emails": true, "extract_socials": true },
  "filters": { "min_rating": 4.0, "min_reviews": 20, "has_website": true }
}
```

#### Sample output (one lead)

```json
{
  "place_id": "ChIJh2YNot3AQiYRvK_QxGTBm0Q",
  "business_name": "Bright Smile Dental Houston",
  "category": "Dental clinic",
  "address": {
    "full": "7700 Main St, Houston, TX 77025, USA",
    "street": "7700 Main St",
    "city": "Houston",
    "state": "TX",
    "postal_code": "77025",
    "country": "USA"
  },
  "contact": {
    "phone": "+1-713-555-0142",
    "website": "https://www.brightsmilehouston.com",
    "emails": [
      { "email": "hello@brightsmilehouston.com", "type": "generic" }
    ],
    "social_links": {
      "facebook": "https://facebook.com/brightsmilehouston",
      "instagram": "https://instagram.com/brightsmilehouston"
    }
  },
  "metrics": { "rating": 4.7, "review_count": 213 },
  "location": { "lat": 29.7189, "lng": -95.3912 },
  "scraped_at": "2026-08-28T12:04:33.401Z"
}
```

### Tips for best results

- **Always enable Apify's proxy** (`proxy_config.use_apify_proxy: true`) for production runs. Google rate-limits unproxied Maps scraping within ~50 results.
- **Use `extract_depth: 'full'` only when you actually need emails** — it adds 1–3 seconds per lead because the Actor visits each business's homepage.
- **Batch by city**, not by state. `["dental clinics"] + "Houston TX"` gives you ~500 unique leads; `["dental clinics"] + "Texas"` caps at the same ~500 because Google de-duplicates by proximity.
- **Add filters** to cut noise: `min_rating: 4.0` and `min_reviews: 20` will drop brand-new and one-star businesses from your dataset.
- **For >1,000 leads**, split into multiple queries (by neighborhood or category) and run them as separate Apify tasks.

### FAQ

#### Will Google block me?

Apify's residential proxy rotates IPs on every request, so blocking is rare. If a particular query returns zero results, the Actor automatically retries once with a different proxy session.

#### Can I scrape reviews too?

Not in this version — only the aggregate `rating` and `review_count`. A reviews-scraping endpoint is on the roadmap.

#### How current is the data?

Google Maps listings update daily; the Actor returns whatever Google has live at the moment of scraping. The `scraped_at` field gives you an ISO-8601 timestamp per lead.

#### Is this legal?

Scraping publicly available business contact info for B2B outreach is generally permitted in most jurisdictions, but you should consult your legal counsel and respect Google's Terms of Service. The Actor includes built-in filters to skip listings without websites (signals a business that does not want to be contacted digitally).

#### I need >50,000 leads — can this scale?

Yes. Run the Actor with `max_results: 5000` per query, split into ~10 separate Apify runs in parallel. Each run uses its own proxy session, so they don't interfere.

### Support

Found a bug or want a feature? Open an issue on the Actor's Apify Console page → **Issues** tab, or contact the author via Apify Store. We respond within 24 hours on business days.

### Use cases

- **B2B sales prospecting** — build lists of qualified leads (e.g. all dental clinics in Texas with 4+ star ratings and 50+ reviews)
- **Market research** — analyze competitor density, rating distribution, and category saturation by region
- **SEO outreach** — find local businesses with weak websites (no SSL, broken links) and pitch web dev services
- **Lead enrichment** — append emails and social profiles to an existing list of business names
- **Recruiting** — find independent businesses (restaurants, salons, clinics) and reach out about hiring needs

### License & source

The Actor is closed-source on the Apify Store but uses an MIT-licensed core. For development documentation, see `DEVELOPERS.md` (available in the source distribution).

# Actor input Schema

## `search_queries` (type: `array`):

List of business categories or queries to search (e.g. \["dental clinics", "plumbers"]).

## `location` (type: `string`):

Geographical location string (e.g. "Houston TX", "London, UK").

## `coordinates` (type: `object`):

Optional explicit lat/lng for the search center. Overrides the textual location if set.

## `max_results` (type: `integer`):

Hard cap on number of businesses returned per query.

## `extract_depth` (type: `string`):

basic = Google Maps data only; full = also visit each website for emails + socials.

## `filters` (type: `object`):

Optional post-extraction filters.

## `enrichment` (type: `object`):

Toggles for email + social extraction. Only consulted when extract\_depth=full.

## `proxy_config` (type: `object`):

Optional proxy. Set use\_apify\_proxy=true to use Apify's residential pools.

## Actor input object example

```json
{
  "search_queries": [
    "dental clinics"
  ],
  "location": "Houston TX",
  "max_results": 100,
  "extract_depth": "basic",
  "enrichment": {
    "extract_emails": true,
    "extract_socials": true
  }
}
```

# Actor output Schema

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

View all scraped business leads with phone, email, social links, ratings, and coordinates.

## `run_details` (type: `string`):

Full run metadata including input, output, and logs.

# 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 = {
    "search_queries": [
        "dental clinics"
    ],
    "location": "Houston TX",
    "max_results": 100,
    "extract_depth": "basic"
};

// Run the Actor and wait for it to finish
const run = await client.actor("riad_h/gmaps-lead-scraper").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 = {
    "search_queries": ["dental clinics"],
    "location": "Houston TX",
    "max_results": 100,
    "extract_depth": "basic",
}

# Run the Actor and wait for it to finish
run = client.actor("riad_h/gmaps-lead-scraper").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 '{
  "search_queries": [
    "dental clinics"
  ],
  "location": "Houston TX",
  "max_results": 100,
  "extract_depth": "basic"
}' |
apify call riad_h/gmaps-lead-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,riad_h/gmaps-lead-scraper"
        }
    }
}

```

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/kVxDRShyjnKCh1P19/builds/TmCxUydOANWrFZrrL/openapi.json
