# Google Maps Lead Cleaner – Dedupe, Email MX & Phone Format (`chris_elder/google-maps-lead-cleaner`) Actor

Clean Google Maps and scraped lead lists before CRM import: dedupe, E.164 phones, email syntax + domain MX check, role/free-mail/disposable flags, website check and a lead score. No SMTP probing. $0.60 per 1,000 leads; duplicates and dropped rows are free.

- **URL**: https://apify.com/chris_elder/google-maps-lead-cleaner.md
- **Developed by:** [Chris Elder](https://apify.com/chris_elder) (community)
- **Categories:** Lead generation, Automation, Integrations
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.40 / 1,000 cleaned 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?

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 Lead Cleaner – Dedupe, Email MX & Phone Format

**Google Maps Lead Cleaner** turns a raw Google Maps (or any scraped) lead list into a clean, CRM-ready table. It removes duplicates, formats every phone number to E.164, splits and checks emails (syntax + domain MX records), flags role, free-mail and disposable addresses, checks each website is up, and gives every lead a 0–100 quality score with a plain list of issues.

Store page: [apify.com/chris_elder/google-maps-lead-cleaner](https://apify.com/chris_elder/google-maps-lead-cleaner)

✅ Pay per result: **$0.60 per 1,000 cleaned leads** · 🆓 Merged duplicates, dropped rows and input errors are never charged · 🔑 No API key, no sign-ups

[▶ Try it with one click](#how-to-use-it) · [📄 Real example output](#output-example-from-a-real-public-run) · [💲 Pricing](#how-much-does-it-cost-to-clean-a-google-maps-lead-list) · [🔗 Run it after your Google Maps scraper](#use-it-as-an-integration-after-a-google-maps-scraper-run) · [🔌 API & integrations](#integrations-api-ai-agents-make-zapier) · [❓ FAQ](#faq) · [🛟 Support](#support-reliability-and-feature-requests)

### What does Google Maps Lead Cleaner do?

- 🧹 **Removes duplicates**: by Google `placeId`, else website (domain + page path), else phone, else name + address. Contacts from the duplicates are merged into the row it keeps, so nothing is lost.
- 📞 **Formats phones to E.164** (`+442077308191`) using each row's country or your default country. You also get national format, phone type (mobile, fixed line, toll-free) and a list of numbers that failed validation. Cells holding several numbers are split.
- 📧 **Cleans emails**: splits lists, lowercases, fixes `name @domain.com` typos, strips `mailto:` and checks the syntax. Junk such as `logo@2x.png` or `user@domain.com` is flagged.
- 🌐 **Checks the email domain's MX records** once per domain (cached), so you know the domain is set up to receive mail.
- 🏷️ **Flags role addresses** (info@, sales@, reservations@), **free-mail** (gmail, outlook…) and **disposable** domains, plus emails whose domain differs from the website.
- 🔎 **Checks websites** with one light request each: alive or not, HTTP status, final URL after redirects, and whether it redirects to another domain.
- ⭐ **Scores each lead 0–100** (high / medium / low) and lists every problem in `issues`, so you can sort, filter or drop weak rows.
- 🔗 **Plugs straight into your scraper**: reads an Apify dataset, pasted JSON or a CSV link, and runs automatically after a Google Maps scraper run as an integration.

**What it does not do:** it does not open an SMTP connection or probe mailboxes, so it never claims an address is "verified deliverable". `valid-mx` means the syntax is valid and the domain publishes MX records. Mailbox-level checks are unreliable from cloud IPs and can hurt your sender reputation, so they are deliberately left out.

### Use cases

- **Agencies and freelancers selling lead lists**: send clients a deduplicated, consistently formatted sheet instead of a raw scrape.
- **Sales teams importing into HubSpot, Pipedrive or Salesforce**: E.164 phones and one best email per row import cleanly, without duplicate contacts.
- **Cold-email operators**: drop addresses whose domain has no MX before they bounce, and send only the cleaned `primaryEmail` list to a paid mailbox verifier if you use one.
- **Local-business researchers**: find closed or broken websites (`website-unreachable`, `website-page-not-found`) and listings with no way to contact them.
- **Data teams**: one consistent schema, whichever Google Maps or contact-extractor Actor produced the data.

### How to use it

1. Click **Try for free**. The prefill holds 8 real public hotel listings (from OpenStreetMap, shaped like Google Maps scraper output). They include one duplicate, an email typed with a space before the @, and a domain without MX. A run takes a few seconds and costs about **$0.004**.
2. To clean your own data, put your Google Maps scraper run's **Dataset ID** in `datasetId` (Storage tab of the run). You can also paste rows into `items`, or give a public CSV link in `csvUrl`.
3. Set `defaultCountry` (e.g. `US`) if your rows have no `countryCode` and phones are in national format.
4. Download the cleaned leads as CSV, Excel or JSON from the run's **Storage** tab. Pick the **CRM import (flat)** view for a narrow sheet. `RUN_SUMMARY` in the key-value store has the totals.

#### Input example

```json
{
  "datasetId": "YOUR_GOOGLE_MAPS_RUN_DATASET_ID",
  "defaultCountry": "GB",
  "dedupeMode": "auto",
  "checkMx": true,
  "checkWebsites": true,
  "dropRowsWithoutContact": false
}
```

Other options:

- `items`: paste a JSON array instead of `datasetId`.
- `csvUrl`: a CSV or JSON file link. Apify exports with `emails/0` style columns are understood.
- `fieldMapping`: e.g. `{"name": "Company", "phone": "Tel"}`, only needed for unusual column names.
- `dedupeMode`:
  - `auto`: one row per place (the default).
  - `company`: one row per website domain, so chain branches are merged.
  - `none`: keep every row.
- `minLeadQuality`: drop rows below this score.
- `passthroughFields`: extra source fields to keep. The default keeps the category, rating, review count and Maps URL.
- `includeEmailDetails`, `includeOriginal`, `websiteTimeoutSecs`, `maxRows`.

**Fields detected automatically** include those from compass/crawler-google-places (`title`, `website`, `phone`, `phoneUnformatted`, `emails`, `address`, `city`, `countryCode`, `placeId`), from Google Maps contact-detail extractors (`emails`, `phones`), from [Website Contact Extractor](https://apify.com/chris_elder/website-contact-extractor), and common CSV headers (`Name`, `Company`, `Email`, `Phone Number`…).

#### Output example (from a real public run)

Input: 149 real hotel listings from OpenStreetMap (© OpenStreetMap contributors, ODbL) in London and Manhattan, shaped like a Google Maps scraper export. Two overlapping London searches give real duplicates, the way overlapping Maps searches do. Named-person and personal webmail addresses were removed from the sample before the run. The run read **149 rows and returned 133 cleaned leads** in about 25 seconds at 512 MB:

- **16 duplicates** merged.
- **127 emails** with MX records.
- **6 domains with no MX** (A record only).
- **128 phones** in E.164.
- **10 websites** down or broken.

Public data (no login needed): [input dataset](https://api.apify.com/v2/datasets/zN55yLtNUalc1U4QD/items?format=json\&clean=1) · [cleaned leads JSON](https://api.apify.com/v2/datasets/SmLomWKVjIARkfxb7/items?format=json\&clean=1) · [cleaned leads CSV](https://api.apify.com/v2/datasets/SmLomWKVjIARkfxb7/items?format=csv\&clean=1) · [RUN_SUMMARY](https://api.apify.com/v2/key-value-stores/IH8Qqg59oCaxVdCzU/records/RUN_SUMMARY) · [console run](https://console.apify.com/view/runs/va006JUIaRCfIBzMs)

First dataset item (the second London search had the same hotel again, so `duplicatesMerged` is 1):

```json
{
  "name": "DoubleTree by Hilton Hotel London - Marble Arch",
  "website": "https://www.hilton.com/en/hotels/lonmadi-doubletree-london-marble-arch/",
  "websiteDomain": "hilton.com",
  "primaryEmail": "doubletreelondonmarblearch.reservations@hilton.com",
  "primaryEmailStatus": "valid-mx",
  "primaryEmailIsRole": true,
  "emails": ["doubletreelondonmarblearch.reservations@hilton.com"],
  "phone": "+442079352361",
  "phoneNational": "020 7935 2361",
  "phoneType": "FIXED_LINE",
  "address": "4 Bryanston Street, London, W1H 7BY",
  "countryCode": "GB",
  "websiteAlive": true,
  "websiteStatus": 200,
  "leadQuality": 95,
  "leadQualityLabel": "high",
  "issues": ["email-role-address-only"],
  "duplicatesMerged": 1,
  "emailDetails": [
    { "email": "doubletreelondonmarblearch.reservations@hilton.com", "status": "valid-mx", "role": true, "freeMail": false, "disposable": false, "matchesWebsite": true, "mxHosts": ["hilton-com.mail.protection.outlook.com"] }
  ],
  "categoryName": "Hotel",
  ...
}
```

The source website URL carried a `?WT.mc_id=…` tracking parameter, which was removed. Two more real rows from the same run:

- `"NYCTS_HOTEL @hilton.com"` (typed with a space) became `nycts_hotel@hilton.com`, status `valid-mx`, issue `email-repaired`.
- `reservations@thetrafalgar.com`: the domain publishes no MX record, so the status is `no-mx-a-record`, issues are `no-usable-email` and `email-domain-a-record-only`, and the score is 60 (medium).

| Field | Meaning |
|---|---|
| `primaryEmail` / `primaryEmailStatus` | Best email: one with MX, preferring a named person, the website's own domain, and not free-mail |
| `emails` | All usable emails (syntax OK, domain has MX, not disposable or junk) |
| `emailDetails[].status` | `valid-mx`, `no-mx`, `no-mx-a-record`, `invalid-syntax`, `disposable`, `placeholder`, `unknown` (DNS failed), `not-checked` (MX check off) |
| `phone` / `phones` | E.164 primary and all valid numbers; `invalidPhones` holds the numbers that failed |
| `websiteAlive` / `websiteStatus` / `websiteFinalUrl` | Website check result; sites that block bots (401/403/429) count as alive |
| `leadQuality` / `leadQualityLabel` | 0–100 score: usable email 40 (+5 named person, +5 same domain as website), valid phone 25, live website 15, address 5, name 5 |
| `issues` | Everything worth knowing, e.g. `no-contact-method`, `email-domain-no-mx`, `phone-invalid`, `website-unreachable`, `website-is-social-or-directory` |
| `duplicatesMerged` / `mergedRowNumbers` | How many input rows were folded into this one, and which |

📊 **Want these results in Google Sheets?** Send them straight to a Sheet with [Google Sheets Sync](https://apify.com/chris_elder/google-sheets-sync). Run it after this Actor, or add it on this Actor's **Integrations** tab so every run exports automatically.

### How much does it cost to clean a Google Maps lead list?

| Event | Free / Bronze | Silver | Gold+ |
|---|---|---|---|
| Actor start (once per run, per GB of memory; default 512 MB = 1) | $0.00005 | $0.00005 | $0.00005 |
| Cleaned lead (one row written to the dataset; MX, phone and website checks included) | **$0.0006** | $0.0005 | $0.0004 |

**Never charged:** duplicates merged into another row, rows removed by `dropRowsWithoutContact` or `minLeadQuality`, and runs that stop on an input error. Apify's automatic $0.00005 start event still applies to those runs.

| Job | Arithmetic | Approx. cost |
|---|---|---|
| One-click prefill (8 rows → 7 leads) | 7 × $0.0006 + $0.00005 | **$0.0043** |
| Real example above (149 rows → 133 leads) | 133 × $0.0006 + $0.00005 | **$0.080** |
| Typical Maps export: 1,000 rows, ~10% duplicates | 900 × $0.0006 + $0.00005 | **$0.54** |
| Large job on Gold: 10,000 rows → 9,000 leads, rows without contact dropped first | 9,000 × $0.0004 + $0.00005 | **$3.60** or less |

Set **Max cost per run** to cap spend. When the cap is reached the Actor stops writing rows and sets `budgetHit: true` in `RUN_SUMMARY`. For comparison, mailbox-level email verifiers on the Store charge roughly $0.80–$1.50 per 1,000 emails at Bronze for the email alone. Clean and dedupe here first, and you send them fewer addresses.

### Use it as an integration after a Google Maps scraper run

Clean every scrape automatically, with no code:

1. Open your Google Maps scraper (or a saved task of it) → **Integrations** tab → **Add integration** → search **Google Maps Lead Cleaner**.
2. **Start when:** *Run succeeded*.
3. **Input:** set `datasetId` to `{{resource.defaultDatasetId}}`. Apify swaps in the triggering run's dataset and gives this Actor read access to that dataset only. Add `defaultCountry` and any other options.
4. Click **Test** with a past run, then save.

```json
{
  "datasetId": "{{resource.defaultDatasetId}}",
  "defaultCountry": "US",
  "dedupeMode": "auto",
  "dropRowsWithoutContact": true
}
```

`RUN_SUMMARY.triggeredBy` records which run started the clean-up. This path was tested end to end: a real Apify integration webhook started this Actor after another Actor's run finished, and the Actor read and cleaned that run's 30 rows. The Actor uses Apify's **limited permissions**, so it can read only the dataset passed in `datasetId`. Chain [Google Sheets Sync](https://apify.com/chris_elder/google-sheets-sync) after it to land the clean list in a Sheet.

### Integrations (API, AI agents, Make, Zapier)

```bash
curl -X POST "https://api.apify.com/v2/acts/chris_elder~google-maps-lead-cleaner/run-sync-get-dataset-items?token=YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"datasetId":"YOUR_DATASET_ID","defaultCountry":"US","dropRowsWithoutContact":true}'
```

Python (`pip install apify-client`):

```python
from apify_client import ApifyClient
client = ApifyClient("YOUR_TOKEN")
maps_run = client.actor("compass/crawler-google-places").call(run_input={"searchStringsArray": ["dentist"], "locationQuery": "Leeds, UK", "maxCrawledPlacesPerSearch": 50})
run = client.actor("chris_elder/google-maps-lead-cleaner").call(run_input={"datasetId": maps_run["defaultDatasetId"], "defaultCountry": "GB"})
for lead in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(lead["name"], lead["primaryEmail"], lead["phone"], lead["leadQuality"])
```

- MCP: `https://mcp.apify.com/?tools=chris_elder/google-maps-lead-cleaner`
- Make / Zapier / n8n: module **Run Actor** → `chris_elder/google-maps-lead-cleaner`, passing the previous run's `defaultDatasetId` as `datasetId`
- Schedules: run your scraper on a schedule and add this Actor as its integration ([see above](#use-it-as-an-integration-after-a-google-maps-scraper-run))

### Limits and good to know

- **No mailbox verification.** `valid-mx` means valid syntax plus a domain with MX records. The mailbox itself may still not exist. Catch-all domains accept everything, so nobody can fully verify them.
- `no-mx-a-record` means the domain has no MX record but has a web server address. Some mail servers will still try to deliver there, but treat these addresses as risky.
- Role addresses (info@, sales@) are flagged, not removed. For local businesses they are often the only public address.
- Disposable domains come from the community-maintained CC0 list [disposable-email-domains](https://github.com/disposable-email-domains/disposable-email-domains). A mailbox on the business's own website domain is never flagged as disposable.
- The website check sends one HEAD request per unique site (GET if HEAD is refused), 10 s timeout, 10 at a time. Pages behind bot protection return 401/403/429; they count as alive and get the `website-blocks-bots` issue.
- `auto` dedupe keeps chain branches apart when each has its own page on a shared website (e.g. hilton.com/…/branch). Use `company` for one row per website domain. Social and directory links (facebook.com, linktr.ee, yelp…) are never merged by domain.
- Speed from the real run above: 149 rows in about 25 s at 512 MB, including 111 MX lookups and 133 website checks. Turning off `checkWebsites` makes large lists much faster.
- Default `maxRows` is 100,000 per run. For bigger lists raise memory, or run per city or keyword.

### Is it legal?

The Actor processes only data **you** provide. It makes public DNS lookups for email domains and one light request to each business website. It does not scrape Google, log in anywhere or contact any mailbox. Lead lists can contain personal data (for example a named person's work email). You are responsible for having a lawful basis under GDPR / UK GDPR, and for following PECR, CAN-SPAM and similar rules when you contact anyone. See Apify's [guide to the legality of web scraping](https://blog.apify.com/is-web-scraping-legal/).

### FAQ

**Does this verify that an email address exists?**
No. It checks syntax, that the domain has MX records, and flags role, free-mail, disposable and junk addresses. It does not probe mailboxes over SMTP and never labels an address "verified deliverable". If you need mailbox-level checks, run a dedicated verifier on the cleaned `primaryEmail` column afterwards.

**Which Google Maps scrapers does it work with?**
Any that write to an Apify dataset or export CSV/JSON. Field names from compass/crawler-google-places and the common "Google Maps with contact details" extractors are detected automatically. For anything else, use `fieldMapping`.

**Why were two branches of the same chain kept as separate rows?**
In `auto` mode the key is the Google `placeId`, or the full website page (domain + path), so different locations stay separate. Choose `dedupeMode: "company"` for one row per website domain.

**My phones are in national format with no country column. What do I do?**
Set `defaultCountry` (e.g. `US`, `GB`, `AU`). Numbers that already start with `+` or `00` are read without it.

**Am I charged for duplicates or rows I drop?**
No. Only rows written to the dataset are charged (`lead-cleaned`). Merged duplicates, rows removed by `dropRowsWithoutContact` or `minLeadQuality`, and input errors are free.

**Can I get a narrow CSV for my CRM?**
Yes. Choose the **CRM import (flat)** view in the Storage tab, or set `includeEmailDetails: false`.

### Support, reliability and feature requests

- Failed items are free.
- Every release is tested on real public inputs; the one-click example works.
- Issues answered within 2 business days via the Actor **Issues** tab.
- Breaking input or price changes are announced in this README first.

Built by GPT Shop Ltd with AI-assisted development; every release is reviewed and tested. Tested on 149 real OpenStreetMap hotel listings (London and Manhattan) read through `datasetId`, the same data through `csvUrl`, a real Apify integration webhook run, input-error runs (no input, missing dataset, HTML instead of CSV), and 18 unit tests (dedupe, phone, email, MX and website logic).

### More tools from GPT Shop Ltd

- [Website Contact Extractor](https://apify.com/chris_elder/website-contact-extractor): find emails, phones and social links on company websites, then clean them here
- [Google Sheets Sync](https://apify.com/chris_elder/google-sheets-sync): send the cleaned leads to a Google Sheet
- [Dataset Transformer](https://apify.com/chris_elder/dataset-transformer): filter, rename, flatten and convert datasets
- [Bulk File Downloader](https://apify.com/chris_elder/bulk-file-downloader)
- All tools: [apify.com/chris_elder](https://apify.com/chris_elder)

# Actor input Schema

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

The dataset to clean, e.g. the default dataset of your Google Maps scraper run (Storage tab → Dataset ID). Using this Actor as an integration after a scraper run? Set this to {{resource.defaultDatasetId}} – Apify fills in the triggering run's dataset and grants this Actor read access to it. Use this OR items OR csvUrl.

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

Paste rows directly instead of datasetId. The prefill is 8 real public hotel listings from OpenStreetMap shaped like Google Maps scraper output (includes one duplicate, a mistyped email and a domain without MX).

## `csvUrl` (type: `string`):

Public link to a CSV (comma, semicolon or tab) or a JSON array, e.g. a Google Sheet published as CSV. Used when datasetId and items are empty.

## `fieldMapping` (type: `object`):

Only needed when your column names are unusual. Keys: name, website, phone, email, address, street, city, postalCode, state, countryCode, placeId. Values: your column name (dot paths like contact.email work). Example: {"name": "Company", "phone": "Tel"}. Common Google Maps scraper and contact-extractor fields are detected automatically.

## `defaultCountry` (type: `string`):

2-letter ISO code (GB, US, DE, AU…) used to read national-format numbers like 020 7730 8191 when a row has no countryCode. Rows with countryCode use their own.

## `dedupeMode` (type: `string`):

auto = one row per place: Google placeId, else website (domain + page path), else phone, else name + address. company = one row per company website domain (merges chain branches). none = keep every row. Merged duplicates are free.

## `checkMx` (type: `boolean`):

Look up DNS MX records once per email domain (cached). Included in the price. This checks the domain can receive mail, not that the mailbox exists (no SMTP probing).

## `checkWebsites` (type: `boolean`):

One HEAD request per unique website (GET fallback), following redirects. Adds status, final URL and alive flag. Included in the price.

## `websiteTimeoutSecs` (type: `integer`):

Per website. 2–60.

## `dropRowsWithoutContact` (type: `boolean`):

Remove rows that have neither a usable email (domain has MX) nor a valid phone. Dropped rows are not charged.

## `minLeadQuality` (type: `integer`):

Drop rows scoring below this. 0 keeps everything. Dropped rows are not charged.

## `passthroughFields` (type: `array`):

Source fields copied to the output unchanged (dot paths allowed). Default keeps the Google Maps category, rating, review count and Maps URL.

## `includeEmailDetails` (type: `boolean`):

Adds emailDetails (status, role, free-mail, disposable, MX hosts) for every email found. Turn off for a narrower CSV.

## `includeOriginal` (type: `boolean`):

Adds the full source row under original.

## `maxRows` (type: `integer`):

Cap on input rows read (protects cost). Only cleaned rows written to the dataset are charged.

## Actor input object example

```json
{
  "items": [
    {
      "title": "Lime Tree Hotel",
      "categoryName": "Hotel",
      "website": "https://www.limetreehotel.co.uk/",
      "phone": "+44 20 7730 8191",
      "emails": [
        "info@limetreehotel.co.uk"
      ],
      "address": "135-137 Ebury Street, London, SW1W 9QU",
      "city": "London",
      "countryCode": "GB"
    },
    {
      "title": "The Warwick by Nestor",
      "categoryName": "Hotel",
      "website": "https://nestorstay.com/properties/the-warwick-by-nestor/",
      "phone": "+44 20 3868 6192",
      "emails": [
        "guest@nestorstay.com"
      ],
      "address": "94 Warwick Way, London, SW1V 1SB",
      "city": "London",
      "countryCode": "GB"
    },
    {
      "title": "The Warwick by Nestor",
      "categoryName": "Hotel",
      "website": "https://nestorstay.com/properties/the-warwick-by-nestor/",
      "phone": "+44 20 3868 6192",
      "emails": [
        "guest@nestorstay.com"
      ],
      "address": "94 Warwick Way, London, SW1V 1SB",
      "city": "London",
      "countryCode": "GB"
    },
    {
      "title": "The Trafalgar St. James London, Curio Collection by Hilton",
      "categoryName": "Hotel",
      "website": "https://www.hilton.com/en/hotels/lontsqq-the-trafalgar-st-james-london/",
      "phone": "+44 20 7870 2900",
      "emails": [
        "reservations@thetrafalgar.com"
      ],
      "address": "2 Spring Gardens, London, SW1A 2TS",
      "city": "London",
      "countryCode": "GB"
    },
    {
      "title": "Hilton",
      "categoryName": "Hotel",
      "website": "https://www.hilton.com/en/hotels/nyctshh-hilton-times-square/",
      "phone": "+1 212-913-9488",
      "emails": [
        "NYCTS_HOTEL @hilton.com"
      ],
      "address": "234 West 42nd Street, New York, 10036",
      "city": "New York",
      "countryCode": "US"
    },
    {
      "title": "Pod 39",
      "categoryName": "Hotel",
      "website": "https://www.thepodhotel.com/pod-39/",
      "phone": "+1-844-763-7666;+1-212-865-5700",
      "emails": [
        "info@podhotel39.com"
      ],
      "address": "145 East 39th Street, New York, 10016",
      "city": "New York",
      "countryCode": "US"
    },
    {
      "title": "Somerset Hotel",
      "categoryName": "Hotel",
      "website": "https://somersethotellondon.com",
      "phone": "+44 20 7723 0741",
      "emails": [
        "reservations@somersethotel.net"
      ],
      "address": "6 Dorset Square, London, NW1 6QA",
      "city": "London",
      "countryCode": "GB"
    },
    {
      "title": "Mandarin Oriental",
      "categoryName": "Hotel",
      "website": "https://www.mandarinoriental.com/en/new-york/manhattan",
      "phone": "+1-212-805-8800",
      "emails": [
        "MONYC-RESERVATIONS@MOHG.COM"
      ],
      "address": "80 Columbus Circle, New York, 10023",
      "city": "New York",
      "countryCode": "US"
    }
  ],
  "defaultCountry": "GB",
  "dedupeMode": "auto",
  "checkMx": true,
  "checkWebsites": true,
  "websiteTimeoutSecs": 10,
  "dropRowsWithoutContact": false,
  "minLeadQuality": 0,
  "passthroughFields": [
    "categoryName",
    "totalScore",
    "reviewsCount",
    "url"
  ],
  "includeEmailDetails": true,
  "includeOriginal": false,
  "maxRows": 100000
}
```

# Actor output Schema

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

No description

## `summary` (type: `string`):

No description

# 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": "Lime Tree Hotel",
            "categoryName": "Hotel",
            "website": "https://www.limetreehotel.co.uk/",
            "phone": "+44 20 7730 8191",
            "emails": [
                "info@limetreehotel.co.uk"
            ],
            "address": "135-137 Ebury Street, London, SW1W 9QU",
            "city": "London",
            "countryCode": "GB"
        },
        {
            "title": "The Warwick by Nestor",
            "categoryName": "Hotel",
            "website": "https://nestorstay.com/properties/the-warwick-by-nestor/",
            "phone": "+44 20 3868 6192",
            "emails": [
                "guest@nestorstay.com"
            ],
            "address": "94 Warwick Way, London, SW1V 1SB",
            "city": "London",
            "countryCode": "GB"
        },
        {
            "title": "The Warwick by Nestor",
            "categoryName": "Hotel",
            "website": "https://nestorstay.com/properties/the-warwick-by-nestor/",
            "phone": "+44 20 3868 6192",
            "emails": [
                "guest@nestorstay.com"
            ],
            "address": "94 Warwick Way, London, SW1V 1SB",
            "city": "London",
            "countryCode": "GB"
        },
        {
            "title": "The Trafalgar St. James London, Curio Collection by Hilton",
            "categoryName": "Hotel",
            "website": "https://www.hilton.com/en/hotels/lontsqq-the-trafalgar-st-james-london/",
            "phone": "+44 20 7870 2900",
            "emails": [
                "reservations@thetrafalgar.com"
            ],
            "address": "2 Spring Gardens, London, SW1A 2TS",
            "city": "London",
            "countryCode": "GB"
        },
        {
            "title": "Hilton",
            "categoryName": "Hotel",
            "website": "https://www.hilton.com/en/hotels/nyctshh-hilton-times-square/",
            "phone": "+1 212-913-9488",
            "emails": [
                "NYCTS_HOTEL @hilton.com"
            ],
            "address": "234 West 42nd Street, New York, 10036",
            "city": "New York",
            "countryCode": "US"
        },
        {
            "title": "Pod 39",
            "categoryName": "Hotel",
            "website": "https://www.thepodhotel.com/pod-39/",
            "phone": "+1-844-763-7666;+1-212-865-5700",
            "emails": [
                "info@podhotel39.com"
            ],
            "address": "145 East 39th Street, New York, 10016",
            "city": "New York",
            "countryCode": "US"
        },
        {
            "title": "Somerset Hotel",
            "categoryName": "Hotel",
            "website": "https://somersethotellondon.com",
            "phone": "+44 20 7723 0741",
            "emails": [
                "reservations@somersethotel.net"
            ],
            "address": "6 Dorset Square, London, NW1 6QA",
            "city": "London",
            "countryCode": "GB"
        },
        {
            "title": "Mandarin Oriental",
            "categoryName": "Hotel",
            "website": "https://www.mandarinoriental.com/en/new-york/manhattan",
            "phone": "+1-212-805-8800",
            "emails": [
                "MONYC-RESERVATIONS@MOHG.COM"
            ],
            "address": "80 Columbus Circle, New York, 10023",
            "city": "New York",
            "countryCode": "US"
        }
    ],
    "defaultCountry": "GB"
};

// Run the Actor and wait for it to finish
const run = await client.actor("chris_elder/google-maps-lead-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": "Lime Tree Hotel",
            "categoryName": "Hotel",
            "website": "https://www.limetreehotel.co.uk/",
            "phone": "+44 20 7730 8191",
            "emails": ["info@limetreehotel.co.uk"],
            "address": "135-137 Ebury Street, London, SW1W 9QU",
            "city": "London",
            "countryCode": "GB",
        },
        {
            "title": "The Warwick by Nestor",
            "categoryName": "Hotel",
            "website": "https://nestorstay.com/properties/the-warwick-by-nestor/",
            "phone": "+44 20 3868 6192",
            "emails": ["guest@nestorstay.com"],
            "address": "94 Warwick Way, London, SW1V 1SB",
            "city": "London",
            "countryCode": "GB",
        },
        {
            "title": "The Warwick by Nestor",
            "categoryName": "Hotel",
            "website": "https://nestorstay.com/properties/the-warwick-by-nestor/",
            "phone": "+44 20 3868 6192",
            "emails": ["guest@nestorstay.com"],
            "address": "94 Warwick Way, London, SW1V 1SB",
            "city": "London",
            "countryCode": "GB",
        },
        {
            "title": "The Trafalgar St. James London, Curio Collection by Hilton",
            "categoryName": "Hotel",
            "website": "https://www.hilton.com/en/hotels/lontsqq-the-trafalgar-st-james-london/",
            "phone": "+44 20 7870 2900",
            "emails": ["reservations@thetrafalgar.com"],
            "address": "2 Spring Gardens, London, SW1A 2TS",
            "city": "London",
            "countryCode": "GB",
        },
        {
            "title": "Hilton",
            "categoryName": "Hotel",
            "website": "https://www.hilton.com/en/hotels/nyctshh-hilton-times-square/",
            "phone": "+1 212-913-9488",
            "emails": ["NYCTS_HOTEL @hilton.com"],
            "address": "234 West 42nd Street, New York, 10036",
            "city": "New York",
            "countryCode": "US",
        },
        {
            "title": "Pod 39",
            "categoryName": "Hotel",
            "website": "https://www.thepodhotel.com/pod-39/",
            "phone": "+1-844-763-7666;+1-212-865-5700",
            "emails": ["info@podhotel39.com"],
            "address": "145 East 39th Street, New York, 10016",
            "city": "New York",
            "countryCode": "US",
        },
        {
            "title": "Somerset Hotel",
            "categoryName": "Hotel",
            "website": "https://somersethotellondon.com",
            "phone": "+44 20 7723 0741",
            "emails": ["reservations@somersethotel.net"],
            "address": "6 Dorset Square, London, NW1 6QA",
            "city": "London",
            "countryCode": "GB",
        },
        {
            "title": "Mandarin Oriental",
            "categoryName": "Hotel",
            "website": "https://www.mandarinoriental.com/en/new-york/manhattan",
            "phone": "+1-212-805-8800",
            "emails": ["MONYC-RESERVATIONS@MOHG.COM"],
            "address": "80 Columbus Circle, New York, 10023",
            "city": "New York",
            "countryCode": "US",
        },
    ],
    "defaultCountry": "GB",
}

# Run the Actor and wait for it to finish
run = client.actor("chris_elder/google-maps-lead-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": "Lime Tree Hotel",
      "categoryName": "Hotel",
      "website": "https://www.limetreehotel.co.uk/",
      "phone": "+44 20 7730 8191",
      "emails": [
        "info@limetreehotel.co.uk"
      ],
      "address": "135-137 Ebury Street, London, SW1W 9QU",
      "city": "London",
      "countryCode": "GB"
    },
    {
      "title": "The Warwick by Nestor",
      "categoryName": "Hotel",
      "website": "https://nestorstay.com/properties/the-warwick-by-nestor/",
      "phone": "+44 20 3868 6192",
      "emails": [
        "guest@nestorstay.com"
      ],
      "address": "94 Warwick Way, London, SW1V 1SB",
      "city": "London",
      "countryCode": "GB"
    },
    {
      "title": "The Warwick by Nestor",
      "categoryName": "Hotel",
      "website": "https://nestorstay.com/properties/the-warwick-by-nestor/",
      "phone": "+44 20 3868 6192",
      "emails": [
        "guest@nestorstay.com"
      ],
      "address": "94 Warwick Way, London, SW1V 1SB",
      "city": "London",
      "countryCode": "GB"
    },
    {
      "title": "The Trafalgar St. James London, Curio Collection by Hilton",
      "categoryName": "Hotel",
      "website": "https://www.hilton.com/en/hotels/lontsqq-the-trafalgar-st-james-london/",
      "phone": "+44 20 7870 2900",
      "emails": [
        "reservations@thetrafalgar.com"
      ],
      "address": "2 Spring Gardens, London, SW1A 2TS",
      "city": "London",
      "countryCode": "GB"
    },
    {
      "title": "Hilton",
      "categoryName": "Hotel",
      "website": "https://www.hilton.com/en/hotels/nyctshh-hilton-times-square/",
      "phone": "+1 212-913-9488",
      "emails": [
        "NYCTS_HOTEL @hilton.com"
      ],
      "address": "234 West 42nd Street, New York, 10036",
      "city": "New York",
      "countryCode": "US"
    },
    {
      "title": "Pod 39",
      "categoryName": "Hotel",
      "website": "https://www.thepodhotel.com/pod-39/",
      "phone": "+1-844-763-7666;+1-212-865-5700",
      "emails": [
        "info@podhotel39.com"
      ],
      "address": "145 East 39th Street, New York, 10016",
      "city": "New York",
      "countryCode": "US"
    },
    {
      "title": "Somerset Hotel",
      "categoryName": "Hotel",
      "website": "https://somersethotellondon.com",
      "phone": "+44 20 7723 0741",
      "emails": [
        "reservations@somersethotel.net"
      ],
      "address": "6 Dorset Square, London, NW1 6QA",
      "city": "London",
      "countryCode": "GB"
    },
    {
      "title": "Mandarin Oriental",
      "categoryName": "Hotel",
      "website": "https://www.mandarinoriental.com/en/new-york/manhattan",
      "phone": "+1-212-805-8800",
      "emails": [
        "MONYC-RESERVATIONS@MOHG.COM"
      ],
      "address": "80 Columbus Circle, New York, 10023",
      "city": "New York",
      "countryCode": "US"
    }
  ],
  "defaultCountry": "GB"
}' |
apify call chris_elder/google-maps-lead-cleaner --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,chris_elder/google-maps-lead-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/n34lq4MfU2KqgSoGc/builds/CHUkhvhJBF7zbDg6m/openapi.json
