# Yelp Business Detail Scraper (`creation/yelp-business-detail-scraper`) Actor

Scrape detailed Yelp business data including name, rating, reviews, categories, address, phone, website, hours, photos, amenities, and business status from Yelp business pages.

- **URL**: https://apify.com/creation/yelp-business-detail-scraper.md
- **Developed by:** [Creation](https://apify.com/creation) (community)
- **Categories:** Developer tools, Automation, Lead generation
- **Stats:** 2 total users, 1 monthly users, 50.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.40 / 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.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

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

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

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Yelp Business Detail Scraper

Scrape a single Yelp business page and extract structured business
information such as contact details, ratings, categories, opening hours,
amenities, business description, owner information, services, review
highlights, related searches, and business photos.

> **Disclaimer:** This is an independent, unofficial scraping tool. It
> is not affiliated with, endorsed by, or sponsored by Yelp. Users are
> responsible for complying with Yelp's terms, applicable laws, and any
> website access restrictions.

### Features

- Scrapes a single Yelp business detail page.
- Accepts a full Yelp URL such as
  `https://www.yelp.com/biz/biscuit-bitch-seattle`.
- Also accepts a relative Yelp path such as
  `/biz/biscuit-bitch-seattle`.
- Automatically normalizes relative Yelp URLs to absolute URLs.
- Extracts business name, Yelp URL, claimed status, and business
  status.
- Extracts rating, review count, and price range.
- Extracts business categories.
- Extracts phone number, address, and external website.
- Extracts opening hours by day.
- Extracts detailed amenities with availability status when available.
- Extracts the business description / "About the Business" content.
- Extracts business history and year established when available.
- Extracts business owner information when available.
- Extracts services offered when available.
- Extracts Yelp review highlights.
- Extracts "People also searched for" terms.
- Extracts business photo URLs.
- Uses Yelp's embedded Apollo state when available for richer
  structured data.
- Uses retry logic for transient request failures.
- Detects common block/challenge responses.
- Uses a residential proxy when available through Apify Proxy.
- Maintains a reusable cookie cache between runs.
- Can refresh the cached cookie separately using the
  `refreshCookieOnly` input.
- Stores each successful scrape as one Dataset item.

### Input

The Actor accepts the following input:

***

Field                 Type                   Required Default       Description

***

`url`                 string                    Yes\* ---           Yelp business
URL or relative
`/biz/...`
path.

`refreshCookieOnly`   boolean                      No `false`       Refresh/check
the cached Yelp
cookie without
scraping a
business page.
--------------

\* `url` is required for a normal scrape. It is ignored when
`refreshCookieOnly` is `true`.

#### Example input

```json
{
  "url": "https://www.yelp.com/biz/biscuit-bitch-seattle",
  "refreshCookieOnly": false
}
```

#### Relative URL example

```json
{
  "url": "/biz/biscuit-bitch-seattle?osq=Vegan+Restaurant",
  "refreshCookieOnly": false
}
```

The Actor converts the relative path to:

```text
https://www.yelp.com/biz/biscuit-bitch-seattle?osq=Vegan+Restaurant
```

### Output

The Actor writes one structured object to the Apify Dataset for each
successful scrape.

#### Output structure

```json
{
  "business": {
    "url": "https://www.yelp.com/biz/biscuit-bitch-seattle",
    "name": "Biscuit Bitch",
    "claimed": true,
    "status": null
  },
  "rating": {
    "rating": 4.2,
    "review_count": 3500,
    "price": "$$"
  },
  "category": [
    "Breakfast & Brunch",
    "Southern"
  ],
  "contact": {
    "phone": "+1 206-555-1234",
    "address": "Seattle, WA",
    "website": "https://example.com"
  },
  "hours": {
    "monday": {
      "hours": "8:00 AM-2:00 PM",
      "status": ""
    },
    "tuesday": {
      "hours": "8:00 AM-2:00 PM",
      "status": ""
    }
  },
  "amenities": [
    {
      "name": "Outdoor seating",
      "available": true
    },
    {
      "name": "Wi-Fi",
      "available": true
    }
  ],
  "about": "Business description when available.",
  "history": {
    "year_established": 2010,
    "description": "Business history when available."
  },
  "business_owner": {
    "name": "Owner Name",
    "role": "Owner",
    "bio": "Owner biography when available."
  },
  "services_offered": [
    "Catering"
  ],
  "review_highlights": [
    "Example review highlight"
  ],
  "people_also_searched": [
    "Breakfast",
    "Brunch"
  ],
  "image_details": {
    "images": [
      "https://example.com/photo1.jpg",
      "https://example.com/photo2.jpg"
    ]
  }
}
```

The example values above are illustrative. Actual values depend on the
Yelp page being scraped.

### Extracted fields

#### Business

- `business.url` --- Scraped Yelp business URL.
- `business.name` --- Business name.
- `business.claimed` --- Whether Yelp identifies the business as
  claimed.
- `business.status` --- Business status, such as permanently or
  temporarily closed, when available.

#### Rating

- `rating.rating` --- Yelp star rating.
- `rating.review_count` --- Number of reviews.
- `rating.price` --- Yelp price range.

#### Category

- `category` --- List of Yelp business categories.

#### Contact

- `contact.phone` --- Business phone number.
- `contact.address` --- Business address.
- `contact.website` --- Business's external website, when available.

#### Hours

- `hours` --- Opening hours organized by day.

#### Amenities

- `amenities` --- Available Yelp amenities and their active/available
  status when Apollo data is available.

#### About and business information

- `about` --- Business description.
- `history.year_established` --- Year established when available.
- `history.description` --- Business history description when
  available.
- `business_owner.name` --- Business owner name when available.
- `business_owner.role` --- Owner role when available.
- `business_owner.bio` --- Owner biography when available.
- `services_offered` --- Services listed by Yelp.

#### Reviews and discovery

- `review_highlights` --- Review highlight text extracted from the
  page.
- `people_also_searched` --- Related search terms displayed by Yelp.

#### Images

- `image_details.images` --- Business photo URLs.

### How it works

The Actor uses the following high-level workflow:

```text
Input Yelp URL
      |
      v
Normalize Yelp URL
      |
      v
Open / retrieve cached cookie
      |
      v
Configure Apify residential proxy
      |
      v
Fetch Yelp business page
      |
      +---- Block/challenge detected
      |          |
      |          v
      |     Refresh cookie
      |          |
      |          v
      |     Retry request
      |
      v
Parse HTML
      |
      +---- Extract visible page data
      |
      +---- Extract Apollo business data
      |
      v
Build structured result
      |
      v
Push result to Apify Dataset
```

### Anti-block and reliability handling

Yelp may return challenge or access-denied pages instead of the
requested business page. The Actor includes several mechanisms intended
to improve reliability:

#### Cookie cache

A named Apify Key-Value Store is used for the cached cookie:

```text
yelp-detail-cookie-cache
```

The cookie is reused across runs instead of being regenerated for every
scrape.

#### Automatic cookie refresh

When a hard `403` block is detected, the Actor attempts to refresh the
cookie using an automated Chrome session running through Xvfb.

The cookie refresh implementation is contained in:

```text
src/cookie_refresh.py
```

#### Residential proxy

The Actor attempts to create an Apify residential proxy session:

```python
Actor.create_proxy_configuration(groups=["RESIDENTIAL"])
```

If residential proxy configuration is unavailable, the Actor logs a
warning and continues using the available connection.

#### Retry handling

The page fetcher retries transient failures and block responses before
giving up.

HTTP `404` is treated as a genuine missing business page and is not
repeatedly retried.

### Scheduled cookie refresh

For workloads that run frequently, you can execute the Actor with:

```json
{
  "refreshCookieOnly": true
}
```

This mode:

1. Loads the cached cookie.
2. Probes a Yelp business detail page.
3. Keeps the existing cookie if it still works.
4. Generates a fresh cookie if the cached cookie no longer works.
5. Does not scrape or push a business result.

This can be useful as a scheduled warm-up before regular scraping runs.

### Project structure

```text
yelp-business-detail-scraper/
│
├── .actor/
│   ├── actor.json
│   ├── input_schema.json
│   ├── output_schema.json
│   └── dataset_schema.json
│
├── src/
│   ├── __init__.py
│   ├── config.py
│   ├── cookie_refresh.py
│   ├── main.py
│   └── yelp_core.py
│
├── Dockerfile
├── requirements.txt
└── README.md
```

### Main source files

#### `src/main.py`

Main Apify Actor entry point.

Responsible for:

- Reading Actor input.
- Opening the cookie cache.
- Configuring the residential proxy.
- Loading or refreshing cookies.
- Fetching the Yelp page.
- Calling the extraction logic.
- Pushing the final result to the Dataset.

#### `src/yelp_core.py`

Contains the main Yelp scraping and parsing logic.

Responsible for:

- URL normalization.
- HTTP session creation.
- Request retries.
- Block detection.
- Apollo-state extraction.
- Business data extraction.
- Contact extraction.
- Hours extraction.
- Amenities extraction.
- Photo extraction.
- Review-highlight extraction.
- Related-search extraction.

#### `src/cookie_refresh.py`

Handles automated browser-based cookie refresh using Chrome,
Selenium/undetected-chromedriver, and Xvfb.

#### `src/config.py`

Contains browser profiles and common request headers.

#### `.actor/input_schema.json`

Defines the Actor's user inputs.

#### `.actor/output_schema.json`

Defines the structure of the Actor output.

#### `.actor/dataset_schema.json`

Controls how Dataset records are presented to users in Apify.

### Docker environment

The Actor runs on Python 3.11.

Python 3.11 is intentionally used because the browser/cookie-refresh
dependencies require compatibility with the current environment.

The Docker image installs:

- Google Chrome
- Xvfb
- Required fonts
- Python dependencies

The Actor is started with:

```bash
python -m src.main
```

### Dependencies

Main dependencies include:

- `apify`
- `curl_cffi`
- `beautifulsoup4`
- `lxml`
- `undetected-chromedriver`
- `selenium`
- `pyvirtualdisplay`

See `requirements.txt` for the exact package requirements.

### Running locally

Install the dependencies:

```bash
pip install -r requirements.txt
```

For the complete cookie-refresh workflow, the environment also needs
Google Chrome and Xvfb.

Run the Actor with:

```bash
python -m src.main
```

When running inside Apify, the Actor uses the Apify SDK to obtain input,
proxy configuration, Key-Value Store access, and Dataset access.

### Apify Store usage

Recommended workflow:

1. Enter a Yelp business URL.
2. Start the Actor.
3. The Actor loads a cached cookie or creates one when required.
4. The Yelp business page is fetched.
5. Business information is extracted.
6. The structured result is saved to the Dataset.
7. The Dataset can be exported as JSON, CSV, Excel, or other supported
   formats.

### Example use case

Input:

```text
https://www.yelp.com/biz/biscuit-bitch-seattle?osq=Vegan+Restaurant
```

The Actor extracts available business information from the detail page
and returns it as a structured Dataset record.

### Limitations

- Yelp page structure can change without notice.
- Some fields may be unavailable for particular businesses.
- Yelp may return challenge or access-denied pages.
- Some data is only available when Yelp embeds it in the page or
  Apollo state.
- Business pages with missing or changed HTML structures may return
  `null` or empty arrays for individual fields.
- Photo URLs and other page assets can change over time.
- The Actor does not guarantee that every Yelp field will be available
  on every business page.

### Troubleshooting

#### Actor cannot fetch a business page

Check that:

- The input is a valid Yelp business URL.
- The Actor has sufficient memory and timeout.
- Apify Proxy is available.
- The Yelp page still exists.
- The Actor logs do not show repeated challenge or access-denied
  responses.

#### Cookie refresh fails

Check the Actor logs for Chrome/Xvfb errors.

The cookie refresh requires:

- Google Chrome
- Xvfb
- Selenium/undetected-chromedriver dependencies
- Sufficient Actor memory

#### Some fields are empty

This can happen when:

- Yelp does not expose the information for that business.
- The relevant section is not present.
- Yelp changes its HTML structure.
- Apollo state is unavailable.
- The business page is served differently based on location or
  session.

### Recommended Apify configuration

For normal scraping:

```json
{
  "refreshCookieOnly": false
}
```

For a scheduled cookie warm-up:

```json
{
  "refreshCookieOnly": true
}
```

The Actor is configured with a default run timeout of 900 seconds and
2048 MB memory.

### License

Add the license that applies to your project before publishing if your
project requires one.

### Disclaimer

This project is an independent tool for extracting publicly accessible
information from Yelp business pages. It is not an official Yelp product
and has no affiliation with Yelp.

Users are responsible for ensuring that their use of the Actor complies
with applicable laws, website terms, robots/access restrictions, and any
relevant data-protection requirements.

# Actor input Schema

## `url` (type: `string`):

A public Yelp business page URL. A relative path like "/biz/forest-family-dentistry-austin" (as stored in this project's url\_name CSV columns) also works - it's normalized automatically.

## `refreshCookieOnly` (type: `boolean`):

If true, ignores url and just checks/refreshes the shared cookie cache, then exits. Meant to be triggered on a schedule so real scrape runs almost always find a warm cookie already cached instead of paying the mint cost live.

## Actor input object example

```json
{
  "url": "https://www.yelp.com/biz/forest-family-dentistry-austin",
  "refreshCookieOnly": false
}
```

# Actor output Schema

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

The scraped business record, stored as a single item in the default dataset.

# 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 = {
    "url": "https://www.yelp.com/biz/forest-family-dentistry-austin"
};

// Run the Actor and wait for it to finish
const run = await client.actor("creation/yelp-business-detail-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 = { "url": "https://www.yelp.com/biz/forest-family-dentistry-austin" }

# Run the Actor and wait for it to finish
run = client.actor("creation/yelp-business-detail-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 '{
  "url": "https://www.yelp.com/biz/forest-family-dentistry-austin"
}' |
apify call creation/yelp-business-detail-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,creation/yelp-business-detail-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/bq6VFsChMj57fOsOD/builds/D6r6DfVk9die6xZOo/openapi.json
