# Google Maps Data Cleaning & Duplicate Audit (`practical_ai_lab_jp/pal-maps-business-dataset-quality`) Actor

Clean business-location exports by finding duplicates, missing websites and incomplete listing fields. For Location-data, GIS and CRM data quality teams. Check the live Pricing tab and input limits.

- **URL**: https://apify.com/practical_ai_lab_jp/pal-maps-business-dataset-quality.md
- **Developed by:** [Practical AI Lab](https://apify.com/practical_ai_lab_jp) (community)
- **Categories:** Lead generation, SEO tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $20.00 / 1,000 maps dataset qa reports

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

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 Data Cleaning & Duplicate Audit

An analytics and QA companion for Google Maps business datasets you **already have**. It does not scrape Google Maps, contact Google, or make any external request. It also does not collect, guess or export personal information (no emails, phone numbers or owner names).

Built by PAL (Practical AI Lab).

### What it does

- **Deduplicates** by place ID, by name + rounded coordinates (about 10 m), and by name + address. Records linked by any of these are merged and the most complete record is kept.
- **Flags missing websites**. It separates `missing`/`invalid` (counted as missing) from `social` (a website that is only a Facebook/Instagram/etc. page) and `own`.
- **Segments** by category and by location (city, state, country code).
- **Scores completeness** (0-100) per business and lists the missing fields.
- **Produces CSV-ready records** (`businesses.csv` in the key-value store). Cells starting with `=`, `+`, `-` or `@` are escaped to avoid spreadsheet formula injection.

### Try the ready-to-run sample

The Input form is prefilled with 4 **fictional, synthetic** demonstration records. Click Start to see the report without scraping Google Maps, purchasing a dataset, or providing an Apify dataset ID. Change the input to your own legally obtained dataset for real analysis. Example data is not a real customer or source.

### Input

| Field | Description |
|---|---|
| `records` | JSON array of business objects. |
| `recordsJson` | The same, as pasted text. An object with an `items` array also works. |
| `sourceDatasetId` | Optional dataset ID from your own Apify account (used only when no records are supplied). |
| `onlyMissingWebsite` | Only list businesses without a usable website. |
| `includeBusinessRows` | Push one row per unique business (default true). |

Recognized fields (the first match wins): `placeId`/`place_id`/`cid`, `title`/`name`, `categoryName`/`category`, `address`, `city`, `state`, `countryCode`, `website`, `location.lat`/`location.lng` (or `latitude`/`longitude`), `totalScore`/`rating`, `reviewsCount`, `permanentlyClosed`, `temporarilyClosed`. Up to 50,000 records per run.

### Output

The default dataset holds rows distinguished by `recordType`:

- `summary`: totals, duplicates removed, missing-website share, average completeness.
- `category_summary` and `segment_summary`: businesses, missing website count and %, average rating, median reviews, completeness.
- `business`: normalized, deduplicated records (with `missingWebsite`, `websiteStatus`, `completenessScore`, `missingFields`, `duplicateCount`).
- `error`: invalid input. Error rows are never charged.

Key-value store: `SUMMARY`, `OUTPUT`, `businesses.csv`.

### Pricing event

Pricing is not configured yet. The planned event name is `maps-dataset-qa-report`, charged once per successful report. Failed runs and error rows are not charged.

### Limits

Analysis is only as good as the input. Address-derived cities are not guessed; the `city` field must be present to segment by city. Social-only detection uses a fixed host list.

### Local tests

```
python3 -m unittest discover -s tests
```

# Actor input Schema

## `records` (type: `array`):

Existing Google Maps business results you already hold, as a JSON array of objects (title/name, placeId, categoryName, address, city, website, location.lat/lng, totalScore, reviewsCount...).

## `recordsJson` (type: `string`):

Alternative to `records`: paste a JSON array (or an object with an `items` array) as text.

## `sourceDatasetId` (type: `string`):

Optional ID of a dataset in your own Apify account to analyze instead of pasting records. Ignored if records are supplied.

## `onlyMissingWebsite` (type: `boolean`):

Restrict business rows and the CSV to businesses with no usable website.

## `includeBusinessRows` (type: `boolean`):

Push one dataset row per unique business in addition to the summaries.

## Actor input object example

```json
{
  "records": [
    {
      "placeId": "demo-001",
      "title": "Example Riverside Cafe",
      "categoryName": "Cafe",
      "address": "101 Sample Avenue",
      "city": "Demo City",
      "countryCode": "US",
      "website": "https://riverside.example",
      "location": {
        "lat": 40.7128,
        "lng": -74.006
      },
      "totalScore": 4.4,
      "reviewsCount": 88
    },
    {
      "placeId": "demo-001",
      "title": "Example Riverside Cafe (duplicate)",
      "categoryName": "Cafe",
      "address": "101 Sample Avenue",
      "city": "Demo City",
      "countryCode": "US",
      "location": {
        "lat": 40.7128,
        "lng": -74.006
      },
      "totalScore": 4.4,
      "reviewsCount": 88
    },
    {
      "placeId": "demo-002",
      "title": "Example Northside Bakery",
      "categoryName": "Bakery",
      "address": "20 Sample Street",
      "city": "Demo City",
      "countryCode": "US",
      "website": "",
      "location": {
        "lat": 40.713,
        "lng": -74.0065
      },
      "totalScore": 4.1,
      "reviewsCount": 31
    },
    {
      "placeId": "demo-003",
      "title": "Example Garden Florist",
      "categoryName": "Florist",
      "address": "8 Example Road",
      "city": "Demo City",
      "countryCode": "US",
      "website": "https://garden.example",
      "location": {
        "lat": 40.714,
        "lng": -74.0045
      },
      "totalScore": 4.7,
      "reviewsCount": 52
    }
  ],
  "onlyMissingWebsite": false,
  "includeBusinessRows": true
}
```

# 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 = {
    "records": [
        {
            "placeId": "demo-001",
            "title": "Example Riverside Cafe",
            "categoryName": "Cafe",
            "address": "101 Sample Avenue",
            "city": "Demo City",
            "countryCode": "US",
            "website": "https://riverside.example",
            "location": {
                "lat": 40.7128,
                "lng": -74.006
            },
            "totalScore": 4.4,
            "reviewsCount": 88
        },
        {
            "placeId": "demo-001",
            "title": "Example Riverside Cafe (duplicate)",
            "categoryName": "Cafe",
            "address": "101 Sample Avenue",
            "city": "Demo City",
            "countryCode": "US",
            "location": {
                "lat": 40.7128,
                "lng": -74.006
            },
            "totalScore": 4.4,
            "reviewsCount": 88
        },
        {
            "placeId": "demo-002",
            "title": "Example Northside Bakery",
            "categoryName": "Bakery",
            "address": "20 Sample Street",
            "city": "Demo City",
            "countryCode": "US",
            "website": "",
            "location": {
                "lat": 40.713,
                "lng": -74.0065
            },
            "totalScore": 4.1,
            "reviewsCount": 31
        },
        {
            "placeId": "demo-003",
            "title": "Example Garden Florist",
            "categoryName": "Florist",
            "address": "8 Example Road",
            "city": "Demo City",
            "countryCode": "US",
            "website": "https://garden.example",
            "location": {
                "lat": 40.714,
                "lng": -74.0045
            },
            "totalScore": 4.7,
            "reviewsCount": 52
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("practical_ai_lab_jp/pal-maps-business-dataset-quality").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 = { "records": [
        {
            "placeId": "demo-001",
            "title": "Example Riverside Cafe",
            "categoryName": "Cafe",
            "address": "101 Sample Avenue",
            "city": "Demo City",
            "countryCode": "US",
            "website": "https://riverside.example",
            "location": {
                "lat": 40.7128,
                "lng": -74.006,
            },
            "totalScore": 4.4,
            "reviewsCount": 88,
        },
        {
            "placeId": "demo-001",
            "title": "Example Riverside Cafe (duplicate)",
            "categoryName": "Cafe",
            "address": "101 Sample Avenue",
            "city": "Demo City",
            "countryCode": "US",
            "location": {
                "lat": 40.7128,
                "lng": -74.006,
            },
            "totalScore": 4.4,
            "reviewsCount": 88,
        },
        {
            "placeId": "demo-002",
            "title": "Example Northside Bakery",
            "categoryName": "Bakery",
            "address": "20 Sample Street",
            "city": "Demo City",
            "countryCode": "US",
            "website": "",
            "location": {
                "lat": 40.713,
                "lng": -74.0065,
            },
            "totalScore": 4.1,
            "reviewsCount": 31,
        },
        {
            "placeId": "demo-003",
            "title": "Example Garden Florist",
            "categoryName": "Florist",
            "address": "8 Example Road",
            "city": "Demo City",
            "countryCode": "US",
            "website": "https://garden.example",
            "location": {
                "lat": 40.714,
                "lng": -74.0045,
            },
            "totalScore": 4.7,
            "reviewsCount": 52,
        },
    ] }

# Run the Actor and wait for it to finish
run = client.actor("practical_ai_lab_jp/pal-maps-business-dataset-quality").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 '{
  "records": [
    {
      "placeId": "demo-001",
      "title": "Example Riverside Cafe",
      "categoryName": "Cafe",
      "address": "101 Sample Avenue",
      "city": "Demo City",
      "countryCode": "US",
      "website": "https://riverside.example",
      "location": {
        "lat": 40.7128,
        "lng": -74.006
      },
      "totalScore": 4.4,
      "reviewsCount": 88
    },
    {
      "placeId": "demo-001",
      "title": "Example Riverside Cafe (duplicate)",
      "categoryName": "Cafe",
      "address": "101 Sample Avenue",
      "city": "Demo City",
      "countryCode": "US",
      "location": {
        "lat": 40.7128,
        "lng": -74.006
      },
      "totalScore": 4.4,
      "reviewsCount": 88
    },
    {
      "placeId": "demo-002",
      "title": "Example Northside Bakery",
      "categoryName": "Bakery",
      "address": "20 Sample Street",
      "city": "Demo City",
      "countryCode": "US",
      "website": "",
      "location": {
        "lat": 40.713,
        "lng": -74.0065
      },
      "totalScore": 4.1,
      "reviewsCount": 31
    },
    {
      "placeId": "demo-003",
      "title": "Example Garden Florist",
      "categoryName": "Florist",
      "address": "8 Example Road",
      "city": "Demo City",
      "countryCode": "US",
      "website": "https://garden.example",
      "location": {
        "lat": 40.714,
        "lng": -74.0045
      },
      "totalScore": 4.7,
      "reviewsCount": 52
    }
  ]
}' |
apify call practical_ai_lab_jp/pal-maps-business-dataset-quality --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,practical_ai_lab_jp/pal-maps-business-dataset-quality"
        }
    }
}
```

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/draOhPPhWYEjb8LSL/builds/D0GrzhOwFegCgWdtR/openapi.json
