# EZlocal Business Data Scraper | Real-Time Leads (`b2b_leads/ezlocal-real-time-data-scraper`) Actor

Collect live EZlocal business data: search US businesses by keyword and market, get full profiles with reviews, and enrich rows with websites, emails, and socials. Results stream to your dataset in real time.

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

## Pricing

from $1.50 / 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

## EZlocal Real-Time Data

Collect US local business data from EZlocal at scale: search businesses by keyword and market, pull full business profiles with ratings and reviews, and enrich every row with the contact details your outreach actually needs — website, emails, and social profiles. Rows stream to your dataset as they are collected, so you can start working with results before the run has finished.

> **Free-trial notice** — on a free Apify plan, a run is capped to a **2-result sample** so you can see the shape of the data. Upgrade to a paid plan for full, unlimited exports.

***

### Who this is for

- **Lead-gen and outreach agencies** building targeted local business lists for cold email, calls, and direct mail.
- **Local SEO and marketing teams** who need to know which businesses in a market exist, what they do, and how to reach them.
- **CRM and data teams** enriching an existing business list with addresses, phone numbers, websites, and social profiles.
- **Sales ops and RevOps** sizing markets by keyword and city before committing to a territory.
- **Market researchers** comparing local business composition across metros.
- **AI-agent builders** who need a clean, structured local-business feed on demand (see [MCP](#mcp--ai-agents)).

***

### What you get

Every run writes one row per business to the default dataset, plus a run summary to the `OUTPUT` key-value entry.

| Feature | Default | What it does |
| --- | --- | --- |
| **Business search** | **On** | Keyword + market search. Returns name, address, phone, rating, service badges, and the business's EZlocal link. |
| **Business URLs** | Off | Paste specific business URLs to get the same output rows for a list you already have. |
| **Full business details** | Off | Fills the same row with the description, the category path, coordinates, ZIP, rating, review count and reviews when published. |
| **Lead details (email discovery)** | Off | Visits the business's own website (when it has one) and adds contact emails and social profiles. **Standalone** — it works on its own, without full business details. |

Enrichment is **add-only**: rows are never filtered, merged away, or dropped because a detail was missing. A business that publishes nothing still gets its search row.

***

### Quick start

1. Set **Business search** on (it already is) and press **Start**. The pre-filled demo searches `plumber` in `New York, NY` for 10 businesses and returns in seconds.
2. Add a row per market you care about — for example `dentist` in `Austin, TX`, `pizza` in `Chicago, IL`.
3. Turn on **Full business details** when you want the deeper profile with reviews, and **Lead details (email discovery)** when you want emails and socials. The two are independent — enable emails alone to keep runs fast.
4. Optionally add a **Webhook URL** to push every row to your CRM or Slack as it is collected.

***

### Input reference

#### Business search

| Field | Type | Default | Notes |
| --- | --- | --- | --- |
| `enableSearch` | boolean | `true` | Primary feature. |
| `searchTasks[].keyword` | string | `plumber` (demo) | What to look for — `plumber`, `dentist`, `roofing`, `pizza`, `auto repair`. |
| `searchTasks[].location` | string | `New York, NY` (demo) | Market in `"City, ST"` format. `"Chicago, Illinois"` also works. |
| `searchTasks[].maxResults` | integer | `10` | Rows this one search may return. Up to **250 per search** is available across pages. |

```json
{
  "enableSearch": true,
  "searchTasks": [
    { "keyword": "dentist", "location": "Austin, TX", "maxResults": 50 },
    { "keyword": "pizza", "location": "Chicago, IL", "maxResults": 25 }
  ]
}
```

> **Depth per search:** a single search returns up to 250 businesses across pages. If you ask for more than that on one row, the run collects 250 and tells you in the log so you can split the search. Different keywords and neighbouring markets reach different slices of the directory.

#### Business URLs

| Field | Type | Default | Notes |
| --- | --- | --- | --- |
| `enableScrapeByUrl` | boolean | `false` | Look up specific business pages. |
| `scrapeUrls` | string\[] | `[]` | `https://ezlocal.com/ny/new-york/...` links. |

#### Details & enrichment

The two enrichments are **independent**. Turn on either one on its own — **Lead details (email discovery)** needs no full business profile and never slows a run down more than the website visits it makes.

| Field | Type | Default | Notes |
| --- | --- | --- | --- |
| `enableListingDetails` | boolean | `false` | Full business profile on the same row, including reviews. |
| `enableLeadDetails` | boolean | `false` | Emails + socials from the business's own website. Works alone. |
| `concurrency` | integer | `2` | Parallel enrichments (1–4). Higher is faster, a little harder on rate limits. |

#### Delivery

| Field | Type | Default | Notes |
| --- | --- | --- | --- |
| `webhookUrl` | string | `""` | Additional real-time POST per row. |
| `webhookFormat` | `json` | `slack` | `json` | `json` = full record; `slack` = Slack message payload. |
| `proxyConfiguration` | object | Apify residential, US | Change only for another country or your own proxy URLs. |

***

### Output fields

| Field | Description |
| --- | --- |
| `type`, `platform`, `featureType` | `business`, `ezlocal`, and `search` or `scrape_by_url`. |
| `name`, `profileUrl`, `profileId` | Business identity and its EZlocal link. |
| `category`, `categories` | Primary category and the full category path (full business details). |
| `description` | About-this-business text. |
| `phone`, `mobile`, `fax` | Contact numbers. |
| `website` | The business's own site, when published. |
| `email`, `emails` | Best contact address plus all addresses found (lead details). |
| `socials`, `instagram`, `facebook`, `linkedin`, `twitter` | Social profiles found on the business's site. |
| `street`, `city`, `state`, `zipCode`, `country`, `location` | Postal address, plus a joined `location`. |
| `lat`, `lng` | Coordinates, for map-ready lists. |
| `rating`, `reviewCount`, `reviews` | Public rating data and reviews, when published. |
| `amenities` | Service badges from the card, e.g. `Open 24 Hours`. |
| `claimed`, `ownerVerified` | Listing signals, when published. |
| `detailsFetched`, `leadDetails` | `true` when each enrichment ran for that row. |
| `searchKeyword`, `searchLocation`, `searchTaskIndex`, `searchTaskLabel` | Which search produced the row — handy for slicing a big export. |
| `scrapedAt` | ISO timestamp. |

Sample row (search only, no enrichment):

```json
{
  "type": "business",
  "platform": "ezlocal",
  "featureType": "search",
  "name": "Nyc Plumber",
  "profileUrl": "https://ezlocal.com/ny/new-york/residential-plumber/092265136",
  "profileId": "092265136",
  "phone": "(347) 532-1861",
  "street": "1391 Avenue Of The Americas",
  "city": "New York",
  "state": "NY",
  "zipCode": "10019",
  "country": "US",
  "location": "1391 Avenue Of The Americas, New York, NY, 10019",
  "searchKeyword": "plumber",
  "searchLocation": "New York, NY",
  "scrapedAt": "2026-10-14T15:00:00.000Z"
}
```

***

### Webhooks

Every row is always written to the dataset. A webhook is an **additional** real-time push of the same row, fired as soon as it is collected — delivery failures never stop the run.

**JSON** (`webhookFormat: "json"`) — the full record object:

```json
{
  "name": "Nyc Plumber",
  "phone": "(347) 532-1861",
  "city": "New York",
  "state": "NY",
  "email": "hello@example.com",
  "profileUrl": "https://ezlocal.com/ny/new-york/residential-plumber/092265136"
}
```

**Slack** (`webhookFormat: "slack"`) — a message payload you can paste straight into a Slack incoming webhook:

```json
{
  "text": ":office: *Nyc Plumber*\nNew York, NY\n*Phone:* (347) 532-1861\n<https://ezlocal.com/ny/new-york/residential-plumber/092265136|View business>"
}
```

***

### MCP / AI agents

This Actor works with the **Apify MCP server**, so an agent can call it directly and reason over the resulting dataset.

Example question to your agent:

> "Find 25 plumbers in Austin, TX on EZlocal, enrich them with full details and email, and return a table sorted by rating."

The agent can call this Actor, read the dataset, and answer — no glue code.

***

### FAQ

**Do I need a proxy?** No. Apify's residential US proxy is configured by default and is the right setting for this Actor. Change it only if you want another country.

**What does the free plan give me?** A 2-result sample per run so you can inspect the schema. Paid plans export everything the search returns.

**How fresh is the data?** Every run collects live. There is no cache and no stale snapshot — re-run for current results.

**Why did my search return fewer rows than I asked for?** Either the market genuinely has fewer matches, or you asked for more than 250 on a single search row (the per-search ceiling). The log states which happened.

**Why is a business missing an email?** Emails are only discoverable when the business publishes a website and lists an address on it. Rows without a website still export with everything else.

**Do I need full business details to get emails?** No. **Lead details (email discovery)** is standalone — it goes straight to the business's website, so you can collect emails without requesting the full profile.

**Can I get a business's full profile without searching?** Yes — paste its EZlocal URL under **Business URLs**.

***

### Support

Open an issue on the Actor's GitHub repository with the run ID, your input JSON, and what you expected. Never include proxy credentials.

# Actor input Schema

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

Find US local businesses on EZlocal by keyword and market — a ready-to-run demo is pre-filled below. NOTE: free Apify plans are limited to a small sample of results per run — upgrade to a paid plan for full, unlimited data.

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

One row per search. Add a keyword (what to look for) and a market in "City, ST" format. Click "+ Add" for every additional search. "Max results" governs that one row.

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

Collect full details for specific EZlocal business pages instead of (or in addition to) searches.

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

EZlocal business URLs (https://ezlocal.com/{state}/{city}/{category}/{id}).

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

Fill each result with its full business profile: description, category path, coordinates, ZIP, rating and reviews when published. Every result is still exported without it — enrichment adds fields to the same row, it never filters or drops rows.

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

Visit each business's own website and add contact emails plus social profiles — the reliable lead-gen option. Works on its own, without full business details, so you can keep runs fast and still get emails. Businesses with no published website are still exported, just without contact fields.

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

How many detail enrichments run in parallel (1–4). Higher is faster but a bit harder on rate limits.

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

Every record is always saved to the run's dataset — a 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 results.

## Actor input object example

```json
{
  "enableSearch": true,
  "searchTasks": [
    {
      "keyword": "plumber",
      "location": "New York, NY",
      "maxResults": 10
    }
  ],
  "enableScrapeByUrl": false,
  "scrapeUrls": [],
  "enableListingDetails": false,
  "enableLeadDetails": false,
  "concurrency": 2,
  "webhookUrl": "",
  "webhookFormat": "json",
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "US"
  }
}
```

# Actor output Schema

## `allResults` (type: `string`):

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

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

Outreach-ready rows with contact details.

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

Run metadata: totals, duration, errors, spending-limit and free-tier (paywall) status.

# 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": "plumber",
            "location": "New York, NY",
            "maxResults": 10
        }
    ],
    "enableListingDetails": false,
    "enableLeadDetails": false,
    "concurrency": 2,
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ],
        "apifyProxyCountry": "US"
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("b2b_leads/ezlocal-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": "plumber",
            "location": "New York, NY",
            "maxResults": 10,
        }],
    "enableListingDetails": False,
    "enableLeadDetails": False,
    "concurrency": 2,
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
        "apifyProxyCountry": "US",
    },
}

# Run the Actor and wait for it to finish
run = client.actor("b2b_leads/ezlocal-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": "plumber",
      "location": "New York, NY",
      "maxResults": 10
    }
  ],
  "enableListingDetails": false,
  "enableLeadDetails": false,
  "concurrency": 2,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "US"
  }
}' |
apify call b2b_leads/ezlocal-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/ezlocal-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/neLnyTAtPmaR3I0sf/builds/6OO2h82LXeg2x465C/openapi.json
