# Chamber of Commerce Scraper | US Business Leads (`b2b_leads/chamberofcommerce-real-time-data-scraper`) Actor

Collect Chamber of Commerce business data in real time. Search a keyword and any US city to stream names, phones, websites, emails, addresses, employee size, key contacts, ratings, and reviews as clean JSON — with webhooks, per-result pricing, and a free plan sample.

- **URL**: https://apify.com/b2b_leads/chamberofcommerce-real-time-data-scraper.md
- **Developed by:** [Emmanuel](https://apify.com/b2b_leads) (community)
- **Stats:** 4 total users, 2 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.00 / 1,000 results

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

## Chamber of Commerce Real-Time Data

Live Chamber of Commerce business directory intelligence: keyword + US city search, full business records, and lead enrichment with phones, websites, emails, employee size, and key contacts. Checkbox features — enable only what you need. Structured JSON streamed to your dataset in real time.

> **Free plan notice:** on the Apify Free plan this Actor exports a small sample of results per run (2 records). Upgrade to a paid Apify plan for full, unlimited exports.

### Who is this for

Built for teams that sell to, research, or audit US local businesses:

- **Lead generation** — pull plumbers, dentists, insurance agencies, contractors, chambers of commerce… with phone, website, email, employee size, and a named contact ready for outreach.
- **Local market sizing** — see how many businesses of a category exist in a city, how well reviewed they are, and who has a working web presence.
- **CRM & enrichment pipelines** — stream records straight into Sheets, Airtable, HubSpot, Pipedrive, or a custom endpoint via webhooks.
- **Local SEO & citation audits** — check addresses, phone numbers, websites, and categories across a market in one pass.
- **Partner & network research** — find chambers of commerce and business associations by city, with their contacts and employee size.
- **AI agents & MCP workflows** — feed live directory answers into an agent (see below).

### Feature matrix

| Feature | What it does | Default |
|---|---|---|
| 🔎 **Business search by keyword + location** | Multi-task search: pair a keyword ("chamber of commerce", "plumber", "dentist"…) with a US city ("Los Angeles, CA", "Austin, TX"). Each task collects its businesses page by page — every business on every page is a row, with name, category, address, phone, rating, and review count as published. | **On** |
| 📋 **Listing Details** | Turn business page URLs or numeric listing ids into full records: phone, website, email(s), employees, key contact, hours, services, payment methods, coordinates. | Off |
| ⭐ **Reviews** | Public customer reviews for the businesses requested in Listing Details — one row per review, capped per business. | Off |
| 🔗 **Scrape By URL** | Paste a business page URL (one full record), a category page URL (one row per business on that page), or a city page URL (its catalog). | Off |
| 🎯 **Lead details (enrich in place)** | Adds website, email(s), employee size, contact name and title, hours, services, socials, and coordinates to **every** row. Enrich, never filter — businesses with no extra details are still exported. | **On** |
| 📤 **Webhooks** | JSON or Slack. Every record is POSTed to your URL the moment it is collected — CRM, Zapier, Make, n8n. | Off |

Features combine freely: search + lead details is the classic lead-gen run; Listing Details + Reviews is the deep-dive run; Scrape By URL re-collects a page you already know.

### Input reference

#### 🔎 Business search

| Field | Type | Default | Notes |
|---|---|---|---|
| `enableSearch` | boolean | `true` | Master switch for search. |
| `searchTasks` | array | 2 demo rows | One row per search: `keyword`, `location`, optional `maxResults`. |
| `maxResultsPerTask` | integer | `10` | Global default cap per task; override per task with `maxResults`. |
| `maxPagesPerTask` | integer | *empty* | Optional catalog page cap (50 businesses per page). Empty = keep going until this task's businesses are all collected or the category ends. |

Search tasks:

```json
{
  "enableSearch": true,
  "searchTasks": [
    { "keyword": "chamber of commerce", "location": "Los Angeles, CA", "maxResults": 10 },
    { "keyword": "plumber", "location": "Austin, TX", "maxResults": 50 },
    { "keyword": "commercial insurance agency", "location": "Chicago, IL", "maxResults": 100 }
  ],
  "maxResultsPerTask": 10
}
```

Keyword tips: the closer the keyword is to a directory category ("plumber", "dentist", "restaurant", "insurance agency"), the tighter the list. If no category matches the keyword, the run falls back to all businesses in that city and says so in the log.

#### 📋 Listing details

| Field | Type | Default | Notes |
|---|---|---|---|
| `enableListingDetails` | boolean | `false` | Collect full records for specific businesses. |
| `listingUrls` | array | `[]` | Full business page URLs, one per line. |
| `listingIds` | array | `[]` | Numeric listing ids, one per line (the number inside a business URL, e.g. `2002915560`). |
| `enableReviews` | boolean | `false` | One row per public review for the requested businesses. Requires Listing Details. |
| `maxReviewsPerBusiness` | integer | `10` | Reviews collected per business (1–50). |

```json
{
  "enableListingDetails": true,
  "listingUrls": ["https://www.chamberofcommerce.com/business-directory/california/los-angeles/chamber-of-commerce/2002915560-los-angeles-area-chamber-of-commerce"],
  "listingIds": ["2002110354"],
  "enableReviews": true,
  "maxReviewsPerBusiness": 10
}
```

#### 🔗 Scrape By URL

| Field | Type | Default | Notes |
|---|---|---|---|
| `enableScrapeByUrl` | boolean | `false` | Collect directly from URLs instead of searching. |
| `scrapeUrls` | array | `[]` | Business pages, category pages, or city pages. |

```json
{
  "enableScrapeByUrl": true,
  "scrapeUrls": [
    "https://www.chamberofcommerce.com/business-directory/texas/austin/plumber/",
    "https://www.chamberofcommerce.com/business-directory/california/los-angeles/food-dining/restaurant/",
    "https://www.chamberofcommerce.com/business-directory/new-york/new-york/"
  ]
}
```

#### 🎯 Lead details

| Field | Type | Default | Notes |
|---|---|---|---|
| `enableLeadDetails` | boolean | `true` | Adds website, email(s), employee size, contact name/title, hours, services, payment methods, socials, coordinates. Adds a little extra time per record. |

Lead details **never** change how many rows you get — they only fill in more of each row. Emails are looked up on the business's own website when the listing publishes none.

#### ⚙️ Output & limits

| Field | Type | Default | Notes |
|---|---|---|---|
| `maxItems` | integer | `10000` | Global cap on total rows across all features. |
| `concurrency` | integer | `3` | Parallel detail lookups (1–8). |
| `delayBetweenRequestsMs` | integer | `250` | Polite pacing between directory pages (0–5000 ms). |

#### 🌐 Connection

`proxyConfiguration` defaults to Apify residential proxy, country **US**. Change the country or provide your own proxy URLs only if you need to — no other setup is required.

### Output field reference

Every row contains `featureType` (`search`, `listing_details`, `reviews`, `scrape_by_url`) and `scrapedAt`.

| Field | Type | Notes |
|---|---|---|
| `name` | string | Business name. |
| `listingId` | string | null | Directory listing id. |
| `profileUrl` | string | null | Canonical business page. |
| `phone` | string | null | Published phone number. |
| `website` | string | null | Published website. |
| `email` / `emails` | string | null / array | Published mailbox(es) plus ones found on the business's own site. |
| `street`, `city`, `state`, `zipCode`, `location` | string | null | Address parts and the full address. |
| `category`, `categories` | string | null / array | Primary category and the full category path. |
| `rating`, `reviewCount` | number | null | Public rating and review count. |
| `employees`, `employeesMin`, `employeesMax` | string / integer | null | Employee size as published (e.g. `"2-5"`, `"20+"`) with parsed bounds. |
| `contactName`, `contactTitle` | string | null | Key contact and role, when the listing publishes one. |
| `verified` | boolean | Owner-verified badge on the listing. |
| `hours`, `services`, `paymentMethods` | arrays | Public business hours, services, and accepted payment methods. |
| `socials` | object | Social profiles. |
| `geo` | object | null | `{ lat, lng }`. |
| `description`, `imageUrl`, `priceRange` | string | null | About text, image, published price range. |
| `searchKeyword`, `searchLocation`, `searchCategory`, `searchTaskIndex`, `searchTaskLabel`, `position` | — | Search context for each row (null on URL-driven rows). |
| `detailsFetched` | boolean | True when lead details were merged into this row. |
| `sourceUrl`, `pageType` | string | null | For Scrape By URL rows: the page and what kind it was. |
| `requested`, `found` | string / boolean | For Listing Details rows: what you asked for, and whether it exists. |
| `businessName`, `reviewIndex`, `rating`, `date`, `author`, `source`, `text` | — | Review rows. |

Dataset views included: **Overview** (everything) and **Leads** (contact-ready columns).

### Webhook guide

Set `webhookUrl` (and `webhookFormat`) and every record is also pushed to your endpoint the moment it is saved. Webhook delivery failures never stop a run.

**JSON** (`webhookFormat: "json"`) — the full record, POSTed as `application/json`:

```json
{
  "featureType": "search",
  "name": "Los Angeles Area Chamber of Commerce",
  "listingId": "2002915560",
  "phone": "213-580-7500",
  "website": "http://www.lachamber.com/",
  "email": "info@lachamber.com",
  "employees": "20+",
  "contactName": "Maria Gonzalez",
  "contactTitle": "Membership Director",
  "city": "Los Angeles",
  "state": "CA",
  "category": "Chamber Of Commerce",
  "scrapedAt": "2026-09-30T12:00:00.000Z"
}
```

**Slack** (`webhookFormat: "slack"`) — a Slack-ready message payload (`{"text": "..."}`):

```json
{
  "text": ":office: *Los Angeles Area Chamber of Commerce*\n*Category:* Chamber Of Commerce  •  *Employees:* 20+\n*Location:* 350 S Bixel St Los Angeles, CA 90017\n*Phone:* 213-580-7500\n*Website:* http://www.lachamber.com/"
}
```

### Using with AI agents (MCP)

The Actor is an [Apify Actor](https://apify.com/actors), so any MCP-capable agent (Claude, Cursor, custom agents) can run it through the [Apify MCP server](https://docs.apify.com/platform/integrations/mcp) and read the dataset as structured JSON.

Ask your agent things like:

> *"Run the Chamber of Commerce Real-Time Data actor for "roofing contractor" in "Dallas, TX" with lead details on, return 25 rows, and list the companies that have a website but no published email."*

Or chain it: search a market → enrich → push to the CRM of your choice through the webhook.

### FAQ

**Do I need my own proxy?** No. Apify residential proxy (US) is on by default and is the supported route. You can change the country or bring your own proxy URLs in the Connection section if you prefer.

**What does the free plan get me?** Up to 2 rows per run as a trial. Paid plans export everything the run collects.

**How fresh is the data?** Every row is collected during the run and stamped with `scrapedAt`; nothing is cached between runs.

**How many rows can one run produce?** As many as you ask for, up to your run's item cap and your max total charge for that run. Businesses stream to the dataset as they arrive, so long runs stay light.

**Can I get emails?** Yes — when the listing publishes a mailbox it is included, and lead details also look for one on the business's own website. Not every business publishes an email.

**Do filters drop rows?** No. Every discovered business is exported; enrichment only ever adds fields.

**Can I run it on a schedule?** Yes — a scheduled run with the same input gives you a fresh snapshot each time.

### Delivery

- **Streaming:** each record is written to the dataset the moment it is collected. Memory stays flat on large runs.
- **Charge limit aware:** if your max total charge for the run is reached, the run stops cleanly with a summary instead of failing.
- **Free tier:** a free Apify plan exports up to 2 results per run (owner-configurable, `FREE_TIER_MODE=block` blocks free runs entirely).
- **Run summary:** the key-value store entry `OUTPUT` carries totals and the paywall object (`detected`, `isPaying`, `pricingTier`, `limited`, `blocked`, `freeTierMaxItems`).

### Local development

```bash
npm install
cp local.input.example.json local.input.json
cp .env.example .env        # only needed when you want to use your own proxy pool
npm run start:local
npm run typecheck
npm run build
npm run test:smoke          # offline checks of parsing, matching, and gating
```

Local runs write JSONL to `output/local_results.jsonl`. Set `"useProxy": false` in `local.input.json` when your own connection reaches the directory directly; the input's Connection section is ignored locally, and the platform always uses Apify proxy.

# Actor input Schema

## `enableSearch` (type: `boolean`):

Search the Chamber of Commerce directory by what the business does and where it is. Enabled by default.

## `searchTasks` (type: `array`):

Primary input. Add one row per search: pair a keyword with a US city and state.

## `maxResultsPerTask` (type: `integer`):

Default maximum businesses per search task. Override per task in the list above.

## `maxPagesPerTask` (type: `integer`):

Optional safety cap on catalog pages per task (50 businesses per page). Leave empty to keep going until this task's businesses are all collected or the category ends.

## `enableListingDetails` (type: `boolean`):

Collect full records for specific businesses by URL or listing id.

## `listingUrls` (type: `array`):

Full business page URLs (one per line), e.g. https://www.chamberofcommerce.com/business-directory/california/los-angeles/chamber-of-commerce/2002915560-los-angeles-area-chamber-of-commerce

## `listingIds` (type: `array`):

Numeric listing ids (one per line). The id is the number inside any business URL, e.g. 2002915560.

## `enableReviews` (type: `boolean`):

Collect public customer reviews for the businesses requested in Listing Details.

## `maxReviewsPerBusiness` (type: `integer`):

Cap on reviews collected per business (1–50).

## `enableScrapeByUrl` (type: `boolean`):

Collect directly from directory URLs instead of searching.

## `scrapeUrls` (type: `array`):

Directory URLs (one per line) — business pages, category pages, or city pages.

## `enableLeadDetails` (type: `boolean`):

Add website, email(s), employee size, contact name and title, hours, services, and social profiles to every business when publicly available. Adds a little extra time per business.

## `maxItems` (type: `integer`):

Global cap on total dataset rows across all features. Set high for large runs (e.g. 10000+). Note: the run also respects your max total charge for this run.

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

How many detail lookups to run in parallel (1–8). Higher is faster; 3 is a good default.

## `delayBetweenRequestsMs` (type: `integer`):

Polite pacing between directory pages (0–5000 ms). Higher values are slower but gentler.

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

Optional. Every record is always saved to the run's dataset — this webhook is an ADDITIONAL real-time push. When set, each new record is also POSTed to this URL (CRM, Slack incoming webhook, Zapier, Make, Google Sheets).

## `webhookFormat` (type: `string`):

json = full record object; slack = Slack-friendly message payload.

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

Apify residential proxy (US) is enabled by default for reliable collection.

## Actor input object example

```json
{
  "enableSearch": true,
  "searchTasks": [
    {
      "keyword": "chamber of commerce",
      "location": "Los Angeles, CA",
      "maxResults": 10
    },
    {
      "keyword": "plumber",
      "location": "Austin, TX",
      "maxResults": 10
    }
  ],
  "maxResultsPerTask": 10,
  "enableListingDetails": false,
  "listingUrls": [],
  "listingIds": [],
  "enableReviews": false,
  "maxReviewsPerBusiness": 10,
  "enableScrapeByUrl": false,
  "scrapeUrls": [],
  "enableLeadDetails": true,
  "maxItems": 10000,
  "concurrency": 3,
  "delayBetweenRequestsMs": 250,
  "webhookUrl": "",
  "webhookFormat": "json",
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "US"
  }
}
```

# Actor output Schema

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

Complete dataset with every row from all enabled features in this run.

## `leads` (type: `string`):

Businesses with contact details from search and listing details.

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

Per-run metadata: totals, lead-details flag, spending-limit status, and the paywall object (detected, isPaying, pricingTier, limited, blocked, freeTierMaxItems).

# 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 = {
    "enableSearch": true,
    "searchTasks": [
        {
            "keyword": "chamber of commerce",
            "location": "Los Angeles, CA",
            "maxResults": 10
        },
        {
            "keyword": "plumber",
            "location": "Austin, TX",
            "maxResults": 10
        }
    ],
    "maxResultsPerTask": 10,
    "maxReviewsPerBusiness": 10,
    "enableLeadDetails": true,
    "maxItems": 10000,
    "concurrency": 3,
    "delayBetweenRequestsMs": 250,
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ],
        "apifyProxyCountry": "US"
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("b2b_leads/chamberofcommerce-real-time-data-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 = {
    "enableSearch": True,
    "searchTasks": [
        {
            "keyword": "chamber of commerce",
            "location": "Los Angeles, CA",
            "maxResults": 10,
        },
        {
            "keyword": "plumber",
            "location": "Austin, TX",
            "maxResults": 10,
        },
    ],
    "maxResultsPerTask": 10,
    "maxReviewsPerBusiness": 10,
    "enableLeadDetails": True,
    "maxItems": 10000,
    "concurrency": 3,
    "delayBetweenRequestsMs": 250,
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
        "apifyProxyCountry": "US",
    },
}

# Run the Actor and wait for it to finish
run = client.actor("b2b_leads/chamberofcommerce-real-time-data-scraper").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 '{
  "enableSearch": true,
  "searchTasks": [
    {
      "keyword": "chamber of commerce",
      "location": "Los Angeles, CA",
      "maxResults": 10
    },
    {
      "keyword": "plumber",
      "location": "Austin, TX",
      "maxResults": 10
    }
  ],
  "maxResultsPerTask": 10,
  "maxReviewsPerBusiness": 10,
  "enableLeadDetails": true,
  "maxItems": 10000,
  "concurrency": 3,
  "delayBetweenRequestsMs": 250,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "US"
  }
}' |
apify call b2b_leads/chamberofcommerce-real-time-data-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,b2b_leads/chamberofcommerce-real-time-data-scraper"
        }
    }
}
```

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/bbTRmVI1lukncEb6a/builds/UaYvoflF2YU7HKAUF/openapi.json
