# Yelp B2b Lead Extractor (`w3crawler/yelp-b2b-lead-extractor`) Actor

Extract publicly visible Yelp business fields without bypassing access controls.

- **URL**: https://apify.com/w3crawler/yelp-b2b-lead-extractor.md
- **Developed by:** [w3crawler](https://apify.com/w3crawler) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.99 / 1,000 business leads

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/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

### What does Yelp B2B Lead Extractor do?

Yelp B2B Lead Extractor collects business information that a business has chosen to publish publicly on [Yelp](https://www.yelp.com/). It accepts Yelp search/listing pages and business-profile pages, reads public structured data and stable page links, and can make a bounded second request for selected public profiles. It also supports an explicit `yelpFusionApi` mode with a user-supplied secret Yelp Places/Fusion API key. It does not log in, solve CAPTCHA or DataDome challenges, infer email addresses, bypass throttling, or access private content.

The Actor is a public-page extractor, not an access-control workaround. If Yelp returns a challenge, throttle, redirect outside Yelp, or unusable page, the run fails closed with an optional diagnostic row and an `OUTPUT_SUMMARY` explanation. The Apify API, schedules, monitoring, and integrations can be used around the Actor; they do not change the source's access rules.

### Why use Yelp B2B Lead Extractor?

Use it for small, auditable prospect lists, local-market research, public category monitoring, or downstream enrichment where the business has published the relevant field. Normal rows preserve Yelp identity and source provenance, and missing values are omitted rather than guessed. Businesses can be deduplicated across source pages and start URLs by public Yelp identity.

#### Tested capabilities

- Public JSON-LD, embedded JSON, and explicit `/biz/` link extraction, plus official Yelp Fusion business search/detail requests when `sourceMode` is `yelpFusionApi`.
- Search and business-profile URLs, bounded pagination, global `maxItems`, and per-source `maxPages`.
- Case-insensitive keyword, minimum rating, minimum review-count, public website, public phone, and public email filters.
- Stable source-order output or global name, rating, and review-count sorting.
- Optional bounded profile enrichment, minimal diagnostics, summary counters, and direct or configured Apify Proxy transport.
- Deterministic repository fixtures for QA. Fixture mode never silently replaces a failed live request.

In `yelpFusionApi` mode, the key is used only in an `Authorization: Bearer ...` request header and is never logged or stored. Fusion business responses can provide business identity, phone, location, categories, ratings, review counts, prices, hours, and media when authorized; ordinary Fusion does not provide email addresses. Setting `hasEmail: true` therefore returns `YELP_FUSION_EMAIL_UNSUPPORTED` and requires a separately authorized email-bearing source. API mode never falls back to a challenged public page.

### How to scrape Yelp business data

1. Open the Actor's Input tab in Apify Console.
2. Provide `query` and `location`, or enter one or more Yelp `startUrls` objects.
3. Start with `maxPages: 1`, a small `maxItems`, `includeDetails: false`, and a respectful delay.
4. Start the run and inspect the dataset, the `OUTPUT_SUMMARY` key-value item, and any diagnostic rows.
5. For automation, use the run/API links in Console, schedule the Actor, or connect its dataset to an approved downstream integration.

`startUrls` takes precedence over the generated `query` + `location` search URL. Filters, sorting, limits, detail settings, and developer settings still apply to custom URLs. The runtime also accepts legacy string entries for backward compatibility, although the Console schema uses objects with a required `url` property.

### What data can it extract?

| Group | Fields |
| --- | --- |
| Identity | `recordId`, `businessId`, `name`, `businessUrl`, `canonicalUrl` |
| Business | `listingStatus`, `businessType`, `categories`, `description`, `priceRange` |
| Public signals | `rating`, `reviewCount`, explicitly published `phone`, explicitly published `email`, `websiteUrls` |
| Location | `address`, `city`, `state`, `postalCode`, `country`, `latitude`, `longitude`, `hours` |
| Media | `imageUrls`, `logoUrl` when associated with the business by the public page |
| Provenance | `source`, `sourceUrl`, `pageUrl`, `searchUrl`, `query`, `location`, `pageNumber`, `itemPosition`, `scrapedAt` |
| Diagnostics | `url`, `error`, `errorCode`, `scrapedAt` on minimal diagnostic rows only |

The Actor does not turn unknown ratings, review counts, addresses, contacts, or websites into fabricated values. Arrays are bounded by the runtime. A normal record is retained when optional detail enrichment fails; there is no invented fallback value and no separate detail-error field in the dataset contract. Operational detail failures remain in `OUTPUT_SUMMARY`.

### Input

All limits below are enforced by runtime validation as well as represented in the input schema. `maxItems` is global across all start URLs and pages. `maxPages` is per start URL. Deduplication is global across sources when enabled. When a non-`relevance` sort is selected, the Actor collects the bounded candidate set, filters it, sorts it globally, and then applies `maxItems`.

| Field | Type and default | Range or values | Behavior |
| --- | --- | --- | --- |
| `query` | string, `restaurants` | max 300 characters | Used with `location` only when `startUrls` is empty. |
| `location` | string, `New York, NY` | max 300 characters | Used with `query` only when `startUrls` is empty. |
| `sourceMode` | string, `public` | `public`, `yelpFusionApi` | Selects public-page extraction or the official Yelp Fusion API. API mode never falls back to public pages. |
| `yelpApiKey` | secret string, omitted | max 512 characters | Required for `yelpFusionApi`; sent only as a Bearer header and never written to output or logs. |
| `startUrls` | array, omitted | 1–20 objects with required `url` | HTTPS `yelp.com` search, city/location, or business URLs; takes precedence over generated search. Legacy strings are accepted by runtime compatibility. |
| `keyword` | string, `""` | max 300 characters | Case-insensitive filter over name, description, categories, and address. |
| `minRating` | number, `0` | 0–5 | Missing ratings do not pass a positive minimum. |
| `minReviewCount` | integer, `0` | 0–1,000,000,000 | Keeps records with an observed count at or above the value. |
| `hasWebsite` | boolean, `false` | `true` or `false` | Requires an explicitly published non-Yelp website. |
| `hasPhone` | boolean, `false` | `true` or `false` | Requires an explicitly published phone number. |
| `hasEmail` | boolean, `false` | `true` or `false` | Requires an explicitly published email; the Actor never guesses one. |
| `sortBy` | string, `relevance` | `relevance`, `name_asc`, `rating_desc`, `reviews_desc` | Global bounded sort; ties retain source order. |
| `maxItems` | integer, `20` | 1–100 | Global maximum normal business records. |
| `maxPages` | integer, `1` | 1–20 per source | Uses a public next link when available, otherwise a bounded Yelp search offset. |
| `deduplicate` | boolean, `true` | `true` or `false` | Deduplicates by business ID, alias, canonical business URL, or record ID. |
| `includeDetails` | boolean, `true` | `true` or `false` | Requests selected public business profiles after the source set is collected. |
| `maxDetailItems` | integer, `5` | 0–20 | Maximum detail requests; `0` disables detail requests. It also cannot exceed the normal record count. |
| `includeDiagnostics` | boolean, `true` | `true` or `false` | Emits minimal diagnostic dataset rows for blocks and request failures when true. |
| `requestDelayMs` | integer, `250` | 0–5,000 | Delay before each public request; zero is intended for bounded QA. |
| `maxRequestRetries` | integer, `1` | 0–3 | Retries network failures only; a clear access block is not retried indefinitely. |
| `timeoutMs` | integer, `30,000` | 5,000–120,000 | Per-request timeout in milliseconds. |
| `proxyConfiguration` | object, `{ "useApifyProxy": false }` | Apify Proxy object | When enabled, passed to `got-scraping`; it does not bypass access controls. Direct access is the default. |
| `fixturePath` | string, `""` | max 500 characters, Actor-relative | Reads an explicit local HTML fixture for deterministic QA; it makes no network request and is not a live fallback. |

#### Official Yelp Fusion API mode

Use the API mode only with a Yelp API key authorized for the official business search and detail endpoints. The key is a secret input and is not included in `OUTPUT_SUMMARY`, dataset rows, or logs. Start with a small bounded request:

```json
{
  "sourceMode": "yelpFusionApi",
  "yelpApiKey": "PASTE_IN_THE_SECRET_INPUT_FIELD",
  "query": "coffee",
  "location": "New York, NY",
  "maxItems": 5,
  "maxPages": 1,
  "includeDetails": true,
  "maxDetailItems": 2
}
```

Do not use a Fusion key to claim email coverage: ordinary Fusion does not return business emails. If the key is omitted, the Actor emits `YELP_API_KEY_REQUIRED` without making a public-page request; if Yelp denies the endpoint, inspect the API diagnostic and obtain the required authorization rather than switching to a bypass.

#### Quick smoke test

```json
{
  "query": "coffee",
  "location": "New York, NY",
  "maxItems": 5,
  "maxPages": 1,
  "includeDetails": false,
  "maxDetailItems": 0,
  "requestDelayMs": 250
}
```

#### Filters and global sorting

```json
{
  "startUrls": [
    { "url": "https://www.yelp.com/search?find_desc=coffee&find_loc=New%20York%2C%20NY" },
    { "url": "https://www.yelp.com/search?find_desc=bakery&find_loc=Boston%2C%20MA" }
  ],
  "keyword": "roaster",
  "minRating": 4,
  "minReviewCount": 10,
  "hasWebsite": true,
  "sortBy": "rating_desc",
  "maxItems": 20,
  "maxPages": 2,
  "deduplicate": true,
  "includeDetails": false
}
```

#### Detail opt-out and diagnostics opt-out

```json
{
  "query": "restaurants",
  "location": "Chicago, IL",
  "maxItems": 10,
  "maxPages": 1,
  "includeDetails": false,
  "maxDetailItems": 0,
  "includeDiagnostics": false
}
```

#### Developer and fixture QA

```json
{
  "startUrls": [
    { "url": "https://www.yelp.com/search?find_desc=coffee&find_loc=New%20York%2C%20NY" }
  ],
  "maxItems": 3,
  "maxPages": 1,
  "requestDelayMs": 0,
  "maxRequestRetries": 0,
  "timeoutMs": 30000,
  "proxyConfiguration": { "useApifyProxy": false },
  "fixturePath": "test/fixtures/sample.html"
}
```

### Output

#### Rich normal record

```json
{
  "recordId": "yelp:acme-coffee-roasters",
  "businessId": "acme-coffee-roasters",
  "name": "Acme Coffee Roasters",
  "businessUrl": "https://www.yelp.com/biz/acme-coffee-roasters",
  "canonicalUrl": "https://www.yelp.com/biz/acme-coffee-roasters",
  "categories": ["Coffee", "Bakery"],
  "rating": 4.5,
  "reviewCount": 120,
  "phone": "+1 212 555 0100",
  "websiteUrls": ["https://example.test/"],
  "address": "10 Main Street, New York, NY, 10001",
  "sourceUrl": "https://www.yelp.com/search?find_desc=coffee&find_loc=New%20York",
  "pageUrl": "https://www.yelp.com/biz/acme-coffee-roasters",
  "pageNumber": 1,
  "itemPosition": 1,
  "scrapedAt": "2026-09-08T00:00:00.000Z"
}
```

#### Detail fallback

When a selected profile request is blocked or fails, the previously collected base record is kept. Fields not present on that base record are omitted; no placeholder detail value is fabricated.

```json
{
  "recordId": "yelp:acme-coffee-roasters",
  "businessId": "acme-coffee-roasters",
  "name": "Acme Coffee Roasters",
  "businessUrl": "https://www.yelp.com/biz/acme-coffee-roasters",
  "sourceUrl": "https://www.yelp.com/search?find_desc=coffee&find_loc=New%20York",
  "pageUrl": "https://www.yelp.com/search?find_desc=coffee&find_loc=New%20York",
  "pageNumber": 1,
  "itemPosition": 1,
  "scrapedAt": "2026-09-08T00:00:00.000Z"
}
```

#### Diagnostic record

```json
{
  "url": "https://www.yelp.com/search?find_desc=coffee&find_loc=New%20York",
  "error": "Public Yelp page returned HTTP 403; no access-control bypass was attempted.",
  "errorCode": "BLOCKED_SOURCE_CHALLENGE",
  "scrapedAt": "2026-09-08T00:00:00.000Z"
}
```

#### `OUTPUT_SUMMARY` key-value item

```json
{
  "actor": "b2b_leads/yelp-b2b-lead-extractor",
  "target": "yelp.com",
  "status": "BLOCKED_SOURCE_CHALLENGE",
  "normalRecordCount": 0,
  "diagnosticCount": 1,
  "dataAvailable": false,
  "sourceCount": 1,
  "pagesVisited": 1,
  "filteredOutCount": 0,
  "duplicateCount": 0,
  "detailRequestedCount": 0,
  "detailEnrichedCount": 0,
  "detailFailureCount": 0,
  "failedRequestCount": 0,
  "blockedReasons": ["BLOCKED_SOURCE_CHALLENGE"],
  "fixtureUsed": false,
  "sourceTransport": "got_scraping_direct",
  "sourceMode": "public",
  "apiCredentialConfigured": false,
  "apiRequestCount": 0,
  "unsupportedFields": [],
  "completedAt": "2026-09-08T00:00:00.000Z"
}
```

You can download the dataset in various formats such as JSON, HTML, CSV, or Excel.

### Tips and advanced options

Start with one source URL, one page, a small item limit, no detail enrichment, and the default delay. Increase `maxPages`, `maxItems`, and `maxDetailItems` only as needed; this Actor intentionally has no concurrency input and performs requests serially. A page with zero filter matches does not by itself stop allowed pagination, but the run stops on source exhaustion, repeated pages, the configured page limit, or an access block.

Use `fixturePath` and `test/fixtures/sample.html` for deterministic parser QA. Fixture records are not evidence that the live Yelp source is accessible. For public source requests, ordinary headers, finite retries, bounded timeouts, and delay are used. Proxy configuration is transparent and optional; it is not a promise of rotation success or a way around Yelp controls.

### FAQ, support, and responsible use

#### Why did I receive only a diagnostic row?

Yelp may return HTTP 403/429, a DataDome or CAPTCHA challenge, an incomplete page, a redirect outside `yelp.com`, or a network timeout. The Actor reports that state instead of pretending it was an empty successful search. A fixture pass is separate from a live-source qualification.

#### Where do I report a problem or use the API?

Use the Actor's Issues tab and include the run ID, input shape, `OUTPUT_SUMMARY`, and error code; do not paste tokens, cookies, proxy credentials, or private page content. Use the Console API/API tab to retrieve the dataset and key-value summary, or schedule and monitor runs through Apify.

#### Legal, privacy, and affiliation

Use this Actor only for public pages and in accordance with Yelp's terms, robots guidance, rate limits, privacy obligations, and applicable law. Public pages can contain personal data; process results only for a legitimate, documented purpose and consult qualified counsel when unsure. This Actor is not affiliated with, endorsed by, or sponsored by Yelp. It does not extract private user data or bypass authentication, CAPTCHAs, paywalls, or other access controls.

# Actor input Schema

## `query` (type: `string`):

Keyword used with location when startUrls is omitted.

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

City, region, or locality used with query when startUrls is omitted.

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

Use public for Yelp page extraction, or yelpFusionApi for the official Yelp Places/Fusion API. API mode never falls back to public pages.

## `yelpApiKey` (type: `string`):

Optional secret Yelp Places/Fusion API key. Required when sourceMode is yelpFusionApi; it is sent only as an Authorization header and never written to output or logs. Fusion does not provide email addresses.

## `startUrls` (type: `array`):

Optional public Yelp search, city/location, or business-profile URLs. When supplied, these URLs control the source pages; query and location remain search-context values.

## `keyword` (type: `string`):

Optional case-insensitive filter over business name, description, categories, and address. It does not change the Yelp source query.

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

Keep records with an observed rating at or above this value. Missing ratings do not pass a positive minimum.

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

Keep records with an observed Yelp review count at or above this value.

## `hasWebsite` (type: `boolean`):

Keep only businesses with an explicitly published non-Yelp website URL.

## `hasPhone` (type: `boolean`):

Keep only businesses with an explicitly published phone number.

## `hasEmail` (type: `boolean`):

Keep only businesses with an explicitly published email address. The Actor never infers or guesses email addresses.

## `sortBy` (type: `string`):

Sort the collected, filtered records across the bounded page set before maxItems is applied.

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

Global maximum number of normal business records emitted across all sources and pages.

## `maxPages` (type: `integer`):

Maximum number of source pages requested for each start URL. Yelp next links are preferred; a bounded start-offset fallback is used only for search pages.

## `deduplicate` (type: `boolean`):

Deduplicate across pages and start URLs by Yelp business ID, alias, or canonical business URL.

## `includeDetails` (type: `boolean`):

Fetch bounded public business-profile pages for selected records and merge fields that are explicitly available there.

## `maxDetailItems` (type: `integer`):

Maximum selected business profiles to request for detail enrichment. 0 disables detail requests.

## `includeDiagnostics` (type: `boolean`):

Emit minimal {url,error,errorCode,scrapedAt} rows for blocks, request failures, redirects, or no public data. Operational counts remain in OUTPUT_SUMMARY.

## `requestDelayMs` (type: `integer`):

Respectful delay before each public request. Use 0 only for fixture QA or carefully bounded testing.

## `maxRequestRetries` (type: `integer`):

Bounded retries for network failures only; access blocks are not retried indefinitely.

## `timeoutMs` (type: `integer`):

Maximum time for each public request.

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

Optional Apify Proxy configuration wired to got-scraping. It does not bypass access controls; direct access is used by default.

## `fixturePath` (type: `string`):

Repository-relative HTML fixture for deterministic QA. Fixture mode makes no network request and must never be used as a silent live fallback.

## Actor input object example

```json
{
  "query": "restaurants",
  "location": "New York, NY",
  "sourceMode": "public",
  "keyword": "",
  "minRating": 0,
  "minReviewCount": 0,
  "hasWebsite": false,
  "hasPhone": false,
  "hasEmail": false,
  "sortBy": "relevance",
  "maxItems": 20,
  "maxPages": 1,
  "deduplicate": true,
  "includeDetails": true,
  "maxDetailItems": 5,
  "includeDiagnostics": true,
  "requestDelayMs": 250,
  "maxRequestRetries": 1,
  "timeoutMs": 30000,
  "proxyConfiguration": {
    "useApifyProxy": false
  },
  "fixturePath": ""
}
```

# Actor output Schema

## `dataset` (type: `string`):

Default dataset.

## `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 = {};

// Run the Actor and wait for it to finish
const run = await client.actor("w3crawler/yelp-b2b-lead-extractor").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 = {}

# Run the Actor and wait for it to finish
run = client.actor("w3crawler/yelp-b2b-lead-extractor").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 '{}' |
apify call w3crawler/yelp-b2b-lead-extractor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,w3crawler/yelp-b2b-lead-extractor"
        }
    }
}
```

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/dt4xw14UmMWjyODml/builds/LaKhIFN4KaceRqAhg/openapi.json
