# Google Maps CEE Supplier Shortlist Builder (`kondasviktor/google-maps-cee-scraper`) Actor

Build evidence-based local supplier shortlists from Google Maps for CEE procurement and sales teams. Transparent readiness scoring, risk flags, CEE language support, and optional BYOK AI summaries.

- **URL**: https://apify.com/kondasviktor/google-maps-cee-scraper.md
- **Developed by:** [Viktor Kondas](https://apify.com/kondasviktor) (community)
- **Categories:** Lead generation, Automation, AI
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.50 / 1,000 supplier record saveds

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.

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

## 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/docs.md):

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

In Python projects, use official [Python client library](https://docs.apify.com/api/client/python/docs.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/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 CEE Supplier Shortlist Builder

**Build evidence-based local supplier shortlists from Google Maps for CEE procurement and sales teams.**

### What it does

- Scrapes **local service provider** listings from Google Maps for a city or region (HU, RO, PL, CZ, SK, AT).
- Resolves locations via **OpenStreetMap Nominatim** or custom **GeoJSON**.
- Returns structured JSON/CSV/Excel with phone, website, ratings, coordinates, and shortlist fields.
- **Procurement mode** adds transparent supplier readiness scoring, risk flags, and shortlist status.
- Optional **multi-provider AI scoring** (BYOK): Gemini, Claude, ChatGPT, or OpenRouter.
- Optional **Google Sheets** export (service account).
- Built for **n8n**, **Make**, **Zapier**, and vibe-coder agent workflows.

**Optional enrichment:** pipe result `website` URLs into the sibling **Website Intelligence + Contact Extractor** actor for emails, tech stack, and EU compliance flags.

### Who it's for

1. **Procurement teams** building first-pass supplier shortlists for clinics, catering, cleaning, security, maintenance, facilities, and local IT services.
2. **Sales teams** building contactable SME lead lists by city or district.
3. **Vibe coders** powering AI supplier-discovery agents.
4. **Market researchers** auditing service density in CEE cities.
5. **Agencies** running competitor mapping for clients.

### What this score is — and is not

This actor helps you **discover and rank public Google Maps evidence**. It does **not** verify legal entity status, insurance, certifications, financial health, pricing, or formal vendor approval.

Use the output for:
- supplier discovery
- shortlist preparation
- outreach prioritization

Do **not** use it as the sole basis for contract award or formal vendor qualification.

### What data you get

| Field | Description |
|-------|-------------|
| `placeId` | Stable Google Maps place identifier |
| `title` | Business name |
| `categoryName` | Primary category |
| `address`, `city`, `postalCode` | Parsed location |
| `phone`, `website` | Contact channels |
| `totalScore`, `reviewsCount` | Rating signals |
| `matchedSearchTerms` | Search terms that surfaced this supplier |
| `supplierReadinessScore` | 0–100 shortlist readiness score |
| `shortlistStatus` | `READY`, `REVIEW`, `LOW_EVIDENCE`, `EXCLUDE` |
| `scoreConfidence` | `HIGH`, `MEDIUM`, `LOW` |
| `shortlistReasons` | Human-readable reasons |
| `riskFlags` | Missing contact, weak evidence, category mismatch, etc. |
| `shortlistReady` | True when status is `READY` |
| `contactableLead` | Leads mode only — has phone/website and rating ≥ 3.5 |
| `aiScore`, `aiSummary` | When AI scoring enabled |
| `reviews[]` | When `includeReviews` is true |

Legacy aliases `procurementScore` and `vendorTier` remain for backward compatibility.

### How to use it

1. **Configure input** — search terms, location, country code, language, limits.
2. **Run** on Apify cloud (recommended) or locally with `apify run`.
3. **Download** dataset as JSON, CSV, or Excel from the run page.

### Input presets

#### Budapest dental clinics

```json
{
  "searchTerms": ["fogászat", "dental"],
  "location": "Budapest, Hungary",
  "countryCode": "HU",
  "language": "hu",
  "maxResultsPerSearch": 5,
  "outputMode": "procurement"
}
````

Store input **prefill** uses this small cap so Apify automated QA finishes within 5 minutes. For real shortlists, raise `maxResultsPerSearch` (e.g. 50–100) and/or add terms like `fogorvos`.

Published Store example: [Budapest dental clinics shortlist](https://apify.com/kondasviktor/google-maps-cee-scraper/examples/budapest-dental-clinics-google-maps-supplier-shortlist).

#### Budapest cleaning suppliers

```json
{
  "searchTerms": ["takarítás", "cleaning service", "facility management"],
  "location": "Budapest, Hungary",
  "countryCode": "HU",
  "language": "hu",
  "maxResultsPerSearch": 100,
  "minRating": 3.5,
  "outputMode": "procurement"
}
```

#### Warsaw IT vendors

```json
{
  "searchTerms": ["software development", "IT outsourcing", "web agency"],
  "location": "Warsaw, Poland",
  "countryCode": "PL",
  "language": "pl",
  "maxResultsPerSearch": 50,
  "outputMode": "leads"
}
```

#### CEE catering suppliers

```json
{
  "searchTerms": ["catering", "étterem", "restaurant catering"],
  "location": "Budapest, Hungary",
  "countryCode": "HU",
  "maxResultsPerSearch": 200,
  "includeReviews": true,
  "maxReviewsPerPlace": 5,
  "outputMode": "procurement",
  "enableAIScoring": true,
  "aiProvider": "openrouter",
  "openrouterApiKey": "YOUR_KEY"
}
```

### Output example

```json
{
  "placeId": "ChIJ...",
  "title": "Smile Dental Clinic",
  "categoryName": "Dentist",
  "address": "Budapest, Váci út 33, 1134",
  "phone": "+36 1 234 5678",
  "website": "https://example.hu",
  "totalScore": 4.7,
  "reviewsCount": 112,
  "matchedSearchTerms": ["fogászat", "fogorvos"],
  "supplierReadinessScore": 84,
  "shortlistStatus": "READY",
  "scoreConfidence": "HIGH",
  "shortlistReady": true,
  "shortlistReasons": ["Strong adjusted rating", "Multiple contact channels"],
  "riskFlags": [],
  "googleMapsUrl": "https://www.google.com/maps/place/...",
  "scrapedAt": "2026-07-15T12:00:00.000Z"
}
```

### Supplier readiness scoring

Deterministic 0–100 score (no AI required):

| Component | Max points |
|-----------|------------|
| Bayesian-adjusted reputation | 25 |
| Review confidence (log scale) | 20 |
| Contactability (website + phone) | 25 |
| Location completeness | 15 |
| Category/search relevance | 10 |
| Operational listing evidence | 5 |

**Shortlist status:**

- `READY` — strong evidence, contactable, complete enough for outreach
- `REVIEW` — promising but needs manual check
- `LOW_EVIDENCE` — too little public review evidence
- `EXCLUDE` — permanently closed or no contact channel

### AI scoring (BYOK — bring your own key)

Scraping and shortlist scoring work **without any LLM key**. AI is optional and summarizes **only the public evidence collected**.

When `enableAIScoring` is true, choose **`aiProvider`** and paste your API key:

| Provider | Input key field | Get key |
|----------|-----------------|---------|
| **Gemini** (default in auto) | `geminiApiKey` | [Google AI Studio](https://aistudio.google.com/app/apikey) |
| **Claude** | `anthropicApiKey` | [Anthropic Console](https://console.anthropic.com/) |
| **ChatGPT** | `openaiApiKey` | [OpenAI API keys](https://platform.openai.com/api-keys) |
| **OpenRouter** | `openrouterApiKey` | [OpenRouter](https://openrouter.ai/keys) |
| **auto** | First available key above | Tries Gemini → Claude → OpenAI → OpenRouter |

**OpenRouter model:** defaults to `google/gemma-4-31b-it:free`. Override with `aiModel`.

### Google Sheets integration

1. Create a Google Cloud service account with Sheets API access.
2. Set `GOOGLE_SERVICE_ACCOUNT_KEY` in actor environment (JSON string).
3. Share the target spreadsheet with the service account email (Editor).
4. Enable `enableGoogleSheetsExport` and pass `googleSheetsUrl`.

### CEE language support

The actor is built for CEE markets (HU, RO, PL, CZ, SK, AT). Here is what is supported:

| Feature | Support |
|---------|---------|
| **Search terms in any language** | Yes — pass local keywords directly, e.g. `"fogászat"` for Hungary |
| **Maps UI language (`language` input)** | Yes — sets `?hl=hu` (or `ro`, `pl`, `cs`, `sk`, `de`) on search URLs |
| **Location geocoding** | Yes — Nominatim resolves `"Budapest, Hungary"` with `countryCode: "HU"` |
| **Category/relevance scoring** | Yes — accent-normalized matching against your `searchTerms` |
| **Localized DOM labels (verified, closed, reviews)** | Partial — HU/PL/RO/CZ patterns plus English; Maps UI changes can affect extraction |

**Recommended Hungary dental run:**

```json
{
  "searchTerms": ["fogászat", "fogorvos", "dental"],
  "location": "Budapest, Hungary",
  "countryCode": "HU",
  "language": "hu",
  "maxResultsPerSearch": 50,
  "outputMode": "procurement"
}
```

You do **not** need English terms like `"dental"` — they broaden coverage but are optional. Combining local + English terms often finds more suppliers because Google Maps indexes both.

### Secrets and local development

- **Never commit** `apify/google-maps-cee-scraper/.env` — it is listed in `.gitignore`.
- **`apify push` excludes** `.env` via `.gitignore` and `.actorignore`.
- **Docker builds exclude** `.env` via `.dockerignore`.
- On Apify cloud, customers paste keys into **input fields** (`openrouterApiKey`, etc.) marked as secrets — not from your local `.env`.
- For local runs only, copy `.env.example` → `.env` and add `APIFY_TOKEN` + optional `OPENROUTER_API_KEY`.
- Only `.env.example` (placeholders, no real keys) is in git.

### FAQ

**Is scraping Google Maps legal?**\
You are responsible for compliance with Google’s Terms of Service and local laws. This actor collects publicly visible listing data for legitimate business research.

**How is this different from generic Google Maps scrapers?**\
This actor is focused on **CEE local-service supplier shortlists** with transparent readiness scoring, risk flags, matched search terms, and optional BYOK evidence summaries — not raw record dumps.

**How many results can I get per run?**\
Up to **500 per search term** (`maxResultsPerSearch`). Multiple terms multiply discovery; duplicates are removed by `placeId`.

**What is AI scoring and does it cost extra on Apify?**\
AI scoring is optional and **BYOK**. You pay your LLM provider directly. The actor’s Apify fee is per saved supplier record. Scraping works with zero LLM keys.

### Local development

```bash
cd apify/google-maps-cee-scraper
npm install
npm run build
npm test
apify run --input-file=INPUT.test.json
npx playwright install chromium
```

### Pricing

Pay-per-event: **$2.50 per 1,000 supplier records** (`place` event), minimum charge **$0.10** per run.

Enable **Pay per event + usage** in Apify Console so platform compute/proxy costs are passed through to users. That keeps developer margins healthy while remaining competitive for decision-ready shortlists rather than raw scraping.

### Related actors (suite)

| Actor | Role |
|-------|------|
| **Google Maps CEE Supplier Shortlist** (this Actor) | Discover local suppliers |
| [Website Intelligence + Contact](https://apify.com/kondasviktor/website-intelligence-contact-extractor) | Enrich websites (contacts, tech, compliance) |
| [Dealer / Distributor Locator](https://apify.com/kondasviktor/dealer-distributor-locator) | Expand brand locator pages |
| [PDF Procurement Document Extractor](https://apify.com/kondasviktor/pdf-procurement-document-extractor) | Find certs / reports / datasheets |
| [Website Change Monitor](https://apify.com/kondasviktor/website-change-monitor) | Watch pages for changes |

Each Actor runs standalone; use them as an optional suite when chaining discovery → enrichment → monitoring.

### Feedback

Open an issue on the actor’s Apify page or contact [Vibe Coder's Life](https://vibecoderslife.com/#contact).

# Actor input Schema

## `searchTerms` (type: `array`):

Local-service supplier keywords (e.g. fogászat, dental clinic, catering). Combine local-language and English terms for best CEE coverage.

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

City or region (e.g. Budapest, Hungary).

## `countryCode` (type: `string`):

ISO 3166-1 alpha-2 country code (HU, RO, PL, CZ, SK, AT).

## `language` (type: `string`):

Google Maps UI language (en, hu, ro, pl, cs, sk, de).

## `maxResultsPerSearch` (type: `integer`):

Maximum places to collect per search term (max 500). Prefill stays low so Apify Store QA finishes under 5 minutes; raise this for production runs.

## `includeReviews` (type: `boolean`):

Extract review snippets for stronger shortlist evidence and optional BYOK AI summaries.

## `maxReviewsPerPlace` (type: `integer`):

Cap reviews when includeReviews is enabled.

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

Filter out places below this star rating.

## `minReviewCount` (type: `integer`):

Filter out places with fewer reviews.

## `enableAIScoring` (type: `boolean`):

Optional BYOK enrichment: evidence-based supplier summary per place. Requires your LLM API key below.

## `aiProvider` (type: `string`):

LLM provider for AI scoring. Use auto to pick the first available key (Gemini → Claude → OpenAI → OpenRouter).

## `aiModel` (type: `string`):

OpenRouter model slug when aiProvider is openrouter or auto (OpenRouter leg). Default: google/gemma-4-31b-it:free. Override with any OpenRouter model, e.g. google/gemma-4-26b-a4b-it:free or anthropic/claude-haiku-4.5.

## `geminiApiKey` (type: `string`):

Google AI Studio key for AI scoring when aiProvider is gemini or auto.

## `anthropicApiKey` (type: `string`):

Claude API key when aiProvider is claude or auto.

## `openaiApiKey` (type: `string`):

ChatGPT API key when aiProvider is openai or auto.

## `openrouterApiKey` (type: `string`):

OpenRouter key when aiProvider is openrouter or auto.

## `enableGoogleSheetsExport` (type: `boolean`):

Append results to a spreadsheet (requires GOOGLE\_SERVICE\_ACCOUNT\_KEY).

## `googleSheetsUrl` (type: `string`):

Spreadsheet URL when export is enabled.

## `outputMode` (type: `string`):

standard = raw fields; procurement = supplier shortlist scoring; leads = contactable lead flag only.

## `customGeolocation` (type: `object`):

Optional Point, Polygon, or MultiPolygon to override Nominatim lookup.

## Actor input object example

```json
{
  "searchTerms": [
    "fogászat",
    "dental"
  ],
  "location": "Budapest, Hungary",
  "countryCode": "HU",
  "language": "hu",
  "maxResultsPerSearch": 5,
  "includeReviews": false,
  "maxReviewsPerPlace": 10,
  "minRating": 0,
  "minReviewCount": 0,
  "enableAIScoring": false,
  "aiProvider": "auto",
  "enableGoogleSheetsExport": false,
  "outputMode": "procurement"
}
```

# Actor output Schema

## `suppliers` (type: `string`):

Deduplicated Google Maps supplier records with procurement shortlist scoring.

## `runOverview` (type: `string`):

Open the run in Apify Console to inspect logs, Live View, and billing.

# 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 = {
    "searchTerms": [
        "fogászat",
        "dental"
    ],
    "location": "Budapest, Hungary",
    "countryCode": "HU",
    "language": "hu",
    "maxResultsPerSearch": 5,
    "aiProvider": "auto",
    "outputMode": "procurement"
};

// Run the Actor and wait for it to finish
const run = await client.actor("kondasviktor/google-maps-cee-scraper").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 = {
    "searchTerms": [
        "fogászat",
        "dental",
    ],
    "location": "Budapest, Hungary",
    "countryCode": "HU",
    "language": "hu",
    "maxResultsPerSearch": 5,
    "aiProvider": "auto",
    "outputMode": "procurement",
}

# Run the Actor and wait for it to finish
run = client.actor("kondasviktor/google-maps-cee-scraper").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 '{
  "searchTerms": [
    "fogászat",
    "dental"
  ],
  "location": "Budapest, Hungary",
  "countryCode": "HU",
  "language": "hu",
  "maxResultsPerSearch": 5,
  "aiProvider": "auto",
  "outputMode": "procurement"
}' |
apify call kondasviktor/google-maps-cee-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=kondasviktor/google-maps-cee-scraper",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

```json
{
    "openapi": "3.0.1",
    "info": {
        "title": "Google Maps CEE Supplier Shortlist Builder",
        "description": "Build evidence-based local supplier shortlists from Google Maps for CEE procurement and sales teams. Transparent readiness scoring, risk flags, CEE language support, and optional BYOK AI summaries.",
        "version": "0.3",
        "x-build-id": "CqLEOhsPBaXwysaXe"
    },
    "servers": [
        {
            "url": "https://api.apify.com/v2"
        }
    ],
    "paths": {
        "/acts/kondasviktor~google-maps-cee-scraper/run-sync-get-dataset-items": {
            "post": {
                "operationId": "run-sync-get-dataset-items-kondasviktor-google-maps-cee-scraper",
                "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/kondasviktor~google-maps-cee-scraper/runs": {
            "post": {
                "operationId": "runs-sync-kondasviktor-google-maps-cee-scraper",
                "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/kondasviktor~google-maps-cee-scraper/run-sync": {
            "post": {
                "operationId": "run-sync-kondasviktor-google-maps-cee-scraper",
                "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",
                "required": [
                    "searchTerms",
                    "location",
                    "countryCode"
                ],
                "properties": {
                    "searchTerms": {
                        "title": "Search terms",
                        "type": "array",
                        "description": "Local-service supplier keywords (e.g. fogászat, dental clinic, catering). Combine local-language and English terms for best CEE coverage.",
                        "items": {
                            "type": "string"
                        }
                    },
                    "location": {
                        "title": "Location",
                        "type": "string",
                        "description": "City or region (e.g. Budapest, Hungary)."
                    },
                    "countryCode": {
                        "title": "Country code",
                        "type": "string",
                        "description": "ISO 3166-1 alpha-2 country code (HU, RO, PL, CZ, SK, AT)."
                    },
                    "language": {
                        "title": "Language",
                        "type": "string",
                        "description": "Google Maps UI language (en, hu, ro, pl, cs, sk, de).",
                        "default": "en"
                    },
                    "maxResultsPerSearch": {
                        "title": "Max results per search term",
                        "minimum": 1,
                        "maximum": 500,
                        "type": "integer",
                        "description": "Maximum places to collect per search term (max 500). Prefill stays low so Apify Store QA finishes under 5 minutes; raise this for production runs.",
                        "default": 50
                    },
                    "includeReviews": {
                        "title": "Include reviews",
                        "type": "boolean",
                        "description": "Extract review snippets for stronger shortlist evidence and optional BYOK AI summaries.",
                        "default": false
                    },
                    "maxReviewsPerPlace": {
                        "title": "Max reviews per place",
                        "minimum": 1,
                        "maximum": 50,
                        "type": "integer",
                        "description": "Cap reviews when includeReviews is enabled.",
                        "default": 10
                    },
                    "minRating": {
                        "title": "Minimum rating",
                        "minimum": 0,
                        "maximum": 5,
                        "type": "number",
                        "description": "Filter out places below this star rating.",
                        "default": 0
                    },
                    "minReviewCount": {
                        "title": "Minimum review count",
                        "minimum": 0,
                        "type": "integer",
                        "description": "Filter out places with fewer reviews.",
                        "default": 0
                    },
                    "enableAIScoring": {
                        "title": "Enable AI scoring",
                        "type": "boolean",
                        "description": "Optional BYOK enrichment: evidence-based supplier summary per place. Requires your LLM API key below.",
                        "default": false
                    },
                    "aiProvider": {
                        "title": "AI provider",
                        "enum": [
                            "auto",
                            "gemini",
                            "claude",
                            "openai",
                            "openrouter"
                        ],
                        "type": "string",
                        "description": "LLM provider for AI scoring. Use auto to pick the first available key (Gemini → Claude → OpenAI → OpenRouter).",
                        "default": "auto"
                    },
                    "aiModel": {
                        "title": "OpenRouter model",
                        "type": "string",
                        "description": "OpenRouter model slug when aiProvider is openrouter or auto (OpenRouter leg). Default: google/gemma-4-31b-it:free. Override with any OpenRouter model, e.g. google/gemma-4-26b-a4b-it:free or anthropic/claude-haiku-4.5."
                    },
                    "geminiApiKey": {
                        "title": "Gemini API key",
                        "type": "string",
                        "description": "Google AI Studio key for AI scoring when aiProvider is gemini or auto."
                    },
                    "anthropicApiKey": {
                        "title": "Anthropic API key",
                        "type": "string",
                        "description": "Claude API key when aiProvider is claude or auto."
                    },
                    "openaiApiKey": {
                        "title": "OpenAI API key",
                        "type": "string",
                        "description": "ChatGPT API key when aiProvider is openai or auto."
                    },
                    "openrouterApiKey": {
                        "title": "OpenRouter API key",
                        "type": "string",
                        "description": "OpenRouter key when aiProvider is openrouter or auto."
                    },
                    "enableGoogleSheetsExport": {
                        "title": "Export to Google Sheets",
                        "type": "boolean",
                        "description": "Append results to a spreadsheet (requires GOOGLE_SERVICE_ACCOUNT_KEY).",
                        "default": false
                    },
                    "googleSheetsUrl": {
                        "title": "Google Sheets URL",
                        "type": "string",
                        "description": "Spreadsheet URL when export is enabled."
                    },
                    "outputMode": {
                        "title": "Output mode",
                        "enum": [
                            "standard",
                            "procurement",
                            "leads"
                        ],
                        "type": "string",
                        "description": "standard = raw fields; procurement = supplier shortlist scoring; leads = contactable lead flag only.",
                        "default": "procurement"
                    },
                    "customGeolocation": {
                        "title": "Custom geolocation (GeoJSON)",
                        "type": "object",
                        "description": "Optional Point, Polygon, or MultiPolygon to override Nominatim lookup."
                    }
                }
            },
            "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
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
```
