# Google Maps Tech Stack & Lead Enrichment (`laced_kirtan/google-maps-tech-stack-leads`) Actor

Finds public Google Maps business listings and optionally visits each public website for emails, social links, and deterministic technology signals.

- **URL**: https://apify.com/laced\_kirtan/google-maps-tech-stack-leads.md
- **Developed by:** [Shwetanshu Mehta](https://apify.com/laced_kirtan) (community)
- **Categories:** Lead generation, Marketing
- **Stats:** 2 total users, 1 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $3.50 / 1,000 enriched business leads

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?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — 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 Tech Stack & Lead Enrichment

This Actor reads public Google Maps search result HTML and returns the phone number when the listing publishes one. When a listing exposes a public website, it also makes a bounded same-site crawl for contact emails, social links, and deterministic technology markers. It does not use private credentials, CAPTCHA bypasses, undocumented authenticated APIs, or invented enrichment.

### Input

In the form view, add each search as a separate item in **Search keywords**. Do not paste the complete JSON object into that individual field.

If you use the full JSON input editor, paste the complete object below:

```json
{
  "searchKeywords": ["plumbers in Austin TX", "dental clinics in Miami"],
  "maxResults": 200,
  "maxPagesPerKeyword": 5,
  "maxRetries": 2,
  "proxyConfiguration": { "useApifyProxy": false },
  "languageCode": "en",
  "countryCode": "us",
  "deepCrawlWebsites": true,
  "detectTechStack": true
}
```

`maxResults` is the run-level cap. The Actor requests up to `maxPagesPerKeyword` pages of roughly 20 places per keyword and stops when a page has no new places. Failed or challenged Google result pages get at most `maxRetries` retries. Proxying is opt-in; when enabled, each attempt uses a fresh proxy session. Business website enrichment stays direct to avoid multiplying proxy bandwidth. Public-site markup and anti-bot behavior can change; challenge pages are detected, retries are bounded, and CAPTCHAs are never solved or bypassed. Unexpected empty result pages are reported as bounded, query-stripped warnings in `RUN_SUMMARY` instead of being silently treated as healthy.

### Output

Each dataset item contains `placeId`, `title`, `rating`, `address`, `phone`, `website`, `emails`, `socialLinks`, `techStack`, `techEvidence`, `hasMetaPixel`, and bounded crawl diagnostics. `phone` is the public Google Maps phone number when available; otherwise it is `null`. `techEvidence` explains why a technology was reported, and Universal Analytics is kept distinct from GA4. `enrichmentStatus`, `qualityScore`, and `isChargeable` make completeness explicit.

Example record:

```json
{
  "title": "Austin Premier Plumbing",
  "address": "123 Main St, Austin, TX",
  "phone": "+1 512-555-0100",
  "website": "https://austinpremierplumbing.com/",
  "emails": ["contact@austinpremierplumbing.com"],
  "techStack": ["WordPress", "Google Tag Manager"],
  "techEvidence": { "WordPress": ["HTML contains a WordPress asset or marker"] },
  "enrichmentStatus": "enriched",
  "qualityScore": 100,
  "isChargeable": true,
  "searchKeyword": "plumbers in Austin TX"
}
```

### Resource controls

- One search and one website page are processed at a time; no unbounded `Promise.all` fan-out.
- Response bodies are streamed with a hard byte limit before parsing.
- Website queues, visited URLs, emails, links, and technology markers are capped.
- Optional proxy dispatchers are short-lived and closed after each Google result-page attempt.
- Dataset records are pushed immediately instead of retaining the result set.

### Monetization

The manifest declares the `enriched-lead` pay-per-event at $0.0035 per useful record ($3.50 per 1,000). Listing-only rows without a phone, email, social link, or technology signal are still returned for transparency but are not charged.

### Local development

```bash
npm install
npm test
npm run check
apify validate-schema .actor/input_schema.json
apify run --purge
```

# Actor input Schema

## `searchKeywords` (type: `array`):

Add one Google Maps search phrase per item, such as pao bhaji in Bhilai or dental clinics in Pune. In the full JSON editor, this value must be an array; do not paste the surrounding input object into this field.

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

Hard run-level cap on emitted business records.

## `deepCrawlWebsites` (type: `boolean`):

Follow a small bounded number of same-site links for contact and technology signals.

## `detectTechStack` (type: `boolean`):

Detect technology markers from public HTML, scripts, meta tags, and response headers.

## `maxWebsitePages` (type: `integer`):

Maximum pages inspected per business website; the first page is always included when a website exists.

## `maxKeywords` (type: `integer`):

Safety cap on distinct keyword searches processed in one run.

## `maxPagesPerKeyword` (type: `integer`):

Maximum lightweight Google Maps result pages requested for each keyword. Each page can expose up to about 20 businesses; pagination stops early when Google returns no new places.

## `languageCode` (type: `string`):

Two- or three-letter result language code, for example en, hi, or es.

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

Two-letter country used to localize Google results, for example in, us, or gb.

## `maxRetries` (type: `integer`):

Bounded retries after failed or challenged Google result-page requests. A fresh proxy session is used for each attempt when proxying is enabled; CAPTCHAs are detected but never solved or bypassed.

## `proxyConfiguration` (type: `object`):

Optional Apify Proxy or custom proxy URLs for Google result pages only. Website enrichment stays direct. Residential proxy access can improve availability but adds proxy traffic cost.

## `requestTimeoutSecs` (type: `integer`):

Timeout per public HTML request.

## `maxResponseBytes` (type: `integer`):

Reject oversized pages before parsing them to protect memory.

## Actor input object example

```json
{
  "searchKeywords": [
    "pao bhaji in Bhilai"
  ],
  "maxResults": 1,
  "deepCrawlWebsites": true,
  "detectTechStack": true,
  "maxWebsitePages": 1,
  "maxKeywords": 1,
  "maxPagesPerKeyword": 1,
  "languageCode": "en",
  "countryCode": "us",
  "maxRetries": 1,
  "proxyConfiguration": {
    "useApifyProxy": false
  },
  "requestTimeoutSecs": 15,
  "maxResponseBytes": 6000000
}
```

# Actor output Schema

## `records` (type: `string`):

No description

## `runSummary` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

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

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "searchKeywords": [
        "pao bhaji in Bhilai"
    ],
    "maxResults": 1,
    "deepCrawlWebsites": false,
    "maxWebsitePages": 1,
    "maxKeywords": 1,
    "maxPagesPerKeyword": 1,
    "maxRetries": 1,
    "proxyConfiguration": {
        "useApifyProxy": false
    },
    "requestTimeoutSecs": 15
};

