# Google Maps Lead Qualifier — ICP lead score per place · $2/1k (`leoworks/google-maps-lead-qualifier`) Actor

Google Maps reviews toolkit by LeoWorks — lead scoring for ICP fit (score 0–100, tier A–D, reason). For reviews use leoworks/google-maps-review-classifier. icp text + places, datasetId or search terms + location. ~3 s/60 places, ~$6/1,000 (compass billed to you).

- **URL**: https://apify.com/leoworks/google-maps-lead-qualifier.md
- **Developed by:** [Leoworks](https://apify.com/leoworks) (community)
- **Categories:** Lead generation, AI, Agents
- **Stats:** 2 total users, 1 monthly users, 91.7% runs succeeded, 1 bookmarks
- **User rating**: 5.00 out of 5 stars

## Pricing

from $1.70 / 1,000 place qualifications

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 Qualifier — score places against your ideal customer

**Part of LeoWorks Google Maps tools:** reviews → health, free ([Google Maps Review Health Check](https://apify.com/leoworks/google-maps-review-health-check)) · reviews → replies ([Google Maps Review Classifier](https://apify.com/leoworks/google-maps-review-classifier)) · places → leads ([Google Maps Lead Qualifier](https://apify.com/leoworks/google-maps-lead-qualifier)).

**For agencies, sales teams, B2B marketers and AI agents that pull leads from Google Maps** — qualify Google Maps leads by describing your ideal customer (ICP) in plain language and get every place scored 0–100 for how well it fits (Google Maps lead scoring), plus a chain-or-independent check, for $2 per 1,000 places scored, with no prompt writing or LLM key. Bring places you already have, or type search terms and a city and the Actor collects the places for you with compass' Google Maps scraper — that scraper is billed to your Apify account on top (about $0.004 per place), **about $6 per 1,000 places all-in**.

- **Lead score (0–100)** and a **qualified** flag for your ideal customer profile (ICP), e.g. *"Independent gyms that offer personal training"*, *"Dental practices that offer cosmetic dentistry"*
- **Chain check** — chain / franchise location or independent business (and an option to keep independents only)
- **Your own checks** — up to 10 yes/no criteria in plain language (e.g. "offers online booking", "looks like a premium business")
- **Free pre-filters** — minimum rating, minimum reviews, website required, phone required, closed places skipped: **places removed by them are not scored or charged by this Actor** (if the Actor collected the places for you, the scraper has already billed those)

**Use it to:** turn a 500-place Google Maps export into a short list worth calling · find independent businesses only · qualify leads for cold email or outreach · let an AI agent build a lead list from a plain-language brief.

**How this differs from our other actors:** this one judges **businesses (places)**, not reviews or comments. Each output row is a lead with its contact fields kept as the scraper gave them. To analyse a place's reviews use our [Google Maps Review Classifier](https://apify.com/leoworks/google-maps-review-classifier).

### Who collects the places?

Two ways in. Either way, this Actor only **scores** places; it does not scrape Google Maps itself.

- **Search terms + Location (quickest):** type what you would search on Google Maps (for example `independent dentist`) and a city and country (for example `Chicago, USA`). The Actor runs **compass' Google Maps scraper** ([compass/crawler-google-places](https://apify.com/compass/crawler-google-places)) for you, then scores the places it found in the same run. That scraper is a separate Actor that **Apify bills to your account** (about $0.004 per place, set by its author and subject to change); this Actor's $0.002 per scored place is added. **All-in about $6 per 1,000 places**. The run log shows the estimate before anything is collected, and your maximum cost per run is respected.
- **Places dataset or pasted places:** run any Google Maps scraper yourself and give this Actor its dataset (or paste the places). You pay that scraper directly and this Actor only for the scoring: $2 per 1,000.

Reviewer names and review texts are never collected. Need emails? The scraper's contact add-ons are not switched on here; run a contact-details scraper yourself and pass its dataset.

### How to use

1. Quickest: in **Search terms** write what you search for, in **Location** write the city and country. Or run a Google Maps scraper yourself for your area and business type — for example [Google Maps Scraper](https://apify.com/compass/crawler-google-places), or [Google Maps with Contact Details](https://apify.com/lukaskrivka/google-maps-with-contact-details) if you want emails (see **Works with** for others).
2. Describe your ideal customer in **ICP** in plain English. If you ran a scraper yourself, pick that run's dataset in **Places dataset**.
3. Optional: free filters (minimum rating or reviews, require a phone or email, exclude names like "hospital", only places without a website), **Exclude chains**, and up to 10 **Custom checks**. Filtered places are not charged.
4. Click **Start**. Scoring 60 places takes about 3 seconds; with **Search terms** add the scraper's own run time (about 10–60 seconds for 20 places).
5. Download `LEADS.csv` (qualified leads, best first, with a one-line reason) or open the **Leads** and **Contacts** views. Export as CSV, Excel or JSON, or import into your CRM.

#### Run it automatically after every scrape

In your Google Maps scraper (or its saved task), open **Integrations → Add integration → Apify Actor**, choose this Actor and set its input to:

```json
{ "datasetId": "{{resource.defaultDatasetId}}", "icp": "Independent dental practices that offer cosmetic dentistry" }
```

#### Qualify before you enrich

Score places first, then spend on enrichment (emails, employees, website analysis) only for the ones that fit. Example: 1,000 places at $0.002 = **$2** to judge. If 300 qualify and enrichment costs $0.10 per lead, you pay $30 instead of $100. The run also saves `QUALIFIED_WEBSITES`: a ready [Website Content Crawler](https://apify.com/apify/website-content-crawler) input — the qualified sites' home pages (no location paths or tracking parameters, no Facebook or other social pages), one per domain, with a crawl depth of 1 and about 6 pages per site so the crawl stays cheap. Paste it into Website Content Crawler, then judge the crawled sites with our [Company Website Classifier](https://apify.com/leoworks/company-website-classifier) using the same ICP, and join the two results on `domain`.

### Output sample

Real rows from run `Xi1EjGj5eWwYuxQXq` (2026-10-11, build 0.2.1): 60 Chicago places from the Google Maps Scraper (dentists, restaurants and gyms), ICP *"Independent gyms or fitness studios that offer personal training"*, minimum rating 4.5, independents only, one custom check ("offers group classes"). 26 places were removed by the rating filter (not charged), 34 were scored.

| place | category | rating (reviews) | lead score | tier | qualified | reason (shortened) |
|---|---|---|---|---|---|---|
| Northside Fitness Studio | Personal trainer | 4.9 (57) | **73** | B | **yes** | Independent. Reachable by website, phone. Matches: offers group classes. |
| Anthos Training Clubs | Personal trainer | 5.0 (92) | **71** | B | **yes** | Independent. Reachable by website, phone. |
| The Space 4.0, Functional Training and Sports Performance | Gym | 4.8 (25) | 46 | C | no | Borderline — worth a manual look |
| Chicago Body Shop | Fitness center | 5.0 (190) | 39 | C | no | |
| Mettle Fitness, LLC | Physical fitness program | 4.7 (50) | 36 | C | no | |
| … 4 more gyms | | | 11–34 | C–D | no | |
| 24 dentists and restaurants | Dentist, Restaurant … | | ≤ 2 | D | no | |

Tiers: **A** = threshold + 0.25 or more, **B** = qualified, **C** = within 0.2 below the threshold ("maybe" — the listing may just not mention the trait), **D** = no fit. In the first run of the same input (`iPEs47tskMe4StyMe`, 2026-10-10) "The Space 4.0" scored 50 and just qualified: scores near the threshold can move a few points between runs.

### Input example

The form default — two places pasted as JSON, no dataset needed (about $0.004, 3 seconds):

```json
{
  "icp": "Dental practices that offer cosmetic dentistry such as teeth whitening, veneers or smile makeovers.",
  "places": [
    { "title": "Kimberly Smiles Cosmetic & General Dentistry", "categoryName": "Dentist", "categories": ["Dentist", "Cosmetic dentist", "Emergency dental service", "Oral surgeon", "Teeth whitening service"], "city": "Chicago", "totalScore": 4.7, "reviewsCount": 484 },
    { "title": "Kids Tooth Team", "categoryName": "Pediatric dentist", "categories": ["Pediatric dentist", "Dental clinic", "Dental hygienist", "Dentist", "Emergency dental service"], "city": "Austin", "totalScore": 4.9, "reviewsCount": 740 }
  ]
}
```

Result: the cosmetic dentist scores 85 (qualified), the pediatric dentist 3. To score a Google Maps scraper's output, pass its dataset instead:

```json
{
  "datasetId": "YOUR_GOOGLE_MAPS_PLACES_DATASET_ID",
  "icp": "Independent gyms or fitness studios that offer personal training",
  "minRating": 4.5,
  "requirePhone": true,
  "excludeChains": true
}
```

### Pricing

Pay only for places that are scored — no subscription.

| Event | Price | When |
|---|---|---|
| `place-qualified` | $0.002 | One Google Maps place checked against your ideal customer profile (lead score, chain check and any custom labels). Places removed by the free pre-filters are not charged. |

That is **$2 per 1,000 places scored**. **First run with the form defaults: about $0.004** (2 places, 3 seconds).

**Cost examples**

| Places | Cost |
|---|---|
| 100 | $0.20 |
| 1,000 | $2.00 |
| 10,000 | $20.00 |
| 100,000 | $200.00 |

With the free $5 monthly Apify credit you can score about **2,500 places**.

Places removed by the free pre-filters, items without a name and places that fail after retries are **not charged by this Actor**. If you set a maximum cost per run, the Actor stops cleanly when it is reached.

#### How much does it cost when I use Search terms?

The scraper that collects the places is a separate Actor and is billed to your Apify account, not to us: about **$0.004 per place** (its price, may change) plus a fraction of a cent to start. This Actor adds **$0.002 per scored place**. All-in **about $6 per 1,000 places**; the form default of one search term with 20 places is about **$0.12**. The scraper bills every place it collects, including places that a free filter here then removes or that appear twice, so use **Max places per search term** to keep the collection small. Before collecting, the run log shows the estimate; if your maximum cost per run is too low, the Actor shrinks the number of places or refuses to start and nothing is charged.

### Works with

Field names of the other Google Maps scrapers are mapped onto the Google Maps Scraper's (`rating` → stars, `reviewCount`/`review_count`/`userRatingCount` → reviews, `category`/`primaryTypeDisplayName`/`types` → category, `phone_number` → phone, `businessStatus`/`status` → closed, `amenities`/`serviceOptions`/`attributes`/`about` → offerings, a website in `url` with the Maps link in `google_maps_url`). Emails and social links are kept in the output.

| Scraper on Apify Store | Checked | Emails & socials kept |
|---|---|---|
| [Google Maps Scraper (compass)](https://apify.com/compass/crawler-google-places) | real output (2026-10-10) | with its contact add-on: `emails`, `linkedIns`, `facebooks`, `instagrams` |
| [Google Maps with Contact Details (lukaskrivka)](https://apify.com/lukaskrivka/google-maps-with-contact-details) | published schema | `emails`, `linkedIns`, `facebooks`, `instagrams` |
| [Google Maps Extractor (compass)](https://apify.com/compass/google-maps-extractor) | published schema | — |
| kaix, scraperlink, vortex_data, scrapesage, poidata, leadsbrary | published schema | `emails`, `contactEmails`, `socialProfiles` |
| xmiso, herus13, api-ninja, santamaria, johnvc (snake_case fields) | published schema | `email`, `emails_and_contacts`, `emails_from_website`, `social_links`, `social_urls` |

The same place appearing twice (several search terms in one dataset) is scored and charged once. **Also:** the Apify API and JavaScript/Python clients · Apify Schedules · Claude, Cursor and Claude Code through the Apify MCP server (below).

### Call it from code

```bash
curl -X POST "https://api.apify.com/v2/acts/leoworks~google-maps-lead-qualifier/run-sync-get-dataset-items?token=YOUR_APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"datasetId": "YOUR_DATASET_ID", "icp": "Independent gyms that offer personal training", "excludeChains": true}'
```

JavaScript (`npm install apify-client`):

```js
import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: 'YOUR_APIFY_TOKEN' });
const run = await client.actor('leoworks/google-maps-lead-qualifier').call({ datasetId: 'YOUR_DATASET_ID', icp: 'Independent gyms that offer personal training' });
const leadsCsv = await client.keyValueStore(run.defaultKeyValueStoreId).getRecord('LEADS.csv');
```

Python (`pip install apify-client`):

```python
from apify_client import ApifyClient
client = ApifyClient("YOUR_APIFY_TOKEN")
run = client.actor("leoworks/google-maps-lead-qualifier").call(run_input={"datasetId": "YOUR_DATASET_ID", "icp": "Independent gyms that offer personal training"})
leads = [p for p in client.dataset(run["defaultDatasetId"]).list_items().items if p.get("qualified")]
```

### Use with Claude, Cursor or Claude Code (MCP)

**Ask your AI agent.** Add the Apify MCP server with this Actor as a tool, then say, for example:

- *"Find 20 cosmetic dentists in Chicago on Google Maps and score them against this ideal customer: “Dental practices that offer cosmetic dentistry such as veneers or teeth whitening”. Show the top 5 with the reason for each score."*
- *"Search Google Maps for “yoga studio” in Austin, USA (15 places), score them for “Independent yoga studios that offer teacher training”, skip chains and anything under 4.0 stars, and list the qualified ones with phone and website."*
- *"Take dataset YOUR_DATASET_ID from my Google Maps scraper run and tell me which places are independent gyms that offer personal training, ranked by lead score."*

The agent calls the tool `leoworks--google-maps-lead-qualifier` and reads the scored places with `get-dataset-items`. When the tool collects the places for you, compass' Google Maps scraper is billed to your Apify account on top (about $0.12 for 20 places; the estimate is in the run log before anything is collected), so keep the number of places small in a first agent session. Replace `YOUR_DATASET_ID` with the ID of your own dataset.

Claude Desktop or Cursor (`mcp.json`):

```json
{
  "mcpServers": {
    "apify": {
      "url": "https://mcp.apify.com?tools=leoworks/google-maps-lead-qualifier",
      "headers": { "Authorization": "Bearer YOUR_APIFY_TOKEN" }
    }
  }
}
```

Claude Code: `claude mcp add --transport http apify "https://mcp.apify.com?tools=leoworks/google-maps-lead-qualifier" --header "Authorization: Bearer YOUR_APIFY_TOKEN"`. Leave out the header to sign in with OAuth in the browser instead. We verified this setup with the Apify MCP server (v0.17.4) on 2026-10-10: the agent scored two coffee shops against "Independent coffee shops" in 8 seconds end to end (run `d0udzeor7QLhdaeLZ`).

### Output

| Where | What |
|---|---|
| Dataset — view **Leads** | One row per place: score, tier, qualified, the reason, category, city, phone, email, website, stars, chain |
| Dataset — view **Contacts** | Emails, phone, website, LinkedIn, Facebook, Instagram and address |
| Dataset — view **Labels with probabilities** | ICP fit, chain and custom checks with probabilities |
| `LEADS.csv` | Qualified leads only, best first, with `rank` and `reason` — ready for a spreadsheet or CRM import |
| `QUALIFIED_WEBSITES` | Website Content Crawler input for the qualified sites: `{ "startUrls": [{ "url": … }], "maxCrawlDepth": 1, "maxCrawlPages": … }` |
| `REPORT` (JSON) | Counts, tiers, how qualified leads can be reached, removals per filter, results by category and city, top 25 leads |

#### One row

From run `Xi1EjGj5eWwYuxQXq` (the business name, address, phone and website are replaced by fictitious values):

```json
{
  "placeId": "ChIJ-example-place-id",
  "title": "Northside Fitness Studio",
  "categoryName": "Personal trainer",
  "address": "100 Example Ave, Chicago, IL 60601",
  "city": "Chicago",
  "phone": "(312) 555-0142",
  "website": "https://northside-fitness.example/",
  "totalScore": 4.9,
  "reviewsCount": 57,
  "contactChannels": 2,
  "leadScore": 73,
  "tier": "B",
  "qualified": true,
  "isChain": false,
  "customLabelsMatched": "offers group classes",
  "reason": "Fits the ICP (73/100, tier B). Independent. Reachable by website, phone. 4.9★ from 57 reviews. Matches: offers group classes."
}
```

Rows also carry `email`, `emails`, `linkedin`, `facebook`, `instagram` when the dataset has them, `unclaimedListing: true` when the owner has not claimed the listing, the Google Maps `url`, and the full `labels` object. Rows removed by a free filter carry `filteredOut` (e.g. `["minRating"]`), a `reason` and no score.

#### Summary and top leads (REPORT)

Each run also saves a **REPORT** record (Output tab → *Summary and top leads*) at no extra charge: places scored and qualified, tiers, how qualified leads can be reached, chains found, places removed by each pre-filter, results by category and by city, and the 25 best leads with their contact fields.

```json
{
  "icp": "Independent gyms or fitness studios that offer personal training",
  "judged": 34,
  "qualified": 2,
  "tiers": { "A": 0, "B": 2, "C": 5, "D": 27 },
  "qualifiedReachableBy": { "email": 0, "phone": 2, "website": 2, "none": 0 },
  "filteredOut": { "minRating": 26 },
  "byCategory": [{ "key": "Personal trainer", "judged": 2, "qualified": 2, "qualifiedShare": 1 }],
  "topLeads": [{ "title": "Northside Fitness Studio", "leadScore": 73, "tier": "B", "category": "Personal trainer", "city": "Chicago", "website": "https://northside-fitness.example/" }]
}
```

### Accuracy

Measured on 120 Google Maps places in Chicago and Austin (dentists, restaurants, gyms; 2026-10-10) against six plain-language ICPs — cosmetic dentistry, dentists who treat children, independent restaurants, restaurants with vegetarian or vegan options, independent gyms, gyms that offer personal training. Each place–ICP pair was labelled by hand from the listing data; pairs that could not be decided from the data were left out. Half of the places were held back and used only once for this check.

| Set | Place–ICP pairs | ICP match | Chain check (places) |
|---|---|---|---|
| **Check set — not used for adjusting** | 116 | **94%** (precision 96%, recall 95%) | **100%** (30) |
| Other half | 116 | 93% | 100% (41) |

The questions were not changed between the two halves. Scores are calibrated probabilities — sort by **lead score** and set **Match threshold** to trade coverage for certainty. The judgment uses only the listing data and common knowledge (for example, that "Planet Fitness" is a chain); if a trait is not in the listing, the place scores low. Check samples before large campaigns.

### Limits

| Item | Limit |
|---|---|
| ICP | Up to 1,000 characters |
| Custom checks | Up to 10, each up to 200 characters |
| Dataset size | Any — datasets are read in pages of 1,000 |
| Search terms | Up to 5 per run, one Location per run (run once per city); up to 200 places per term and 500 places per run |
| Credit left when collecting for you | With **Search terms**, starting the scraper may need roughly $0.50 of remaining Apify credit this billing cycle, even when the run costs a few cents (observed, not documented by Apify). The run then stops before anything is collected and this Actor charges nothing |
| Language | Listings in any language; ICP and checks in English (measured). Other languages usually work — treat them as **beta** |
| Speed | 60 places in about 3 seconds; 100 places in about 4 seconds |
| Data | Only the place's name, categories, offerings, description, website domain, rating, review count, price level and city are sent for the judgment — never phone numbers, addresses or reviewer data |

### FAQ

**Which AI makes the judgments?** Jev, TypeSafe's decision model (version `jev-1.13.0`, pinned). Jev answers each check with a calibrated probability instead of generated text, so the same input gets nearly the same answer every run (scores can move a few points; near the threshold that can flip a result — use tier C for "maybe").

**Does it scrape Google Maps or find emails?** It does not scrape Google Maps itself. With **Search terms** and a **Location** it calls compass' Google Maps scraper for you (billed to your account, about $0.004 per place) and scores what it finds; with a dataset you ran earlier it scores that. Contact fields are passed through from the scraper's output; emails need a contact-details scraper (see **Works with**).

**Who is billed for collecting the places?** You are, by Apify, as the person who runs this Actor: the scraper's charge appears on its own run, linked in this run's log and summary (`SUMMARY.collection`). This Actor charges only the $0.002 per scored place.

**Why did it search the wrong area?** The scraper searches from a US data center unless it is given a location. Fill **Location** with a city and country (for example `Seoul, South Korea`); the run log lists the first places found with their addresses.

**How do I write a good ICP?** Name the business type and the traits that must show in a Google Maps listing: *"Independent dental practices that offer cosmetic dentistry such as veneers or whitening"* works; *"Good dentists that would buy our software"* does not (nothing in a listing shows that). Put each extra trait you only want to know about in **Custom checks** instead of the ICP — every trait in the ICP must be met.

**Which threshold should I use?** 0.5 (default) for a balanced list; 0.7 for a short, sure list before expensive outreach; 0.3 (or tier C) when listings are thin and you will check by hand.

**Why not paste the places into ChatGPT?** For a few places you can. For thousands, this gives a calibrated score per place, measured accuracy (see **Accuracy**) and a fixed $2 per 1,000 places — with free filters, de-duplication and a CRM-ready CSV.

**Does it find emails?** No. Run a scraper that collects them (for example Google Maps with Contact Details); this Actor keeps them in its output and can require them with **Require an email**.

**Why is it priced per place and not per check?** One price covers the lead score, the chain check and all your custom checks for that place.

### Disclaimer

> Independent tool — not affiliated with, endorsed by or sponsored by Google, or by the authors of the scraper listed above. Names are used only to describe compatible data sources. The places shown are public Google Maps listings used as examples. Use leads in line with the laws that apply to your outreach.

### Reviews and support

If this Actor saved you time, a short review on Apify Store helps others find it. Questions or a dataset whose fields are not detected? Open an issue in the **Issues** tab — we answer within a day.

### Changelog

See the Changelog tab.

# Changelog

This Actor's version history is a separate document: https://apify.com/leoworks/google-maps-lead-qualifier/changelog.md

# Actor input Schema

## `icp` (type: `string`):

Describe the businesses you want as leads, in plain language — e.g. “Independent gyms that offer personal training”, “Dental practices that offer cosmetic dentistry such as veneers”. Each place gets a lead score (0–100) for how well it fits.

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

Optional. What you would type in the Google Maps search box, one per line, up to 5 — for example “independent dentist”. Needs “Location” below. The Actor then runs compass’ Google Maps scraper (compass/crawler-google-places) for you and scores the places it finds. That scraper is a separate Actor that Apify bills to YOUR account, about $0.004 per place on top of this Actor’s $0.002 per scored place — all-in about $6 per 1,000 places (about $0.12 for the default 20 places); the estimate is shown in the run log before anything is collected. Reviewer data and review texts are not collected. Use this OR “Places dataset” OR “Places”; if Search terms are set, the pasted “Places” samples are ignored.

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

Where to search, for example “Chicago, USA” or “Seoul, South Korea”. Required when you use Search terms (without it the scraper searches around a US data center). One location per run; to cover several cities, run once per city. Ignored with “Places dataset” or “Places”.

## `maxPlacesPerSearch` (type: `integer`):

Only used with Search terms: how many places the scraper collects for each term (default 20, at most 200; at most 500 places in total per run). You are billed for every place it collects. If your maximum cost per run is lower than the estimate, the Actor shrinks this number or refuses to start.

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

An Apify dataset of Google Maps places — for example the output of a Google Maps scraper you ran earlier. Use this OR “Search terms” OR “Places”.

## `places` (type: `array`):

Place objects pasted as JSON (same fields as a Google Maps scraper's output: title, categoryName, categories, additionalInfo, website, phone, totalScore, reviewsCount …). Use this OR “Places dataset” OR “Search terms”.

## `customLabels` (type: `array`):

Up to 10 extra yes/no checks (each up to 200 characters) in plain language, e.g. “offers online booking”, “looks like a premium / high-end business”. Each gets a probability.

## `judgePlacePhotos` (type: `boolean`):

Beta, off by default. For each qualified place, looks at its main Google Maps photo and adds: independent business or known chain, outdoor seating seen, condition of the building (0–4). Costs $0.001 for each photo that gives at least one value (a photo that shows none of them is not charged). The photo is sent to TypeSafe only to be judged and is not stored. A place's main photo is not always the outside, so each answer can be "unknown" or "not seen"; "not seen" is not proof that something is absent. Not yet measured on a labeled set.

## `photoLabels` (type: `array`):

Up to 5 extra yes/no checks about what the photo shows, e.g. “has a garden”, “glass storefront” (each up to 200 characters). Needs "Judge the place photo". Not measured: treat the answers as a hint.

## `photoMaxSide` (type: `integer`):

Longest side in pixels of the photo that is judged (512, 768 or 1024; other values are rounded to the nearest). 1024 reads signs best; smaller is faster.

## `minRating` (type: `number`):

Free pre-filter: skip places rated below this (0 = off). Skipped places are not charged.

## `minReviews` (type: `integer`):

Free pre-filter: skip places with fewer reviews (0 = off).

## `requireWebsite` (type: `boolean`):

Free pre-filter: skip places without a website.

## `requirePhone` (type: `boolean`):

Free pre-filter: skip places without a phone number.

## `requireEmail` (type: `boolean`):

Keep only places with an email address — needs a scraper that collects emails (e.g. Google Maps with Contact Details, or the Google Maps Scraper's contact add-on).

## `excludeClosed` (type: `boolean`):

Free pre-filter: skip permanently or temporarily closed places.

## `excludeKeywords` (type: `array`):

Free filter: places whose name or categories contain any of these words are removed before judging, e.g. "hospital", "Aspen Dental". Not case-sensitive.

## `onlyWithoutWebsite` (type: `boolean`):

For web agencies: keep only businesses that have no website on Google Maps.

## `onlyUnclaimed` (type: `boolean`):

Keep only listings the owner has not claimed on Google Maps (when the scraper reports it).

## `excludeChains` (type: `boolean`):

Mark chain and franchise locations as not qualified (they are still checked and charged, with `chain` in the output).

## `matchThreshold` (type: `number`):

Minimum lead score (as a probability 0–1) for a place to count as qualified.

## `maxItems` (type: `integer`):

Process at most this many places (0 = all).

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

Places checked in parallel.

## `healthCheck` (type: `boolean`):

Internal: fail the run when results look degraded (used by the developer's scheduled checks).

## Actor input object example

```json
{
  "icp": "Dental practices that offer cosmetic dentistry such as teeth whitening, veneers or smile makeovers.",
  "maxPlacesPerSearch": 20,
  "places": [
    {
      "title": "Kimberly Smiles Cosmetic & General Dentistry",
      "categoryName": "Dentist",
      "categories": [
        "Dentist",
        "Cosmetic dentist",
        "Emergency dental service",
        "Oral surgeon",
        "Teeth whitening service"
      ],
      "website": "https://dentistwickerparkchicago.com/",
      "phone": "(773) 276-1047",
      "city": "Chicago",
      "totalScore": 4.7,
      "reviewsCount": 484
    },
    {
      "title": "Kids Tooth Team",
      "categoryName": "Pediatric dentist",
      "categories": [
        "Pediatric dentist",
        "Dental clinic",
        "Dental hygienist",
        "Dentist",
        "Emergency dental service"
      ],
      "website": "https://kidstoothteam.com/locations/our-south-austin-office/",
      "phone": "(512) 641-4914",
      "city": "Austin",
      "totalScore": 4.9,
      "reviewsCount": 740,
      "additionalInfo": {
        "Service options": [
          {
            "English": true
          }
        ],
        "Offerings": [
          {
            "Emergency services": true
          },
          {
            "Pediatric care": true
          },
          {
            "Sedation dentistry": true
          }
        ]
      }
    }
  ],
  "customLabels": [],
  "judgePlacePhotos": false,
  "photoLabels": [],
  "photoMaxSide": 1024,
  "minRating": 0,
  "minReviews": 0,
  "requireWebsite": false,
  "requirePhone": false,
  "requireEmail": false,
  "excludeClosed": true,
  "excludeKeywords": [],
  "onlyWithoutWebsite": false,
  "onlyUnclaimed": false,
  "excludeChains": false,
  "matchThreshold": 0.5,
  "maxItems": 0,
  "maxConcurrency": 10,
  "healthCheck": false
}
```

# Actor output Schema

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

No description

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

No description

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

No description

## `leadsCsv` (type: `string`):

No description

## `qualifiedWebsites` (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 = {
    "icp": "Dental practices that offer cosmetic dentistry such as teeth whitening, veneers or smile makeovers.",
    "places": [
        {
            "title": "Kimberly Smiles Cosmetic & General Dentistry",
            "categoryName": "Dentist",
            "categories": [
                "Dentist",
                "Cosmetic dentist",
                "Emergency dental service",
                "Oral surgeon",
                "Teeth whitening service"
            ],
            "website": "https://dentistwickerparkchicago.com/",
            "phone": "(773) 276-1047",
            "city": "Chicago",
            "totalScore": 4.7,
            "reviewsCount": 484
        },
        {
            "title": "Kids Tooth Team",
            "categoryName": "Pediatric dentist",
            "categories": [
                "Pediatric dentist",
                "Dental clinic",
                "Dental hygienist",
                "Dentist",
                "Emergency dental service"
            ],
            "website": "https://kidstoothteam.com/locations/our-south-austin-office/",
            "phone": "(512) 641-4914",
            "city": "Austin",
            "totalScore": 4.9,
            "reviewsCount": 740,
            "additionalInfo": {
                "Service options": [
                    {
                        "English": true
                    }
                ],
                "Offerings": [
                    {
                        "Emergency services": true
                    },
                    {
                        "Pediatric care": true
                    },
                    {
                        "Sedation dentistry": true
                    }
                ]
            }
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("leoworks/google-maps-lead-qualifier").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 = {
    "icp": "Dental practices that offer cosmetic dentistry such as teeth whitening, veneers or smile makeovers.",
    "places": [
        {
            "title": "Kimberly Smiles Cosmetic & General Dentistry",
            "categoryName": "Dentist",
            "categories": [
                "Dentist",
                "Cosmetic dentist",
                "Emergency dental service",
                "Oral surgeon",
                "Teeth whitening service",
            ],
            "website": "https://dentistwickerparkchicago.com/",
            "phone": "(773) 276-1047",
            "city": "Chicago",
            "totalScore": 4.7,
            "reviewsCount": 484,
        },
        {
            "title": "Kids Tooth Team",
            "categoryName": "Pediatric dentist",
            "categories": [
                "Pediatric dentist",
                "Dental clinic",
                "Dental hygienist",
                "Dentist",
                "Emergency dental service",
            ],
            "website": "https://kidstoothteam.com/locations/our-south-austin-office/",
            "phone": "(512) 641-4914",
            "city": "Austin",
            "totalScore": 4.9,
            "reviewsCount": 740,
            "additionalInfo": {
                "Service options": [{ "English": True }],
                "Offerings": [
                    { "Emergency services": True },
                    { "Pediatric care": True },
                    { "Sedation dentistry": True },
                ],
            },
        },
    ],
}

# Run the Actor and wait for it to finish
run = client.actor("leoworks/google-maps-lead-qualifier").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 '{
  "icp": "Dental practices that offer cosmetic dentistry such as teeth whitening, veneers or smile makeovers.",
  "places": [
    {
      "title": "Kimberly Smiles Cosmetic & General Dentistry",
      "categoryName": "Dentist",
      "categories": [
        "Dentist",
        "Cosmetic dentist",
        "Emergency dental service",
        "Oral surgeon",
        "Teeth whitening service"
      ],
      "website": "https://dentistwickerparkchicago.com/",
      "phone": "(773) 276-1047",
      "city": "Chicago",
      "totalScore": 4.7,
      "reviewsCount": 484
    },
    {
      "title": "Kids Tooth Team",
      "categoryName": "Pediatric dentist",
      "categories": [
        "Pediatric dentist",
        "Dental clinic",
        "Dental hygienist",
        "Dentist",
        "Emergency dental service"
      ],
      "website": "https://kidstoothteam.com/locations/our-south-austin-office/",
      "phone": "(512) 641-4914",
      "city": "Austin",
      "totalScore": 4.9,
      "reviewsCount": 740,
      "additionalInfo": {
        "Service options": [
          {
            "English": true
          }
        ],
        "Offerings": [
          {
            "Emergency services": true
          },
          {
            "Pediatric care": true
          },
          {
            "Sedation dentistry": true
          }
        ]
      }
    }
  ]
}' |
apify call leoworks/google-maps-lead-qualifier --silent --output-dataset

```

## MCP server setup

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

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/fRq17rhi6lqX7a6bu/builds/JqAmalVSpbqGYKBHQ/openapi.json
