# Google Maps Bad Review Lead Scraper & Cold Outreach Bot (`awake_orchestra_trb/google-maps-review-outreach-actor`) Actor

Scrapes Google Maps businesses with recent 1-3 star reviews (1-28 days), extracts contact emails & socials from websites, and dispatches automated reputation management pitch emails.

- **URL**: https://apify.com/awake\_orchestra\_trb/google-maps-review-outreach-actor.md
- **Developed by:** [Reputation Management](https://apify.com/awake_orchestra_trb) (community)
- **Stats:** 2 total users, 1 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per usage

This Actor is paid per platform usage. The Actor is free to use, and you only pay for the Apify platform usage, which gets cheaper the higher subscription plan you have.

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

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
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.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## 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.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## 🌟 Google Maps Bad Review Lead Scraper & Automated Outreach Bot

> **Find high-intent B2B clients on Google Maps who recently received 1–3 star negative reviews, extract their verified decision-maker emails & social media, and automatically pitch reputation management & review removal services.**

***

### 💡 Why This Actor Converts Higher Than Standard Scrapers

Standard Google Maps scrapers give you thousands of random, cold businesses that have no immediate reason to buy your services.

This Actor is engineered specifically for **Reputation Management Agencies, SEO Consultants, and B2B Cold Outreach Specialists**:

1. **Trigger Event Detection**: It scans reviews and isolates businesses that received a **1, 2, or 3-star review within the last 1 to 28 days**.
2. **High Urgency**: Business owners are furious and desperate to fix their rating right after getting a negative review. That is the single best time to pitch review removal or reputation repair.
3. **Verified Email Extraction**: It crawls the business's official website (homepage, `/contact`, `/kontakt`, `/impressum`, `/about`) to extract direct emails (`info@`, `contact@`, `owner@`), phone numbers, and social links (Instagram, Facebook, LinkedIn).
4. **Automated Pitch Dispatch**: Optionally sends personalized pitch emails referencing the exact reviews with direct review links, using your rotating Gmail / SMTP accounts with built-in 24-hour rate limits.

***

### ⚡ Key Features

- **🚀 Parallel Multi-Browser Engine**: Runs across multiple dedicated browser windows and tabs simultaneously, processing queries 2x–4x faster.
- **🎯 Precision Filtering**: Filter by maximum review age (e.g. past 28 days), target stars (1–3★), and minimum bad review threshold.
- **🌐 Multilingual Review Parsing**: Accurately interprets review post dates across **English, German, French, Spanish, Italian, Portuguese, and Dutch** (e.g., *vor 3 Tagen*, *il y a 2 jours*, *hace 4 días*).
- **🔗 Direct Review Deep Links**: Extracts direct shareable links to the exact negative reviews and embeds them into your leads dataset and pitch emails.
- **📬 Automated SMTP Rotation & Rate-Limiting**: Rotate through multiple sender accounts with 24-hour sliding usage counters to safeguard your domain reputation.
- **🛡️ Smart Anti-Deduplication**: Remembers processed places in Apify Key-Value store and local `leads.csv` so you never re-scrape or re-email the same company twice.
- **🧪 Dry-Run Mode**: Test your queries, review extraction, and email templates safely in simulation mode before launching live campaigns.

***

### 📥 Input Configuration

| Parameter | Type | Default | Description |
|---|---|---|---|
| `searchQueries` | `array` | *Swiss Queries* | List of Google Maps search queries (e.g. `["Roofers in Chicago", "Dentists in London"]`). |
| `maxPlacesPerQuery` | `integer` | `200` | Max places to scrape per query (bot auto-scrolls to the end). |
| `maxBrowsers` | `integer` | `2` | Number of parallel browser windows to run simultaneously. |
| `maxConcurrency` | `integer` | `2` | Number of concurrent tabs per browser window. |
| `targetStars` | `array` | `[1, 2, 3]` | Star ratings considered hot leads (1★, 2★, 3★). |
| `maxReviewAgeDays` | `integer` | `28` | Maximum review age in days (e.g. 28 = last 4 weeks). |
| `minBadReviews` | `integer` | `1` | Minimum count of bad reviews required to qualify. |
| `enableEmailOutreach`| `boolean` | `true` | Whether to automatically send pitch emails. |
| `dryRun` | `boolean` | `false` | If true, simulates sending without sending real emails. |
| `smtpAccounts` | `array` | *Configured* | Gmail or custom SMTP accounts for rotating outreach. |
| `senderName` | `string` | `"Reputation Management"` | Sender name displayed in outreach emails. |
| `emailSubject` | `string` | `"Negative Review Removal..."` | Outreach email subject template. Supports `{{business_name}}`. |

***

### 📤 Output Dataset Format

Every qualified lead is saved to the default Apify Dataset in clean, structured JSON and can be exported with 1-click to **Excel, CSV, JSON, or Google Sheets**:

```json
{
  "businessName": "Garage Kummer",
  "primaryEmail": "info@garage-kummer.ch",
  "outreachStatus": "Sent",
  "badReviewStars": "1★, 2★",
  "badReviewCount": 2,
  "latestBadReviewSnippet": "Extremely disappointing service. Car was not ready on time and unexpected charges were added...",
  "website": "https://www.garage-kummer.ch",
  "phone": "+41 33 222 11 00",
  "rating": "3.8",
  "totalReviews": "42",
  "category": "Auto repair shop",
  "address": "Gwattstrasse 12, 3604 Thun, Switzerland",
  "searchQuery": "Thun Car repair shops",
  "reviewLinks": "https://maps.app.goo.gl/example1 ; https://maps.app.goo.gl/example2",
  "badReviewDirectLinks": [
    "https://maps.app.goo.gl/example1",
    "https://maps.app.goo.gl/example2"
  ],
  "placeUrl": "https://www.google.com/maps/place/Garage+Kummer...",
  "allEmails": "info@garage-kummer.ch, service@garage-kummer.ch",
  "allPhones": "+41 33 222 11 00",
  "facebook": "https://facebook.com/garagekummer",
  "instagram": "https://instagram.com/garagekummer",
  "linkedin": "",
  "twitter": "",
  "discoveredAt": "2026-10-02 21:05:00"
}
```

***

### 📊 Overview Table Columns in Apify Console

When viewing your dataset in the Apify Console, you'll see a formatted table with these columns:

1. **Business** – Business name
2. **Email** – Verified primary email address
3. **Outreach Status** – Delivery status (`Sent`, `Simulated`, `No Email Found`)
4. **Negative Stars** – Star ratings summary (e.g. `1★, 2★`)
5. **Count** – Number of recent negative reviews
6. **Review Text** – Preview snippet of the customer's complaint
7. **Website** – Direct clickable website URL
8. **Phone** – Verified phone number
9. **Rating** – Current overall rating
10. **Query** – Keyword used to find the lead

***

### 🚀 How to Run Locally

You can run this Actor completely free on your local machine with zero Apify compute charges:

```bash
## 1. Navigate to the Actor directory
cd apify-maps-review-outreach

## 2. Run with visible browsers (2 parallel browsers, 2 tabs each)
python run_local.py --browsers 2 --tabs 2

## 3. Or run in silent background mode
python run_local.py --headless --browsers 2 --tabs 2

## 4. Or simply double click:
run_local.bat
```

Results are saved to **`leads.csv`** (opens in Excel) and **`storage/datasets/default/`**.

***

### 🛡️ Best Practices & Deliverability

- **Warmup & Limits**: Default limit is 200 emails per day for Gmail. Start with 30–50/day if your email account is fresh.
- **Proxy**: When running large scale jobs (>500 places), enable Apify Residential Proxies to prevent Google Maps rate-limiting.
- **Test First**: Use `dryRun: true` on your first run to preview the leads and pitch emails.

# Actor input Schema

## `searchQueries` (type: `array`):

Google Maps search queries (e.g. 'Dentists in Zurich', 'Restaurants in Geneva', 'Plumbers in London').

## `maxPlacesPerQuery` (type: `integer`):

Maximum number of business listings to scrape per search query. The bot waits for the query to settle and auto-scrolls until the end of Google Maps results.

## `maxBrowsers` (type: `integer`):

How many browser windows to run simultaneously. Each browser processes a different search query in parallel for maximum speed.

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

How many tabs each browser window opens concurrently to inspect businesses, crawl emails, and send pitch emails.

## `targetStars` (type: `array`):

Review star ratings to consider as negative / hot leads (e.g. 1, 2, 3).

## `maxReviewAgeDays` (type: `integer`):

Only consider reviews posted within this many days (e.g. 28 days = past 4 weeks).

## `minBadReviews` (type: `integer`):

Minimum count of recent bad reviews needed to qualify a business as a hot lead.

## `maxBadReviewsToCapture` (type: `integer`):

Maximum number of recent bad reviews to extract and include in the pitch email.

## `extractSocials` (type: `boolean`):

Crawl the business website for Instagram, Facebook, LinkedIn, Twitter/X, and YouTube links.

## `extractPhones` (type: `boolean`):

Extract official phone numbers from Google Maps and website contact pages.

## `emailFindingDepth` (type: `string`):

Speed vs depth when scanning website for contact emails.

## `enableEmailOutreach` (type: `boolean`):

If enabled, the Actor will dispatch pitch emails to the extracted business emails.

## `dryRun` (type: `boolean`):

If checked, simulates email sending without actually sending emails. Ideal for testing and previewing lead outputs.

## `smtpAccounts` (type: `array`):

List of Gmail or custom SMTP accounts for rotating cold email outreach.

## `senderName` (type: `string`):

Name of the sender displayed in outreach emails.

## `emailSubject` (type: `string`):

Email subject line. Supports {{business\_name}} variable.

## `emailTemplateHtml` (type: `string`):

HTML version of pitch email. Variables: {{business\_name}}, {{sender\_name}}, {{review\_links}}, {{review\_count}}, {{stars\_summary}}.

## `emailTemplateText` (type: `string`):

Plain-text fallback version of pitch email.

## `sendDelaySeconds` (type: `integer`):

Delay in seconds between sending outreach emails to avoid spam filters.

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

Apify Proxy or custom proxies to avoid Google IP blocking / captchas. (Leave useApifyProxy: false when running locally).

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

Language parameter for Google Maps UI ('en' for English, 'de' for German, 'fr' for French, 'es' for Spanish).

## `headless` (type: `boolean`):

Run browser headlessly (required for Apify cloud execution).

## `googleSessionCookies` (type: `string`):

JSON array of Google session cookies to bypass sign-in prompts if needed.

## `webhookUrl` (type: `string`):

Optional HTTP POST webhook URL triggered for every hot lead found.

## Actor input object example

```json
{
  "searchQueries": [
    "Biel/Bienne Moving companies",
    "Thun Car repair shops",
    "Winterthur Property management companies",
    "Aarau Renovation contractors",
    "St. Gallen Cleaning companies",
    "Lugano Auto repair shops",
    "Fribourg Hair salons",
    "Neuchâtel Dentists",
    "Sion Roofing contractors",
    "Chur Real estate agencies"
  ],
  "maxPlacesPerQuery": 200,
  "maxBrowsers": 2,
  "maxConcurrency": 2,
  "targetStars": [
    1,
    2,
    3
  ],
  "maxReviewAgeDays": 28,
  "minBadReviews": 1,
  "maxBadReviewsToCapture": 4,
  "extractSocials": true,
  "extractPhones": true,
  "emailFindingDepth": "deep",
  "enableEmailOutreach": true,
  "dryRun": false,
  "smtpAccounts": [
    {
      "email": "shahji90k@gmail.com",
      "app_password": "hgck udow kgdn fzpo",
      "host": "smtp.gmail.com",
      "port": 587,
      "daily_limit": 200
    }
  ],
  "senderName": "Reputation Management",
  "emailSubject": "Negative Review Removal Service for {{business_name}}",
  "emailTemplateHtml": "<!DOCTYPE html><html><body style=\"font-family: Arial, sans-serif; line-height: 1.6; color: #222; max-width: 600px; padding: 15px;\"><p>Hello <strong>{{business_name}} Team</strong>,</p><p>We have noticed some recent negative reviews on your Google profile. We can completely remove them for you.<br><strong>You only pay AFTER successful removal!</strong></p><p><strong>Review links:</strong></p><ol style=\"padding-left: 20px;\">{{review_links}}</ol><br><p>Best regards,<br><strong>{{sender_name}}</strong></p></body></html>",
  "emailTemplateText": "Hello {{business_name}} Team,\n\nWe have noticed some recent negative reviews on your Google profile. We can completely remove them for you.\nYou only pay AFTER successful removal!\n\nReview links:\n{{review_links}}\n\nBest regards,\n{{sender_name}}",
  "sendDelaySeconds": 3,
  "proxyConfiguration": {
    "useApifyProxy": false
  },
  "language": "en",
  "headless": true
}
```

# Actor output Schema

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

Dataset containing verified leads with recent negative reviews, contact emails, and outreach delivery status.

# 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 = {
    "searchQueries": [
        "Biel/Bienne Moving companies",
        "Thun Car repair shops",
        "Winterthur Property management companies",
        "Aarau Renovation contractors",
        "St. Gallen Cleaning companies",
        "Lugano Auto repair shops",
        "Fribourg Hair salons",
        "Neuchâtel Dentists",
        "Sion Roofing contractors",
        "Chur Real estate agencies"
    ],
    "smtpAccounts": [
        {
            "email": "shahji90k@gmail.com",
            "app_password": "hgck udow kgdn fzpo",
            "host": "smtp.gmail.com",
            "port": 587,
            "daily_limit": 200
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("awake_orchestra_trb/google-maps-review-outreach-actor").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 = {
    "searchQueries": [
        "Biel/Bienne Moving companies",
        "Thun Car repair shops",
        "Winterthur Property management companies",
        "Aarau Renovation contractors",
        "St. Gallen Cleaning companies",
        "Lugano Auto repair shops",
        "Fribourg Hair salons",
        "Neuchâtel Dentists",
        "Sion Roofing contractors",
        "Chur Real estate agencies",
    ],
    "smtpAccounts": [{
            "email": "shahji90k@gmail.com",
            "app_password": "hgck udow kgdn fzpo",
            "host": "smtp.gmail.com",
            "port": 587,
            "daily_limit": 200,
        }],
}

# Run the Actor and wait for it to finish
run = client.actor("awake_orchestra_trb/google-maps-review-outreach-actor").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 '{
  "searchQueries": [
    "Biel/Bienne Moving companies",
    "Thun Car repair shops",
    "Winterthur Property management companies",
    "Aarau Renovation contractors",
    "St. Gallen Cleaning companies",
    "Lugano Auto repair shops",
    "Fribourg Hair salons",
    "Neuchâtel Dentists",
    "Sion Roofing contractors",
    "Chur Real estate agencies"
  ],
  "smtpAccounts": [
    {
      "email": "shahji90k@gmail.com",
      "app_password": "hgck udow kgdn fzpo",
      "host": "smtp.gmail.com",
      "port": 587,
      "daily_limit": 200
    }
  ]
}' |
apify call awake_orchestra_trb/google-maps-review-outreach-actor --silent --output-dataset

```

## MCP server setup

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

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/6dkFw0V0lnvsZ7ssp/builds/EXEdudGdNUeaptdel/openapi.json
