# Google Maps Business & Website Contact Extractor (`glossy_craftsman/business-contact-enricher`) Actor

Extract verified public business contacts, company details, owned social profiles, technologies from Google Maps and hiring signals with resilient crawling.

- **URL**: https://apify.com/glossy_craftsman/business-contact-enricher.md
- **Developed by:** [Koo 빙](https://apify.com/glossy_craftsman) (community)
- **Categories:** Lead generation
- **Stats:** 2 total users, 1 monthly users, 80.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$10.00 / 1,000 qualified domain with contacts

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

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

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

### Find Google Maps businesses and website contacts in one run

Build a business contact list from **industry and location**, then enrich the discovered websites with public emails, phone numbers, fax numbers, contact pages and social links. **Maps Lead Qualifier & Contact Enricher** keeps branch contacts, company-wide contacts and contacts needing review separate, with source evidence you can inspect.

Use it for local prospect research, supplier discovery or enriching a list of websites you already have. Download a **four-sheet Excel workbook** for review, or use the dataset in your own workflow.

- Search Google Maps using multiple industry presets and countries, or type a specific industry.
- Check business activity against website evidence and your selected conditions.
- Keep Maps phone numbers while separating unrelated website numbers.
- See why a lead needs review instead of treating every extracted email as a verified branch contact.
- Export business summaries, contact details and supporting evidence together.

The Maps collector runs inside this Actor: no separately billed Maps Actor or Google Places API key is required. See Pricing below for billing conditions.

### Quick start

1. Open **Input → Form → Basic search**.
2. Choose **Find businesses on Google Maps**.
3. Select **Industries**, or enter a narrower activity in **Specific industry**.
4. Select a country and enter a city or region. Start with the prefilled **5-business** example.
5. Click **Start**. After the run, open **Business and contact results → Business summary**.
6. In the Output view selector, choose **Download Excel — All results**, then click the blue download button.

The starter Form uses a 90-second Maps budget, three pages per website and a 210-second collection budget. Saving results, contact checks and Excel export can add time. Existing saved tasks retain their inputs. Set the collection budget to 0 for unrestricted collection time, subject to other run limits.

For your first run, leave **Require a qualified primary email** and **Require a qualified primary phone** off so you can inspect incomplete results too. Turn them on when you need stricter contact requirements.

#### Example: commercial printers in Austin

Use the **Form** editor—no JSON is needed. Choose broad industries and countries from the menus, or type an exact activity. The actual input below selects **Commercial printer**, **United States**, **Austin, Texas**, **maximum 5 businesses**, and **Only scan businesses with a website**.

![Actual input Form with a country menu, specific industry and maximum 5 businesses](https://raw.githubusercontent.com/woobeen650-hue/apify-actor-images/main/google-maps-search-input.png)

### Actual example results

![Actual October 8 Console results: Sixth Street Printing and two Quik Print branches](https://raw.githubusercontent.com/woobeen650-hue/apify-actor-images/main/business-contact-results.png)

These are historical results from the **October 8, 2026** test, build `8pyjVmDubnuLBc6kV`, before the rc22 review improvements. They are not a new rc22 benchmark. Run status was `SUCCEEDED`; the supplied run report measured **115.679 seconds**. Results vary by site, location and access conditions.

| Observed measure | Result |
| --- | ---: |
| Business / branch rows | 3 |
| Unique website domains | 2 |
| Rows with a main phone | 3 |
| Rows with a primary branch email | 2 |
| Website scan failures | 0 |
| Excel sheets | 4 |

| Business / branch | Primary email | Main phone |
| --- | --- | --- |
| Sixth Street Printing | — | +15123229651 |
| Quik Print — Congress Ave | congress@qpaustin.com | +15124772014 |
| Quik Print — Shoal Creek Blvd | north@qpaustin.com | +15123319968 |

The two Quik Print listings share a website but have distinct supported branch emails. `info@qpaustin.com` is kept as a company contact. An extracted email is not automatically a qualified branch email.

Sixth Street's website published a different telephone number from Maps. The screenshot shows the older status; rc22 exposes this difference for review and separates inquiry purpose from branch ownership. rc22 also requires the full requested activity to be supported on the website when website evidence is required; a company-name keyword alone is insufficient. Do not interpret the screenshot's older `matched` labels as a fresh rc22 accuracy assessment.

The maximum of 5 was an upper limit; this run saved 3 rows. The supplied export does not establish the discovery stop reason or actual event charges. This small example is not an exhaustive market search, mailbox-delivery test or general performance guarantee.

### Read and export your results

Start with **Review status**, **Primary email**, **Main phone** and **Next action**. A `Phone review` status asks you to check differing Maps and website numbers. When another issue takes priority, the phone warning remains in **Next action** and **Quality & evidence**. Use **Company shared email** and **Company shared phone** when a contact belongs to the organization but is not confirmed for the selected branch.

| Output view / Excel sheet | What it contains |
| --- | --- |
| Business summary | One row per business, key contacts, website, address and next action |
| Contact details | Extracted contacts, normalized values, purpose, attribution and source evidence |
| Quality & evidence | Industry checks, warnings, page errors and reasons for review |
| All fields | Detailed exported fields for auditing and downstream processing |

The sheets share **Record ID**. Phone numbers are stored as text, preserving the leading `+`. The workbook includes filters, frozen headers and status colors.

#### Download Excel

Open **Output → Download Excel — All results** and click **Download Excel — All results**. The workbook contains four sheets; the number of businesses depends on your run.

- The download is a separate output view, not a link inside a business row.
- It includes **all saved rows in dataset order**, independently of temporary Console filters and sorting. Filter or sort in Excel after download.
- For large exports, download every numbered part.
- If authentication is required, use **Open file storage** and download the XLSX from your signed-in Apify Console.
- Native dataset export remains available for CSV/JSON and other supported formats.

Very long audit cells may be truncated to Excel's cell limit and are marked accordingly; the dataset retains full values. Export parts are bounded by business count and file size. An export failure does not erase the saved dataset.

### Choose your search conditions

| Setting | How to use it |
| --- | --- |
| Industries | Select one or more broad presets; a broad category is not proof of a narrow specialization |
| Specific industry | Describe the exact business activity, such as commercial printer or semiconductor equipment manufacturer |
| Required products or services | Add conditions that must all be supported by business evidence |
| Countries / territories | Select one or more countries |
| Cities or regions | Narrow the search; each entered location is combined with each selected country |
| Maximum businesses | Set an upper limit, not a guaranteed result count |
| Preview search plan only | Inspect SEARCH_PLAN without collecting businesses; start another run with preview off to collect |
| Require a qualified primary email / phone | Apply contact requirements to result qualification |

For independent city/country pairs, leave Countries empty and enter full locations such as `Austin, Texas, United States` and `Seoul, South Korea`.

You can also choose existing website or dataset inputs. Direct website enrichment uses company contact qualification because no individual Maps branch is requested.

### Enrich websites you already have

In **What would you like to do?**, choose the website enrichment option and paste your website URLs. No Maps discovery is needed. Direct website inputs use company-level contact qualification because no Maps branch is selected. Start with one or two domains before a larger list. Dataset input is also available for existing results and supported retry workflows.

### Contact and industry checks

Primary Maps contacts require branch evidence. A company email domain alone does not establish branch ownership. Other-branch and unverified contacts remain available for inspection rather than being silently promoted.

- Phone numbers are normalized and duplicate contact values within a business are merged with their evidence.
- Phone and fax are extracted separately; ambiguous TEL/FAX overlap is flagged.
- Email purpose helps distinguish general, sales, support, careers and other contacts. Unknown-purpose addresses are not automatically primary contacts; supported official contact blocks can establish general contact use.
- Email validation checks **syntax and domain MX records**, not mailbox existence or delivery.
- `website_supported` requires website evidence for the full requested detailed activity. `website_partial` means the website supports only part of the condition or a name-only keyword.
- Industry matching uses rules and dictionaries. Missing evidence remains `needs_review`; it is not proof that the business is unsuitable.
- Website evidence can support company activity without independently proving every branch's activity.

Explicitly labeled fax numbers are separated from telephone evidence when qualifying an email. JSON-LD email evidence preserves identity fields from the same object without borrowing another branch's details. Bounded activity-to-business-customer relations can support a commercial activity condition. Unlabeled multiple numbers, unresolved structured-data references and unsupported industry wording can still remain in review.

### Pricing

Check the Actor's **Pricing** tab for the current event price. The event is `domain-with-contacts`, displayed as **Qualified domain with contacts**. A displayed business row is not automatically a charge.

For a domain to qualify, the saved result must meet target and required-contact conditions, contain an eligible primary contact, and pass the website enrichment rule. That rule requires a qualifying website email or the supported phone-plus-social combination. A Maps-only phone on a failed or no-contact website scan is insufficient.

There is at most one charge attempt per canonical domain within a run/checkpoint workflow. Two branches sharing a domain therefore do not automatically mean two event charges. Independent runs are billed independently. Preview runs do not emit contact-result events.

For example, **3 business rows across 2 domains** allow at most 2 result events if both domains qualify; they do not imply 3 events. If the Pricing tab shows $0.01 per event, 2 confirmed events equal $0.02 in event fees. This is an illustration, not the measured charge for the sample run. Use the current displayed price.

An address-review or phone-review warning is not by itself a no-charge guarantee: another eligible contact can still satisfy the configured conditions. Check RUN_SUMMARY and the platform's run charge records for billing outcomes.

Event budgets limit event charges; they are not a blanket cap on every platform resource charge. Consult your current Apify plan and run settings for applicable compute, proxy and storage costs. No price or cost estimate is inferred from the example workbook.

### FAQ

#### Why did the run return fewer businesses than requested?

The limit is a maximum. Search availability, duplicates, filtering, blocking and the discovery time budget can reduce the count. Check `RUN_SUMMARY.collection_outcome` for the requested maximum, saved count and discovery stop reasons, then `MAPS_DISCOVERY` for query details. A successful run can still contain a partial search. Public Maps results do not provide exhaustive market coverage.

#### Why is an extracted email missing from Primary email?

It may be a company-wide contact, another branch, an unknown-purpose address or insufficiently supported. Inspect Contact details for its attribution and evidence. An email can be extracted successfully without being a qualified branch contact.

#### Does this Actor call another Maps Actor?

No. Discovery uses its own browser collector. It does not require a Google Places API key. Public page layouts and access restrictions can still affect collection.

#### Can I automate runs?

Use the Actor's API and scheduling options in Apify. Test a small input first, then reuse the configuration in your workflow.

#### Can I resume or retry?

Restarting with the same storage can reuse checkpoints. Advanced `resumeStoreId` workflows continue in the original Dataset/KVS; inspect RUN_SUMMARY for the results location. Do not share one checkpoint between concurrent runs. Dataset mode with `retryFailedOnly` supports explicit failed-site retries; blocked and robots-denied pages are not treated as ordinary retryable failures.

#### How do I report a problem?

Open the Actor's **Issues** tab with the Run ID, relevant input settings and one affected Record ID. Include the expected result and the observed warning. Do not include API tokens or signed download URLs.

### API / JSON example

The visual Form editor is the easiest starting point. The equivalent starter configuration below is for users who prefer JSON or want to automate runs.

This JSON matches the packaged quick-start Maps example. Change the industry and location first; increase time or page limits only after reviewing a small run. Saved tasks keep their previous inputs.

```json
{
  "sourceMode": "google_maps",
  "industries": [],
  "specificIndustry": "Commercial printer",
  "targetCountries": [
    "US"
  ],
  "targetLocations": [
    "Austin, Texas"
  ],
  "maxBusinesses": 5,
  "discoveryTimeoutSecs": 90,
  "maxPagesPerWebsite": 3,
  "recoverMissingWebsites": false,
  "previewOnly": false,
  "requireEmail": false,
  "requirePhone": false,
  "onlyMatchingBusinesses": false,
  "mapsMaxQueries": 3,
  "collectionTimeoutSecs": 210
}
```

# Changelog

This Actor's version history is a separate document: https://apify.com/glossy_craftsman/business-contact-enricher/changelog.md

# Actor input Schema

## `sourceMode` (type: `string`):

Choose Maps to find businesses and contacts together, or enrich websites you already have.

## `industries` (type: `array`):

Choose one or more broad industries. Add a specific industry below for a precise niche; a broad preset does not imply a particular subsector.

## `specificIndustry` (type: `string`):

Describe the exact business activity, such as semiconductor equipment manufacturer or dental clinic. This takes priority over broad presets.

## `targetProducts` (type: `array`):

Every phrase must be supported by business evidence. Example: semiconductor equipment, payroll software. This narrows both search and validation.

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

Optional search-query override. Search text never counts as business evidence. Industry/keyword filters still apply.

## `targetCountries` (type: `array`):

Select one or more countries. Type to narrow the list where supported by the Console.

## `targetLocations` (type: `array`):

Leave empty for country-level search coverage. With multiple countries, every city is combined with each selected country; use separate runs for unrelated city-country pairs.

## `maxBusinesses` (type: `integer`):

An upper limit, not a guaranteed result count. Search coverage and time limits may produce fewer businesses.

## `onlyBusinessesWithWebsites` (type: `boolean`):

Website recovery runs first. Unconfirmed businesses remain visible with their Maps contacts; this option skips their website scan.

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

Require a qualified primary email

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

Require a qualified primary phone

## `previewOnly` (type: `boolean`):

Save SEARCH_PLAN without Maps discovery or website scans. Review combinations and the maximum business count, then disable this option to collect data.

## `websites` (type: `array`):

Website mode only. One domain or URL per item. Duplicate domains are scanned once; shared-domain branches are retained in metadata.

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

Dataset mode only. Select a business or Google Maps dataset.

## `datasetWebsiteField` (type: `string`):

Optional dotted path, e.g. company.website. Auto-detects common website fields otherwise.

## `retryFailedOnly` (type: `boolean`):

Dataset mode only. Retry timeout, DNS, TLS, HTTP, parser and internal errors. Successful rows, blocked sites and robots denials are not retried.

## `excludeDatasetId` (type: `string`):

Skip prior businesses using Place ID or the same canonical business identity.

## `resumeStoreId` (type: `string`):

Use the original rc8 run Key-value store ID and identical search settings. Results continue in the original Dataset; open its link in RUN_SUMMARY. Do not run two processes against the same checkpoint.

## `businessTypes` (type: `array`):

Accept any selected role. When products are specified, the role and all requested products must appear together in a business-description segment.

## `targetAudience` (type: `string`):

Only explicit evidence counts; a business may serve both audiences.

## `matchingStrictness` (type: `string`):

Strict prioritizes stronger evidence. Uncertain records stay visible as Needs review.

## `requireWebsiteEvidence` (type: `boolean`):

Require first-party website activity evidence before marking the industry as matched. Maps-only evidence stays in review.

## `includeKeywords` (type: `array`):

All entered phrases must occur in website/category evidence, e.g. API 610.

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

Any matching phrase excludes a business. Use specific phrases; a website can mention services it does not offer.

## `onlyMatchingBusinesses` (type: `boolean`):

Off retains uncertain/excluded scan results for audit. Source-filter diagnostics always remain visible.

## `minimumRating` (type: `number`):

0 disables the filter. Missing ratings fail a non-zero minimum.

## `minimumReviewCount` (type: `integer`):

0 disables the filter. Missing counts fail a non-zero minimum.

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

Uses the source closure flag. Unknown is not treated as closed.

## `searchRadiusKm` (type: `number`):

Coordinate radius (km, optional)

## `centerLatitude` (type: `number`):

Required when radius is greater than zero. Latitude is not inferred from city text.

## `centerLongitude` (type: `number`):

Required when radius is greater than zero. Missing business coordinates are marked for review.

## `findEmails` (type: `boolean`):

Publicly displayed emails only; no guessed addresses or SMTP probes.

## `findPhones` (type: `boolean`):

Classify explicit main/sales/support/mobile labels; otherwise Unknown.

## `findFaxes` (type: `boolean`):

Keep fax numbers separate from telephone numbers.

## `findContactForms` (type: `boolean`):

Detect message/email forms; never submit them.

## `findSocials` (type: `boolean`):

Extract profiles linked by the website.

## `findContactPages` (type: `boolean`):

Return discovered contact/about links. Not all discovered links were necessarily visited.

## `findAddresses` (type: `boolean`):

Structured-data and address-element values with sources. Not postal validation.

## `emailTypes` (type: `array`):

Empty = all. Compliance, recruitment and webmaster/privacy addresses remain visible but are not primary sales contacts or sufficient for contact billing. Email purpose is rule-based; employee identity and branch ownership are not verified.

## `validationLevel` (type: `string`):

Fast skips DNS. Standard checks syntax/MX. Thorough increases automatic page budget to at least 15 (Fast remains capped at 2). No mailbox existence checks.

## `saveEvidence` (type: `boolean`):

Keep short classification excerpts. Contact sources remain available.

## `recoverMissingWebsites` (type: `boolean`):

Use a bounded public web search, then check company name plus phone or street address. Ambiguous candidates are kept unconfirmed. Adds up to the recovery time budget.

## `websiteRecoveryTimeoutSecs` (type: `integer`):

Maximum total time in seconds for finding and verifying missing business websites. This budget is separate from Google Maps discovery and website enrichment.

## `websiteRecoveryMaxBusinesses` (type: `integer`):

Maximum number of businesses without a website to attempt recovering per run. Unresolved businesses keep their available Google Maps contact details.

## `crawlDepth` (type: `string`):

Fast: max 2 HTTP pages. Balanced: 10. Deep: 30. Browser fallback limited to 3 simultaneous browsers.

## `maxTotalChargeUsd` (type: `number`):

0 uses the platform run limit. Applies to result events only; compute, proxy and platform fees are separate. The lower of this budget and the platform limit is enforced.

## `domainTimeoutSecs` (type: `integer`):

Maximum seconds per website

## `stallTimeoutSecs` (type: `integer`):

Stop after no website finishes (seconds)

## `browserConcurrency` (type: `integer`):

Concurrent website browser fallbacks

## `discoveryTimeoutSecs` (type: `integer`):

Total search/detail time budget for the built-in crawler, not per country. Already-read businesses are retained on timeout. Enrichment time is separate.

## `mapsSearchCoverage` (type: `string`):

Regional mode adds top-level ISO subdivisions for selected countries when no specific city is entered. This increases coverage, not nationwide completeness. Search and time limits still apply. Existing saved inputs retain their limits.

## `mapsMaxQueries` (type: `integer`):

Limits industry × location query combinations. Unstarted searches appear in the discovery summary.

## `maxPagesPerWebsite` (type: `integer`):

0 uses preset. Fast always caps at 2. Counts attempted pages, including failed requests.

## `concurrency` (type: `integer`):

0 = automatic: Fast 100, other modes 20. Higher values increase memory and blocking risk.

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

Per HTTP request. Retries extend total website time.

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

Retries timeouts, connection errors, 429 and selected 5xx responses with short backoff.

## `maxResponseSizeMB` (type: `integer`):

Decoded response limit per page. Oversized pages stop safely with RESPONSE_TOO_LARGE; TLS verification is never disabled. Larger limits increase memory usage.

## `mapsPageTimeoutSecs` (type: `integer`):

Maximum wait for one Maps page/list to load.

## `mapsMaxScrolls` (type: `integer`):

Stops on list end, repeated no-new-results, or this scroll limit.

## `mapsDetailConcurrency` (type: `integer`):

Uses one browser with a small bounded number of detail pages. Does not rotate identities or bypass blocks.

## `mapsScrollWaitMs` (type: `integer`):

Allows result cards to load after normal list scrolling.

## `mapsDetailWaitMs` (type: `integer`):

Allows website/address buttons to render after the business heading.

## `respectRobotsTxt` (type: `boolean`):

Honor fetched rules. Unavailable robots.txt does not block the entire website.

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

Optional. Used for HTTP and browser fallback. Proxy fees are separate.

## `includeExternalContactLinks` (type: `boolean`):

Follow explicitly linked contact pages on other public domains within the page budget. Contacts may belong to a third party; inspect their source URLs.

## `customUserAgent` (type: `string`):

Optional HTTP User-Agent. Does not change the robots.txt agent identity.

## `webhookUrl` (type: `string`):

Optional public HTTPS endpoint. Counters only, no contact lists. One attempt; use Apify integrations for managed retries.

## `industry` (type: `string`):

Older saved tasks/API compatibility. industries takes priority when populated.

## `processingMode` (type: `string`):

Legacy API mode. Use Website scan depth above for new runs.

## `validateEmails` (type: `boolean`):

RC5 compatibility. false disables MX checks.

## `collectionTimeoutSecs` (type: `integer`):

Stop starting new website scans after this budget and save completed results. Final checks and Excel export can add time. 0 disables the collection budget. Small starter runs use 210 seconds; this is not a hard total-runtime guarantee.

## Actor input object example

```json
{
  "sourceMode": "google_maps",
  "industries": [],
  "specificIndustry": "Commercial printer",
  "targetProducts": [],
  "targetCountries": [
    "US"
  ],
  "targetLocations": [
    "Austin, Texas"
  ],
  "maxBusinesses": 5,
  "onlyBusinessesWithWebsites": true,
  "requireEmail": false,
  "requirePhone": false,
  "previewOnly": false,
  "retryFailedOnly": false,
  "businessTypes": [],
  "targetAudience": "any",
  "matchingStrictness": "balanced",
  "requireWebsiteEvidence": true,
  "onlyMatchingBusinesses": false,
  "minimumRating": 0,
  "minimumReviewCount": 0,
  "excludeClosed": true,
  "searchRadiusKm": 0,
  "findEmails": true,
  "findPhones": true,
  "findFaxes": true,
  "findContactForms": true,
  "findSocials": true,
  "findContactPages": true,
  "findAddresses": true,
  "emailTypes": [],
  "validationLevel": "standard",
  "saveEvidence": true,
  "recoverMissingWebsites": true,
  "websiteRecoveryTimeoutSecs": 60,
  "websiteRecoveryMaxBusinesses": 10,
  "crawlDepth": "balanced",
  "maxTotalChargeUsd": 0,
  "domainTimeoutSecs": 180,
  "stallTimeoutSecs": 240,
  "browserConcurrency": 3,
  "discoveryTimeoutSecs": 90,
  "mapsSearchCoverage": "regional",
  "mapsMaxQueries": 3,
  "maxPagesPerWebsite": 3,
  "concurrency": 0,
  "requestTimeoutSecs": 15,
  "maxRetries": 1,
  "maxResponseSizeMB": 5,
  "mapsPageTimeoutSecs": 25,
  "mapsMaxScrolls": 15,
  "mapsDetailConcurrency": 2,
  "mapsScrollWaitMs": 1000,
  "mapsDetailWaitMs": 1000,
  "respectRobotsTxt": true,
  "proxyConfiguration": {
    "useApifyProxy": false
  },
  "includeExternalContactLinks": false,
  "processingMode": "standard",
  "validateEmails": true,
  "collectionTimeoutSecs": 210
}
```

# Actor output Schema

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

No description

## `downloads` (type: `string`):

Separate download page for all saved business results. Files are available after export completes; large results may have multiple parts.

# 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 = {
    "sourceMode": "google_maps",
    "industries": [],
    "specificIndustry": "Commercial printer",
    "targetCountries": [
        "US"
    ],
    "targetLocations": [
        "Austin, Texas"
    ],
    "maxBusinesses": 5,
    "requireEmail": false,
    "requirePhone": false,
    "previewOnly": false,
    "onlyMatchingBusinesses": false,
    "recoverMissingWebsites": false,
    "discoveryTimeoutSecs": 90,
    "mapsMaxQueries": 3,
    "maxPagesPerWebsite": 3,
    "collectionTimeoutSecs": 210
};

// Run the Actor and wait for it to finish
const run = await client.actor("glossy_craftsman/business-contact-enricher").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 = {
    "sourceMode": "google_maps",
    "industries": [],
    "specificIndustry": "Commercial printer",
    "targetCountries": ["US"],
    "targetLocations": ["Austin, Texas"],
    "maxBusinesses": 5,
    "requireEmail": False,
    "requirePhone": False,
    "previewOnly": False,
    "onlyMatchingBusinesses": False,
    "recoverMissingWebsites": False,
    "discoveryTimeoutSecs": 90,
    "mapsMaxQueries": 3,
    "maxPagesPerWebsite": 3,
    "collectionTimeoutSecs": 210,
}

# Run the Actor and wait for it to finish
run = client.actor("glossy_craftsman/business-contact-enricher").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 '{
  "sourceMode": "google_maps",
  "industries": [],
  "specificIndustry": "Commercial printer",
  "targetCountries": [
    "US"
  ],
  "targetLocations": [
    "Austin, Texas"
  ],
  "maxBusinesses": 5,
  "requireEmail": false,
  "requirePhone": false,
  "previewOnly": false,
  "onlyMatchingBusinesses": false,
  "recoverMissingWebsites": false,
  "discoveryTimeoutSecs": 90,
  "mapsMaxQueries": 3,
  "maxPagesPerWebsite": 3,
  "collectionTimeoutSecs": 210
}' |
apify call glossy_craftsman/business-contact-enricher --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,glossy_craftsman/business-contact-enricher"
        }
    }
}
```

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/dBuWLzfHuybvk4JEh/builds/jZ3jeGSGmxcpz4khv/openapi.json
