# Google Maps Leads Cleaner and Scorer (`asmalek/google-maps-leads-cleaner`) Actor

Clean, de-duplicate and rank Google Maps Scraper results. Spot businesses with no real website, invalid phones and chains. No AI, no extra fees.

- **URL**: https://apify.com/asmalek/google-maps-leads-cleaner.md
- **Developed by:** [Hack Gammer](https://apify.com/asmalek) (community)
- **Stats:** 2 total users, 1 monthly users, 77.8% 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 Leads Cleaner and Scorer

Turn a messy Google Maps Scraper export into a clean, ranked lead list. No AI, no API keys,
no extra cost. Works on the data you already have.

Useful if you want to remove duplicate and closed businesses, fix phone numbers, and find
local businesses that have no website (for web design agencies and sales teams).

### What it does

1. Removes duplicate businesses (same place ID, or same name and address)
2. Removes permanently closed businesses, flags temporarily closed ones
3. Validates and standardizes phone numbers (E.164 format)
4. Detects "websites" that are not a real site: Facebook, Instagram, Zomato, Justdial, link-in-bio pages, free site builders
5. Flags chains (repeated name, repeated domain, or a well known brand)
6. Scores every lead from 0 to 100 and gives a priority: A, B or C

### How to use (3 steps)

1. **Get your data.** Run [Google Maps Scraper](https://apify.com/compass/crawler-google-places) with your search (for example "hair salon in Austin"). Wait until the run finishes.
2. **Open this Actor and pick your data.** In the first box ("Google Maps Scraper results"), open the list and choose the dataset of your run. You do not need to copy any ID.
3. **Press Start.** Open the **Output** tab to see your ranked leads, or download `leads_ranked.csv` from the Key-value store.

No Google Maps Scraper run in your account? Use your own CSV or JSON file instead:

1. In the box **Or upload a CSV / JSON file**, click and upload your file, and choose where to store it.
2. In the next box, **Storage where you uploaded the file**, pick the same storage. This lets the
   Actor read your file, and only that storage.
3. Press Start.

Upload not working for you? Open your file in Excel or Notepad, copy everything (including the first
row with the column names) and paste it into **Or paste your CSV here**.

Just want to see how it looks? Press Start without choosing anything: the example rows at the
bottom of the form are used.

#### Common questions

**I do not see my run in the list.** Make sure the run finished and belongs to your own account.
Otherwise export the results as CSV from the run, open the file, and paste its text into the CSV box.

**What does the Actor have access to?** It runs with limited permissions. It can only read the one
dataset (or storage) you pick, or the text you paste. It cannot see anything else in your account.

**Does it cost anything?** The Actor itself has no AI and no extra fees. Apify usage for the run
(a few seconds of compute) is shown on your run page.

### Input

| Field | Meaning |
|---|---|
| Google Maps Scraper results | Pick the dataset of your run from the list |
| Upload a CSV / JSON file + Storage | A file you already have, and the storage you uploaded it to |
| Paste your CSV | The text of a CSV export you already have |\n| Example rows | Sample rows used only when nothing else is given (you can paste your own JSON list too) |
| Who are the leads for | `web_agency` (no website is good) or `general` (real website and phone are good) |
| Which leads to return | All, priority A only, or A and B |
| Minimum score for A / B | Defaults 85 and 65 |
| Extra chain brand names | Local franchises to treat as chains |

### Output

- Dataset: one row per lead with `rank`, `score`, `priority`, `title`, `categoryName`, `phone_e164`, `phone_valid`, `website`, `website_domain`, `total_score`, `reviews_count`, address fields, `flags`, `url`, `placeId`
- Key-value store: `leads_ranked.csv` (same data as CSV) and `REPORT` (counts of duplicates, closed places, invalid phones, chains, priorities)

Example row:

```json
{
  "rank": 1,
  "score": 87,
  "priority": "A",
  "title": "Example Hair Studio",
  "phone_e164": "+15125550142",
  "phone_valid": true,
  "website": "",
  "reviews_count": 640,
  "flags": "no website"
}
```

### Scoring (web_agency goal, max 90)

| Rule | Points |
|---|---|
| No website | +40 |
| Social, directory or free-builder website | +30 |
| Valid phone | +20 |
| Reviews (grows with the log of the count, capped) | up to +20 |
| Rating 4.5+ / 4.0+ / 3.5+ | +10 / +7 / +3 |
| Temporarily closed | -30 |
| Chain | -25 |
| No valid phone | score capped at 55 |

For the `general` goal, a valid phone and a real website are worth +30 each.
Priority: A = 85+, B = 65-84, C = below 65. The score is a ranking aid, not a guarantee.

### Limits

- The tool only knows what the Google Maps listing says. A business can have a website that is
  not linked on its listing, so check your best leads before you contact them.
- The list of weak websites and chain brands is not complete. Use **Extra chain brand names** to add more.
- Phone checks depend on the country code in the data.

### Data note

This Actor only processes data you provide. It does not scrape anything. You are responsible for
having a legal basis to use business contact data under the privacy laws and terms that apply to you.

# Actor input Schema

## `datasetId` (type: `string`):

Open the list and choose the dataset of your Google Maps Scraper run. Not in the list? Upload your CSV file below.

## `fileUrl` (type: `string`):

Click, upload your Google Maps export (CSV or JSON), and choose where to store it. Then pick the same storage in the next box. You can also paste a public direct link here.

## `sourceStoreId` (type: `string`):

Pick the same storage you used for the upload above. This gives the Actor permission to read your file, and only that storage.

## `csvText` (type: `string`):

Backup option: open your export in Excel or Notepad, copy everything (including the first row with the column names) and paste it here. Used only when no dataset and no file are given.

## `goal` (type: `string`):

Web design agency: businesses without a real website come first. General sales: businesses with a valid phone, a real website and good reviews come first.

## `keepOnly` (type: `string`):

Return everything, only the best leads (priority A), or priority A and B.

## `minScoreA` (type: `integer`):

Leads with this score or more get priority A (best).

## `minScoreB` (type: `integer`):

Leads with this score or more (but below A) get priority B.

## `extraChainBrands` (type: `array`):

Brand names to treat as chains, for example a local franchise. Chains get a lower score.

## `items` (type: `array`):

A few sample rows so you can press Start and see how the result looks. You can also paste your own rows here as a JSON list.

## Actor input object example

```json
{
  "goal": "web_agency",
  "keepOnly": "all",
  "minScoreA": 85,
  "minScoreB": 65,
  "items": [
    {
      "title": "Sunrise Hair Studio",
      "categoryName": "Hair salon",
      "address": "12 Park Street",
      "city": "Austin",
      "countryCode": "US",
      "website": null,
      "phone": "(512) 555-0142",
      "phoneUnformatted": "+15125550142",
      "totalScore": 4.6,
      "reviewsCount": 640,
      "claimThisBusiness": false,
      "permanentlyClosed": false,
      "temporarilyClosed": false,
      "placeId": "example1",
      "url": "https://maps.example/1"
    },
    {
      "title": "Blue Door Dental",
      "categoryName": "Dentist",
      "address": "40 Elm Avenue",
      "city": "Austin",
      "countryCode": "US",
      "website": "https://www.facebook.com/bluedoordental",
      "phone": "(512) 555-0177",
      "phoneUnformatted": "+15125550177",
      "totalScore": 4.8,
      "reviewsCount": 64,
      "claimThisBusiness": false,
      "permanentlyClosed": false,
      "temporarilyClosed": false,
      "placeId": "example2",
      "url": "https://maps.example/2"
    },
    {
      "title": "Green Leaf Cafe",
      "categoryName": "Cafe",
      "address": "9 Oak Road",
      "city": "Austin",
      "countryCode": "US",
      "website": "https://greenleafcafe.com",
      "phone": "(512) 555-0190",
      "phoneUnformatted": "+15125550190",
      "totalScore": 4.4,
      "reviewsCount": 300,
      "claimThisBusiness": false,
      "permanentlyClosed": false,
      "temporarilyClosed": false,
      "placeId": "example3",
      "url": "https://maps.example/3"
    },
    {
      "title": "Old Mill Pizza",
      "categoryName": "Pizza restaurant",
      "address": "5 Mill Lane",
      "city": "Austin",
      "countryCode": "US",
      "website": null,
      "phone": "(512) 555-0111",
      "phoneUnformatted": "+15125550111",
      "totalScore": 4.1,
      "reviewsCount": 80,
      "claimThisBusiness": false,
      "permanentlyClosed": true,
      "temporarilyClosed": false,
      "placeId": "example4",
      "url": "https://maps.example/4"
    }
  ]
}
```

# Actor output Schema

## `leads` (type: `string`):

Cleaned leads, best first, with score, priority and flags.

## `csv` (type: `string`):

The same ranked leads as a CSV file you can open in Excel.

## `report` (type: `string`):

Counts of duplicates, closed places, invalid phones, chains and priorities.

# 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 = {
    "items": [
        {
            "title": "Sunrise Hair Studio",
            "categoryName": "Hair salon",
            "address": "12 Park Street",
            "city": "Austin",
            "countryCode": "US",
            "website": null,
            "phone": "(512) 555-0142",
            "phoneUnformatted": "+15125550142",
            "totalScore": 4.6,
            "reviewsCount": 640,
            "claimThisBusiness": false,
            "permanentlyClosed": false,
            "temporarilyClosed": false,
            "placeId": "example1",
            "url": "https://maps.example/1"
        },
        {
            "title": "Blue Door Dental",
            "categoryName": "Dentist",
            "address": "40 Elm Avenue",
            "city": "Austin",
            "countryCode": "US",
            "website": "https://www.facebook.com/bluedoordental",
            "phone": "(512) 555-0177",
            "phoneUnformatted": "+15125550177",
            "totalScore": 4.8,
            "reviewsCount": 64,
            "claimThisBusiness": false,
            "permanentlyClosed": false,
            "temporarilyClosed": false,
            "placeId": "example2",
            "url": "https://maps.example/2"
        },
        {
            "title": "Green Leaf Cafe",
            "categoryName": "Cafe",
            "address": "9 Oak Road",
            "city": "Austin",
            "countryCode": "US",
            "website": "https://greenleafcafe.com",
            "phone": "(512) 555-0190",
            "phoneUnformatted": "+15125550190",
            "totalScore": 4.4,
            "reviewsCount": 300,
            "claimThisBusiness": false,
            "permanentlyClosed": false,
            "temporarilyClosed": false,
            "placeId": "example3",
            "url": "https://maps.example/3"
        },
        {
            "title": "Old Mill Pizza",
            "categoryName": "Pizza restaurant",
            "address": "5 Mill Lane",
            "city": "Austin",
            "countryCode": "US",
            "website": null,
            "phone": "(512) 555-0111",
            "phoneUnformatted": "+15125550111",
            "totalScore": 4.1,
            "reviewsCount": 80,
            "claimThisBusiness": false,
            "permanentlyClosed": true,
            "temporarilyClosed": false,
            "placeId": "example4",
            "url": "https://maps.example/4"
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("asmalek/google-maps-leads-cleaner").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 = { "items": [
        {
            "title": "Sunrise Hair Studio",
            "categoryName": "Hair salon",
            "address": "12 Park Street",
            "city": "Austin",
            "countryCode": "US",
            "website": None,
            "phone": "(512) 555-0142",
            "phoneUnformatted": "+15125550142",
            "totalScore": 4.6,
            "reviewsCount": 640,
            "claimThisBusiness": False,
            "permanentlyClosed": False,
            "temporarilyClosed": False,
            "placeId": "example1",
            "url": "https://maps.example/1",
        },
        {
            "title": "Blue Door Dental",
            "categoryName": "Dentist",
            "address": "40 Elm Avenue",
            "city": "Austin",
            "countryCode": "US",
            "website": "https://www.facebook.com/bluedoordental",
            "phone": "(512) 555-0177",
            "phoneUnformatted": "+15125550177",
            "totalScore": 4.8,
            "reviewsCount": 64,
            "claimThisBusiness": False,
            "permanentlyClosed": False,
            "temporarilyClosed": False,
            "placeId": "example2",
            "url": "https://maps.example/2",
        },
        {
            "title": "Green Leaf Cafe",
            "categoryName": "Cafe",
            "address": "9 Oak Road",
            "city": "Austin",
            "countryCode": "US",
            "website": "https://greenleafcafe.com",
            "phone": "(512) 555-0190",
            "phoneUnformatted": "+15125550190",
            "totalScore": 4.4,
            "reviewsCount": 300,
            "claimThisBusiness": False,
            "permanentlyClosed": False,
            "temporarilyClosed": False,
            "placeId": "example3",
            "url": "https://maps.example/3",
        },
        {
            "title": "Old Mill Pizza",
            "categoryName": "Pizza restaurant",
            "address": "5 Mill Lane",
            "city": "Austin",
            "countryCode": "US",
            "website": None,
            "phone": "(512) 555-0111",
            "phoneUnformatted": "+15125550111",
            "totalScore": 4.1,
            "reviewsCount": 80,
            "claimThisBusiness": False,
            "permanentlyClosed": True,
            "temporarilyClosed": False,
            "placeId": "example4",
            "url": "https://maps.example/4",
        },
    ] }

# Run the Actor and wait for it to finish
run = client.actor("asmalek/google-maps-leads-cleaner").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 '{
  "items": [
    {
      "title": "Sunrise Hair Studio",
      "categoryName": "Hair salon",
      "address": "12 Park Street",
      "city": "Austin",
      "countryCode": "US",
      "website": null,
      "phone": "(512) 555-0142",
      "phoneUnformatted": "+15125550142",
      "totalScore": 4.6,
      "reviewsCount": 640,
      "claimThisBusiness": false,
      "permanentlyClosed": false,
      "temporarilyClosed": false,
      "placeId": "example1",
      "url": "https://maps.example/1"
    },
    {
      "title": "Blue Door Dental",
      "categoryName": "Dentist",
      "address": "40 Elm Avenue",
      "city": "Austin",
      "countryCode": "US",
      "website": "https://www.facebook.com/bluedoordental",
      "phone": "(512) 555-0177",
      "phoneUnformatted": "+15125550177",
      "totalScore": 4.8,
      "reviewsCount": 64,
      "claimThisBusiness": false,
      "permanentlyClosed": false,
      "temporarilyClosed": false,
      "placeId": "example2",
      "url": "https://maps.example/2"
    },
    {
      "title": "Green Leaf Cafe",
      "categoryName": "Cafe",
      "address": "9 Oak Road",
      "city": "Austin",
      "countryCode": "US",
      "website": "https://greenleafcafe.com",
      "phone": "(512) 555-0190",
      "phoneUnformatted": "+15125550190",
      "totalScore": 4.4,
      "reviewsCount": 300,
      "claimThisBusiness": false,
      "permanentlyClosed": false,
      "temporarilyClosed": false,
      "placeId": "example3",
      "url": "https://maps.example/3"
    },
    {
      "title": "Old Mill Pizza",
      "categoryName": "Pizza restaurant",
      "address": "5 Mill Lane",
      "city": "Austin",
      "countryCode": "US",
      "website": null,
      "phone": "(512) 555-0111",
      "phoneUnformatted": "+15125550111",
      "totalScore": 4.1,
      "reviewsCount": 80,
      "claimThisBusiness": false,
      "permanentlyClosed": true,
      "temporarilyClosed": false,
      "placeId": "example4",
      "url": "https://maps.example/4"
    }
  ]
}' |
apify call asmalek/google-maps-leads-cleaner --silent --output-dataset

```

## MCP server setup

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

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/X2h9LcgYoQ6Loh9ce/builds/VNstP5RjrzrOugxsV/openapi.json