// Run the Actor and wait for it to finish
const run = await client.actor("laced_kirtan/google-maps-tech-stack-leads").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 = {
    "searchKeywords": ["pao bhaji in Bhilai"],
    "maxResults": 1,
    "deepCrawlWebsites": False,
    "maxWebsitePages": 1,
    "maxKeywords": 1,
    "maxPagesPerKeyword": 1,
    "maxRetries": 1,
    "proxyConfiguration": { "useApifyProxy": False },
    "requestTimeoutSecs": 15,
}

# Run the Actor and wait for it to finish
run = client.actor("laced_kirtan/google-maps-tech-stack-leads").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 '{
  "searchKeywords": [
    "pao bhaji in Bhilai"
  ],
  "maxResults": 1,
  "deepCrawlWebsites": false,
  "maxWebsitePages": 1,
  "maxKeywords": 1,
  "maxPagesPerKeyword": 1,
  "maxRetries": 1,
  "proxyConfiguration": {
    "useApifyProxy": false
  },
  "requestTimeoutSecs": 15
}' |
apify call laced_kirtan/google-maps-tech-stack-leads --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,laced_kirtan/google-maps-tech-stack-leads"
        }
    }
}

```

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/SsfiBhNn8AIjz6JSH/builds/2eJX9U9q4xnCttsNk/openapi.json
