# Google Maps Lead Intelligence Scraper (`cyber_orbit/gmaps-scraper-apify-actor-v-3-bulk-queries-supported`) Actor

Scalable Google Maps lead scraper for agencies and growth teams. Extracts clean business data with bulk mode, deduplication, and API-ready datasets. Built for reliable, low-cost lead generation and automation at scale.

- **URL**: https://apify.com/cyber\_orbit/gmaps-scraper-apify-actor-v-3-bulk-queries-supported.md
- **Developed by:** [Team Cyber Orbit](https://apify.com/cyber_orbit) (community)
- **Categories:** Lead generation, Automation, Integrations
- **Stats:** 4 total users, 1 monthly users, 100.0% runs succeeded, NaN bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.00 / 1,000 results

This Actor is paid per result. You are not charged for the Apify platform usage, but only a fixed price for dataset items the Actor outputs.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

## What's an Apify Actor?

Actors are a software tools running on the Apify platform, for all kinds of web data extraction and automation use cases.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

In JavaScript/TypeScript projects, use official [JavaScript/TypeScript client](https://docs.apify.com/api/client/js.md):

```bash
npm install apify-client
```

In Python projects, use official [Python client library](https://docs.apify.com/api/client/python.md):

```bash
pip install apify-client
```

In shell scripts, use [Apify CLI](https://docs.apify.com/cli/docs.md):

````bash
# MacOS / Linux
curl -fsSL https://apify.com/install-cli.sh | bash
# Windows
irm https://apify.com/install-cli.ps1 | iex
```bash

In AI frameworks, you might use the [Apify MCP server](https://docs.apify.com/platform/integrations/mcp.md).

If your project is in a different language, use the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).


# README

## Google Maps Scraper Actor

**Author:** Assad Baloch

[![Apify Actor](https://img.shields.io/badge/Apify-Actor-green.svg)](https://apify.com)
[![License: ISC](https://img.shields.io/badge/License-ISC-blue.svg)](https://opensource.org/licenses/ISC)

A robust, production-ready Google Maps scraper using the Apify/Crawlee Playwright stack. The actor supports single and bulk queries, persistent deduplication (dataset-level or global), per-query dataset creation, run summaries, and backup/restore utilities for KV and dataset snapshots.

### 🚀 Features

- ✅ **Single and bulk queries** with per-query dataset creation
- ✅ **Persistent deduplication** (dataset-level or global) with TTL and key strategy (place_id preferred)
- ✅ **Structured output** — full lead serialization with metadata and raw snapshots
- ✅ **Backup & restore** scripts for KV and datasets
- ✅ **Testable design** — scraping logic is in `lib/scrape.js` for unit testing


### 🚀 Features

- ✅ **Low Cost** - Session reuse and concurrency=1 minimize proxy usage
- ✅ **Complete Data** - Name, website, phone, category, address, rating, reviews
- ✅ **Auto Pagination** - Scrolls through results until target reached
- ✅ **No External Visits** - Only scrapes Google Maps (no website crawling)
- ✅ **JSON Output** - Structured data pushed to Apify Dataset
- ✅ **API Ready** - Instant access via Apify API

### 📥 Input Parameters

```json
{
  "keyword": "restaurants",
  "location": "New York, NY",
  "maxResults": 50
}
````

| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
| `keyword` | string | ✅ Yes | - | Search term (e.g., "coffee shops", "plumbers") |
| `location` | string | ✅ Yes | - | Location to search (e.g., "Los Angeles, CA") |
| `maxResults` | integer | ❌ No | 50 | Max number of results (1-500) |
| `niche` | string | ❌ No | - | Optional label used to group results into a named dataset |
| `datasetName` | string | ❌ No | - | Optional exact dataset name to save results into |
| `perRunDataset` | boolean | ❌ No | false | If true, creates a timestamped dataset for this run |

- `dedupEnabled` | boolean | ❌ No | true | Enable or disable deduplication (recommended: true) |
  | `dedupScope` | string | ❌ No | `dataset` | `dataset` or `global` scope for deduplication |
  | `dedupKeyStrategy` | string | ❌ No | `place_id` | `place_id` preferred; fallback to `name_address` |
  | `dedupTTLdays` | integer | ❌ No | 0 | Days to keep an entry in dedup store (0 = no expiry) |
  | `dedupAutoAdd` | boolean | ❌ No | true | Automatically add saved records to dedup store |
  | `dedupSkip` | boolean | ❌ No | true | If true, duplicates are skipped (not saved); otherwise they are flagged |
  | `bulkQueries` | string | ❌ No | - | Multiline textarea. Each line: `keyword | location | tag(optional)` |
  | `autoCreateDatasets` | boolean | ❌ No | true | If true, creates/uses per-query datasets for bulk runs |
  | `datasetNameTemplate` | string | ❌ No | `gmaps-{tag || keyword}-{location}` | Template for dataset names |
  | `maxParallelQueries` | integer | ❌ No | 3 | Max concurrency for bulk queries |
  | `maxQueriesPerRun` | integer | ❌ No | 200 | Max queries processed in one run |
  **Note:** If you provide a custom `datasetName` or `niche`, the actor will sanitize it (lowercase, letters/numbers and hyphens only) to comply with Apify dataset naming rules; you’ll see a warning in logs if the name is modified.

### 📤 Output Format

Each business record includes:

```json
{
  "business_name": "Blue Bottle Coffee",
  "website_url": "https://bluebottlecoffee.com",
  "phone_number": "+1 415-555-0123",
  "category": "Coffee shop",
  "address": "66 Mint St, San Francisco, CA 94103",
  "google_maps_url": "https://www.google.com/maps/place/...",
  "rating": 4.5,
  "total_reviews": 1234
}
```

### 🎯 Usage

#### Via Apify Console

1. Go to [Apify Console](https://console.apify.com/actors)
2. Find your actor
3. Click **"Start"**
4. Enter input parameters
5. View results in **Dataset** tab

##### Example inputs

- Run into a per-niche dataset and skip duplicates automatically:

```json
{
  "keyword": "coffee shop",
  "location": "Seattle, WA",
  "niche": "coffee-shops-seattle",
  "deduplication": "per_niche",
  "dedupAuto": true
}
```

- Run into a timestamped snapshot dataset and generate a CSV file for download:

```json
{
  "keyword": "plumbers",
  "location": "Chicago, IL",
  "perRunDataset": true,
  "downloadAsCsv": true,
  "csvOptions": {
    "filename": "plumbers-chicago.csv",
    "includeDuplicates": false,
    "delimiter": ","
  }
}
```

> Tip: If you only want to collect results and do dedup manually later, set `dedupAuto=false` and duplicates will be marked with `is_duplicate: true` in the saved records.

#### Via API

##### Start a Run

```bash
curl -X POST https://api.apify.com/v2/acts/YOUR_ACTOR_ID/runs \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_API_TOKEN" \
  -d '{
    "keyword": "dentists",
    "location": "Chicago, IL",
    "maxResults": 100
  }'
```

##### Get Results

```bash
## Get latest run results as JSON
curl https://api.apify.com/v2/acts/YOUR_ACTOR_ID/runs/LAST/dataset/items?format=json \
  -H "Authorization: Bearer YOUR_API_TOKEN"

## Get as CSV
curl https://api.apify.com/v2/acts/YOUR_ACTOR_ID/runs/LAST/dataset/items?format=csv \
  -H "Authorization: Bearer YOUR_API_TOKEN"
```

#### Via JavaScript/Node.js

```javascript
import { ApifyClient } from 'apify-client';

const client = new ApifyClient({
    token: 'YOUR_API_TOKEN',
});

// Start the actor
const run = await client.actor('YOUR_ACTOR_ID').call({
    keyword: 'coffee shops',
    location: 'Seattle, WA',
    maxResults: 75,
});

// Get results
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(`Scraped ${items.length} businesses`);
items.forEach(business => {
    console.log(`${business.business_name} - ${business.rating}⭐`);
});
```

#### Via Python

```python
from apify_client import ApifyClient

client = ApifyClient('YOUR_API_TOKEN')

## Start the actor
run = client.actor('YOUR_ACTOR_ID').call(run_input={
    'keyword': 'plumbers',
    'location': 'Boston, MA',
    'maxResults': 50
})

## Get results
items = client.dataset(run['defaultDatasetId']).list_items().items
for item in items:
    print(f"{item['business_name']} - {item['rating']}⭐")
```

### 🔧 Configuration

#### Enable Proxies (Important!)

For best results, enable Apify residential proxies:

1. Go to Actor **Settings**
2. Scroll to **"Proxy configuration"**
3. Enable **"Apify proxy"**
4. Select **"Residential proxies"**
5. Click **"Save"**

#### Dataset CSV export

The actor no longer generates a separate CSV file automatically. You can download the dataset for any run as CSV directly from the Apify Console (Dataset tab) or via the API using the dataset `format=csv` parameter.

Example (download dataset as CSV via API):

```
GET https://api.apify.com/v2/datasets/{datasetId}/items?format=csv
```

#### Recommended Settings

- **Memory**: 2048 MB
- **Timeout**: 3600 seconds (1 hour)
- **Proxy**: Residential proxies

***

### 🇺🇸 Bulk query examples (USA) — 10+ niche input set ✅

You can paste the following sample directly into the `bulkQueries` textarea in the actor UI. Each line is a single query in the format `keyword | location | tag(optional)`. The actor will process each line as an independent query and (if `autoCreateDatasets` is enabled) create a dataset using the `datasetNameTemplate` (for example: `gmaps-home-contractors-united-states`).

**Run modes:**

- `auto` (default): the actor runs in bulk if `bulkQueries` is provided; otherwise single-query mode is used.
- `single`: explicit single-query mode — **requires** `keyword` and `location`.
- `bulk`: explicit bulk mode — **requires** `bulkQueries` (useful when you want to run a bulk job even if `keyword`/`location` are present).

This makes it convenient to use either workflow while keeping validation strict and user-friendly.

```
Home contractors | United States | home-contractors
HVAC services | United States | hvac
Recruitment agency | United States | recruitment-agency
Digital marketing agency | United States | digital-marketing
Plumbing contractors | United States | plumbers
Electrical contractors | United States | electricians
Landscaping services | United States | landscaping
Roofing contractors | United States | roofing
Cleaning services | United States | cleaning
IT managed services | United States | it-managed-services
Security services | United States | security
Accounting firms | United States | accounting
```

> ⚠️ Tip: Country-wide searches can return a lot of results — set a reasonable `maxResults` per query and use `maxParallelQueries` to balance speed vs resource usage.

You can also save this sample as `examples/preset-bulk-usa.txt` and copy/paste it into the UI or use it as a starting point for larger bulk runs.

### 📊 API Endpoints

After run completion, access results via:

```
## JSON format
GET https://api.apify.com/v2/datasets/{datasetId}/items?format=json

## CSV format
GET https://api.apify.com/v2/datasets/{datasetId}/items?format=csv

## Excel format
GET https://api.apify.com/v2/datasets/{datasetId}/items?format=xlsx

## XML format
GET https://api.apify.com/v2/datasets/{datasetId}/items?format=xml
```

**Quick access to latest run:**

```
GET https://api.apify.com/v2/acts/{actorId}/runs/LAST/dataset/items?format=json
```

### ⚡ Performance

| Results | Time | Est. Cost |
|---------|------|-----------|
| 10 | ~1 min | $0.01 |
| 50 | ~3 min | $0.03 |
| 100 | ~5 min | $0.05 |
| 500 | ~20 min | $0.20 |

*Times and costs are approximate and vary by location/density*

### 💰 Cost Optimization

This actor minimizes costs through:

1. **Session Reuse** - Single session used up to 50 times
2. **Low Concurrency** - Only 1 concurrent request
3. **Smart Scrolling** - Stops when target reached
4. **No External Crawling** - Never visits business websites
5. **Efficient Extraction** - Minimal wait times

### 🛠️ Development

#### Local Setup

```bash
## Clone repository
git clone https://github.com/techkaroshi-Assad/gmaps-scraper-apify-actor.git
cd gmaps-scraper-apify-actor

## Install dependencies
npm install

## Set Apify token
export APIFY_TOKEN=your_token_here

## Run locally
npm start
```

#### File Structure

```
.
├── main.js                 ## Main scraper logic
├── package.json           ## Dependencies
├── INPUT_SCHEMA.json      ## Input parameter definitions
├── Dockerfile             ## Docker configuration
├── .actor/
│   └── actor.json        ## Actor metadata
└── README.md             ## This file
```

#### User Guide & Local Testing

- For quick smoke tests that do not require browsers, run:

```bash
node test-smoke.js
```

This will validate dataset creation, Key-Value store behavior, and run summary saving to the `gmaps-run-summaries` dataset and KV store.

- To perform a full local run with browsers (Playwright), install Playwright browsers locally first (large download):

```bash
npx playwright install --with-deps
npx apify run
```

Note: the production Docker image used on Apify already includes browsers.

### 🐛 Troubleshooting

#### No Results Found

- Verify keyword and location are correct
- Test the same search on Google Maps manually
- Try more generic keywords (e.g., "restaurants" vs "vegan restaurants")

#### Missing Phone/Website

- Some businesses don't list this information on Google Maps
- This is expected behavior - the scraper gets all available data

#### Build Failures

- Ensure all 5 files are present
- Check `package.json` syntax
- Verify `Dockerfile` has no extra whitespace

#### Run Timeouts

- Reduce `maxResults` to test (try 10-20)
- Enable residential proxies in settings
- Increase timeout in Actor settings

### 📚 Resources

- [Apify Documentation](https://docs.apify.com)
- [Crawlee Documentation](https://crawlee.dev)
- [Playwright Documentation](https://playwright.dev)
- [Apify API Reference](https://docs.apify.com/api/v2)

### 🔒 Privacy & Terms

- This actor only scrapes publicly available data from Google Maps
- Respects Google's robots.txt and terms of service
- Uses residential proxies to avoid rate limiting
- No personal data collection beyond public business information

### 📝 License

IPO License-Tech\_Karoshi\_@Assad Balouch

ISC License - See LICENSE file for details

### 🤝 Support

- **Issues**: Open an issue in this repository
- **Dev Support**: techkaroshi@gmail.com
- **Apify Support**: support@apify.com
- **Documentation**: [Apify Docs](https://docs.apify.com)

### ⭐ Credits

Built with:

- [Apify SDK](https://sdk.apify.com)
- [Crawlee](https://crawlee.dev)
- [Playwright](https://playwright.dev)

***

**Made with ❤️ by techkaroshi-Assad**

*For questions or custom development, reach out via GitHub Issues*

# Actor input Schema

## `mode` (type: `string`):

Choose single-query or bulk mode. In 'bulk' mode provide `bulkQueries` (one per line); in 'single' mode provide `keyword` and `location`.

## `keyword` (type: `string`):

What to search for (e.g., 'restaurants', 'plumbers', 'dentists')

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

Where to search (e.g., 'New York, NY', 'Seattle, WA', 'California, United States' or 'United States'). Use city, state, or country formats as desired.

## `locationType` (type: `string`):

Optional hint for UI about location granularity. Not required — 'auto' will work with typical city/state/country strings.

## `maxResults` (type: `integer`):

Maximum number of results to scrape

## `niche` (type: `string`):

Optional label for this scraping niche, used to group results into a named dataset (e.g., 'coffee-shops')

## `datasetName` (type: `string`):

Optional exact dataset name to save results into. If not set, dataset name is derived from keyword/location or niche.

## `perRunDataset` (type: `boolean`):

If true, a timestamped dataset will be created for this run (useful for snapshots)

## `dedupEnabled` (type: `boolean`):

When enabled, previously seen leads are skipped according to deduplication scope and strategy.

## `dedupScope` (type: `string`):

Select whether deduplication applies per dataset or globally across all datasets.

## `dedupKeyStrategy` (type: `string`):

Primary key used for deduplication. 'place\_id' is preferred when available; fallback to 'name\_address' otherwise.

## `dedupTTLdays` (type: `integer`):

Number of days after which seen entries are considered forgotten (0 = never expire)

## `dedupAutoAdd` (type: `boolean`):

If true, newly scraped leads will be added to the dedup store automatically.

## `dedupSkip` (type: `boolean`):

If true, duplicates will be skipped (not saved) automatically. If false, duplicates are flagged in the dataset.

## `preset` (type: `string`):

Optional preset to pre-fill common search inputs (examples: coffee\_seattle, restaurants\_nyc)

## `bulkQueries` (type: `string`):

Optional multiline input where each line is 'keyword | location | tag(optional)'. The actor will process each line as a separate query.

## `autoCreateDatasets` (type: `boolean`):

If true, the actor will create or append to a dataset per query/tag automatically.

## `datasetNameTemplate` (type: `string`):

Template for per-query dataset names. Use {tag}, {keyword}, and {location} placeholders. Default: gmaps-{tag || keyword}-{location}

## `maxParallelQueries` (type: `integer`):

Maximum number of queries to process in parallel for a bulk run (useful for efficient compute).

## `maxQueriesPerRun` (type: `integer`):

Maximum number of bulk queries to process in a single run.

## `saveRunSummary` (type: `boolean`):

If true, a run summary (counts and metadata) will be saved to a dedicated dataset and KV store for the run.

## Actor input object example

```json
{
  "mode": "auto",
  "keyword": "coffee shops",
  "location": "San Francisco, CA",
  "locationType": "auto",
  "maxResults": 50,
  "niche": "coffee-shops",
  "datasetName": "gmaps-coffee-shops",
  "perRunDataset": false,
  "dedupEnabled": true,
  "dedupScope": "dataset",
  "dedupKeyStrategy": "place_id",
  "dedupTTLdays": 0,
  "dedupAutoAdd": true,
  "dedupSkip": true,
  "preset": "",
  "bulkQueries": "",
  "autoCreateDatasets": true,
  "datasetNameTemplate": "gmaps-{tag || keyword}-{location}",
  "maxParallelQueries": 3,
  "maxQueriesPerRun": 200,
  "saveRunSummary": 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 = {};

// Run the Actor and wait for it to finish
const run = await client.actor("cyber_orbit/gmaps-scraper-apify-actor-v-3-bulk-queries-supported").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 = {}

# Run the Actor and wait for it to finish
run = client.actor("cyber_orbit/gmaps-scraper-apify-actor-v-3-bulk-queries-supported").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{}' |
apify call cyber_orbit/gmaps-scraper-apify-actor-v-3-bulk-queries-supported --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=cyber_orbit/gmaps-scraper-apify-actor-v-3-bulk-queries-supported",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

```json
{
    "openapi": "3.0.1",
    "info": {
        "title": "Google Maps Lead Intelligence Scraper",
        "description": "Scalable Google Maps lead scraper for agencies and growth teams. Extracts clean business data with bulk mode, deduplication, and API-ready datasets. Built for reliable, low-cost lead generation and automation at scale.",
        "version": "0.0",
        "x-build-id": "wgUnsKggaMyOp0ZeH"
    },
    "servers": [
        {
            "url": "https://api.apify.com/v2"
        }
    ],
    "paths": {
        "/acts/cyber_orbit~gmaps-scraper-apify-actor-v-3-bulk-queries-supported/run-sync-get-dataset-items": {
            "post": {
                "operationId": "run-sync-get-dataset-items-cyber_orbit-gmaps-scraper-apify-actor-v-3-bulk-queries-supported",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for its completion, and returns Actor's dataset items in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        },
        "/acts/cyber_orbit~gmaps-scraper-apify-actor-v-3-bulk-queries-supported/runs": {
            "post": {
                "operationId": "runs-sync-cyber_orbit-gmaps-scraper-apify-actor-v-3-bulk-queries-supported",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor and returns information about the initiated run in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/runsResponseSchema"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/acts/cyber_orbit~gmaps-scraper-apify-actor-v-3-bulk-queries-supported/run-sync": {
            "post": {
                "operationId": "run-sync-cyber_orbit-gmaps-scraper-apify-actor-v-3-bulk-queries-supported",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for completion, and returns the OUTPUT from Key-value store in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        }
    },
    "components": {
        "schemas": {
            "inputSchema": {
                "type": "object",
                "properties": {
                    "mode": {
                        "title": "Run mode",
                        "enum": [
                            "auto",
                            "single",
                            "bulk"
                        ],
                        "type": "string",
                        "description": "Choose single-query or bulk mode. In 'bulk' mode provide `bulkQueries` (one per line); in 'single' mode provide `keyword` and `location`.",
                        "default": "auto"
                    },
                    "keyword": {
                        "title": "Search Keyword",
                        "type": "string",
                        "description": "What to search for (e.g., 'restaurants', 'plumbers', 'dentists')"
                    },
                    "location": {
                        "title": "Location",
                        "type": "string",
                        "description": "Where to search (e.g., 'New York, NY', 'Seattle, WA', 'California, United States' or 'United States'). Use city, state, or country formats as desired."
                    },
                    "locationType": {
                        "title": "Location type (optional)",
                        "enum": [
                            "auto",
                            "city",
                            "state",
                            "country",
                            "custom"
                        ],
                        "type": "string",
                        "description": "Optional hint for UI about location granularity. Not required — 'auto' will work with typical city/state/country strings.",
                        "default": "auto"
                    },
                    "maxResults": {
                        "title": "Maximum Results",
                        "minimum": 1,
                        "maximum": 500,
                        "type": "integer",
                        "description": "Maximum number of results to scrape",
                        "default": 50
                    },
                    "niche": {
                        "title": "Niche / Dataset Tag",
                        "type": "string",
                        "description": "Optional label for this scraping niche, used to group results into a named dataset (e.g., 'coffee-shops')"
                    },
                    "datasetName": {
                        "title": "Dataset Name",
                        "type": "string",
                        "description": "Optional exact dataset name to save results into. If not set, dataset name is derived from keyword/location or niche."
                    },
                    "perRunDataset": {
                        "title": "Create dataset per run",
                        "type": "boolean",
                        "description": "If true, a timestamped dataset will be created for this run (useful for snapshots)",
                        "default": false
                    },
                    "dedupEnabled": {
                        "title": "Enable deduplication",
                        "type": "boolean",
                        "description": "When enabled, previously seen leads are skipped according to deduplication scope and strategy.",
                        "default": true
                    },
                    "dedupScope": {
                        "title": "Deduplication scope",
                        "enum": [
                            "dataset",
                            "global"
                        ],
                        "type": "string",
                        "description": "Select whether deduplication applies per dataset or globally across all datasets.",
                        "default": "dataset"
                    },
                    "dedupKeyStrategy": {
                        "title": "Deduplication key strategy",
                        "enum": [
                            "place_id",
                            "name_address"
                        ],
                        "type": "string",
                        "description": "Primary key used for deduplication. 'place_id' is preferred when available; fallback to 'name_address' otherwise.",
                        "default": "place_id"
                    },
                    "dedupTTLdays": {
                        "title": "Deduplication TTL (days)",
                        "minimum": 0,
                        "type": "integer",
                        "description": "Number of days after which seen entries are considered forgotten (0 = never expire)",
                        "default": 0
                    },
                    "dedupAutoAdd": {
                        "title": "Automatically add to dedup store",
                        "type": "boolean",
                        "description": "If true, newly scraped leads will be added to the dedup store automatically.",
                        "default": true
                    },
                    "dedupSkip": {
                        "title": "Skip duplicates automatically",
                        "type": "boolean",
                        "description": "If true, duplicates will be skipped (not saved) automatically. If false, duplicates are flagged in the dataset.",
                        "default": true
                    },
                    "preset": {
                        "title": "Preset",
                        "enum": [
                            "",
                            "coffee_seattle",
                            "restaurants_nyc",
                            "plumbers_chicago",
                            "agency_saas_ai",
                            "bulk_sample"
                        ],
                        "type": "string",
                        "description": "Optional preset to pre-fill common search inputs (examples: coffee_seattle, restaurants_nyc)",
                        "default": ""
                    },
                    "bulkQueries": {
                        "title": "Bulk queries (one per line)",
                        "type": "string",
                        "description": "Optional multiline input where each line is 'keyword | location | tag(optional)'. The actor will process each line as a separate query.",
                        "default": ""
                    },
                    "autoCreateDatasets": {
                        "title": "Auto-create datasets per query",
                        "type": "boolean",
                        "description": "If true, the actor will create or append to a dataset per query/tag automatically.",
                        "default": true
                    },
                    "datasetNameTemplate": {
                        "title": "Dataset name template",
                        "type": "string",
                        "description": "Template for per-query dataset names. Use {tag}, {keyword}, and {location} placeholders. Default: gmaps-{tag || keyword}-{location}",
                        "default": "gmaps-{tag || keyword}-{location}"
                    },
                    "maxParallelQueries": {
                        "title": "Max parallel queries",
                        "minimum": 1,
                        "maximum": 20,
                        "type": "integer",
                        "description": "Maximum number of queries to process in parallel for a bulk run (useful for efficient compute).",
                        "default": 3
                    },
                    "maxQueriesPerRun": {
                        "title": "Max queries per run",
                        "minimum": 1,
                        "type": "integer",
                        "description": "Maximum number of bulk queries to process in a single run.",
                        "default": 200
                    },
                    "saveRunSummary": {
                        "title": "Save run summary",
                        "type": "boolean",
                        "description": "If true, a run summary (counts and metadata) will be saved to a dedicated dataset and KV store for the run.",
                        "default": true
                    }
                }
            },
            "runsResponseSchema": {
                "type": "object",
                "properties": {
                    "data": {
                        "type": "object",
                        "properties": {
                            "id": {
                                "type": "string"
                            },
                            "actId": {
                                "type": "string"
                            },
                            "userId": {
                                "type": "string"
                            },
                            "startedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "finishedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "status": {
                                "type": "string",
                                "example": "READY"
                            },
                            "meta": {
                                "type": "object",
                                "properties": {
                                    "origin": {
                                        "type": "string",
                                        "example": "API"
                                    },
                                    "userAgent": {
                                        "type": "string"
                                    }
                                }
                            },
                            "stats": {
                                "type": "object",
                                "properties": {
                                    "inputBodyLen": {
                                        "type": "integer",
                                        "example": 2000
                                    },
                                    "rebootCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "restartCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "resurrectCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "computeUnits": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "options": {
                                "type": "object",
                                "properties": {
                                    "build": {
                                        "type": "string",
                                        "example": "latest"
                                    },
                                    "timeoutSecs": {
                                        "type": "integer",
                                        "example": 300
                                    },
                                    "memoryMbytes": {
                                        "type": "integer",
                                        "example": 1024
                                    },
                                    "diskMbytes": {
                                        "type": "integer",
                                        "example": 2048
                                    }
                                }
                            },
                            "buildId": {
                                "type": "string"
                            },
                            "defaultKeyValueStoreId": {
                                "type": "string"
                            },
                            "defaultDatasetId": {
                                "type": "string"
                            },
                            "defaultRequestQueueId": {
                                "type": "string"
                            },
                            "buildNumber": {
                                "type": "string",
                                "example": "1.0.0"
                            },
                            "containerUrl": {
                                "type": "string"
                            },
                            "usage": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "integer",
                                        "example": 1
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "usageTotalUsd": {
                                "type": "number",
                                "example": 0.00005
                            },
                            "usageUsd": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "number",
                                        "example": 0.00005
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
```
