# Yelp Scraper (`mlg14/yelp-scraper`) Actor

Find Yelp businesses by keyword and location or scrape direct URLs. Export contact details, ratings, hours, categories, amenities, photos, and business descriptions.

- **URL**: https://apify.com/mlg14/yelp-scraper.md
- **Developed by:** [MLG Data](https://apify.com/mlg14) (community)
- **Categories:** Travel, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$1.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

## Yelp Scraper

Scrape Yelp business listings by search term and location, or collect public details from business URLs you already have. Export Yelp business data to CSV, JSON, or a spreadsheet, including contact information, ratings, opening hours, categories, amenities, and photos. This is a Yelp API alternative for public business profiles; it does not require a business API key.

Each dataset item represents one business. The scraper combines search discovery with the structured data on each business page. It can search several terms across several locations, process direct business or search URLs in the same run, and stop at a total result limit. Duplicate business URLs found by multiple searches are processed once.

### What data can you extract from Yelp?

| Field | Description | Example |
| --- | --- | --- |
| `name` | Displayed business name | `Homeslice Neal's Yard` |
| `bizId` | Stable Yelp business identifier | `3AqrHgpIbcMXj0q-p_oPzg` |
| `directUrl` | Canonical business page | `https://www.yelp.com/biz/homeslice-neals-yard-london` |
| `alternateNames` | Other published names | `[]` |
| `address` | Street lines, city, region, postal code, country | `{"city":"London","country":"GB"}` |
| `latitude`, `longitude` | Coordinates when present in the page | `51.514438`, `-0.126427` |
| `phone` | Displayed business number | `020 3151 7488` |
| `website` | Business website URL | `http://www.homeslicepizza.co.uk/` |
| `aggregatedRating` | Overall rating | `4.4` |
| `reviewCount` | Published review total | `291` |
| `priceRange` | Displayed price indicator | `££` |
| `type` | Broad category | `Restaurants` |
| `cuisine` | Restaurant category, when applicable | `Pizza` |
| `categories` | All displayed category labels | `["Restaurants","Pizza"]` |
| `operationHours` | Daily regular hours and exceptions | Monday, noon to 11 PM |
| `upcomingSpecialHours` | Published future exceptions | `[]` |
| `amenitiesAndMore` | Active services and amenities | `["Offers take-out","Vegan options"]` |
| `businessHighlights` | Business supplied highlights | `[]` |
| `aboutTheBusiness` | Specialties, history, year established | `{"yearEstablished":"2011"}` |
| `healthScore` | Published inspection score, where offered | `null` |
| `claimed` | Whether the business profile is claimed | `true` |
| `verified` | Whether the owner is verified | `false` |
| `advertiser` | Whether the business advertises on Yelp | `false` |
| `isClosed` | Closed business flag | `false` |
| `primaryPhoto` | Main photo URL | A public image URL |
| `images` | Photo URLs with captions | `[{"link":"…","alt":"Salami, rocket, parmesan"}]` |
| `summary` | Brief public description | `null` |
| `menuUrl` | External menu URL, when supplied | `http://www.homeslicepizza.co.uk/menu/` |
| `scrapeStartedAt`, `scrapeFinishedAt` | UTC processing timestamps | ISO 8601 timestamps |

The `address` object includes `addressLine1`, `addressLine2`, `addressLine3`, `city`, `regionCode`, `postalCode`, and `country`. Every profile uses the same set of top-level keys. Missing optional facts appear as `null` or an empty list, making exports easier to join and compare.

No individual review text is collected. `aggregatedRating` and `reviewCount` describe the business as a whole. The `images` array contains photo links and captions, not downloaded image files. Location coordinates may be absent for profiles that do not expose a public point on the map.

### How to scrape Yelp

1. Enter one or more search terms, such as `Pizza` or `Plumbers`, and one or more locations. Every term is paired with every location. You can also paste direct business or search URLs.
2. Set **Results per search** to the number of businesses you want from each pair. Set **Total businesses** when you need a cap across the whole run.
3. Choose **Photos per business**. The default is 20 links. Set it to zero if photo links are not needed.
4. Run the scraper and open its dataset. Preview the results or download them as CSV or JSON.

For example, two terms and three locations create six separate searches. If a business appears in more than one of those searches, it is output only once. Direct business URLs are processed as well, so you can add a known competitor to a broader search run. A direct search URL must include both `find_desc` and `find_loc` query parameters.

### Input

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `searchTerms` | List of strings | Empty | Business names, products, or categories to search. |
| `locations` | List of strings | Empty | Cities or areas searched for every term. |
| `searchLimit` | Integer | 10 | Maximum profiles processed for each term and location pair, up to 240. |
| `directUrls` | List of strings | Empty | Business or search URLs to process alongside keyword searches. |
| `maxImages` | Integer | 20 | Photo links returned per business. Zero skips photos. |
| `maxItems` | Integer | 0 | Maximum dataset items across the whole run; zero adds no overall cap. |
| `proxyConfiguration` | Object | Enabled | Network routing for public page requests. |

Provide at least one complete term and location pair, one direct URL, or both. Search terms without locations are skipped with a warning. If no valid source remains, the run fails with a clear input error. Direct URLs can point to regional Yelp domains; the scraper resolves the same public business data and outputs a canonical `yelp.com` business URL.

This input collected 35 distinct public profiles in a completed verification run:

```json
{
  "searchTerms": ["Pizza"],
  "locations": ["London"],
  "searchLimit": 40,
  "maxItems": 35,
  "maxImages": 20
}
```

The result count can be lower than the search limit. Search result counts change, duplicates are removed, and a profile can become unavailable between discovery and collection. `maxItems` caps emitted businesses, while `searchLimit` caps the number of candidates to process for each search pair. Set `searchLimit` above your required final count when you expect duplicates or unavailable profiles.

### Output example

The following item is from the completed London pizza run. Opening hours and photos are shortened here; the dataset item contains the full lists:

```json
{
  "name": "Homeslice Neal's Yard",
  "bizId": "3AqrHgpIbcMXj0q-p_oPzg",
  "directUrl": "https://www.yelp.com/biz/homeslice-neals-yard-london",
  "alternateNames": [],
  "address": {
    "addressLine1": "13 Neal's Yard",
    "addressLine2": "Covent Garden",
    "addressLine3": "",
    "city": "London",
    "regionCode": "XGL",
    "postalCode": "WC2H 9DP",
    "country": "GB"
  },
  "latitude": 51.514438,
  "longitude": -0.126427,
  "phone": "020 3151 7488",
  "website": "http://www.homeslicepizza.co.uk/",
  "aggregatedRating": 4.4,
  "reviewCount": 291,
  "priceRange": "££",
  "type": "Restaurants",
  "cuisine": "Pizza",
  "categories": ["Restaurants", "Pizza"],
  "operationHours": [
    {
      "hours": ["12:00 PM - 11:00 PM"],
      "regularHours": ["12:00 PM - 11:00 PM"],
      "regularHoursRaw": [[720, 1380]],
      "hasSpecialHours": false,
      "dayOfWeekShort": "Mon"
    }
  ],
  "upcomingSpecialHours": [],
  "amenitiesAndMore": ["Offers take-out", "Vegan options", "Wheelchair accessible"],
  "businessHighlights": [],
  "aboutTheBusiness": {
    "specialties": null,
    "history": "Homeslice began with a hand-built wood fire oven...",
    "yearEstablished": "2011"
  },
  "healthScore": null,
  "claimed": true,
  "verified": false,
  "advertiser": false,
  "isClosed": false,
  "primaryPhoto": "https://s3-media0.fl.yelpcdn.com/bphoto/G2yVWPacPsc3qkxlDHkaMg/l.jpg",
  "images": [
    {
      "link": "https://s3-media0.fl.yelpcdn.com/bphoto/G2yVWPacPsc3qkxlDHkaMg/o.jpg",
      "alt": "Salami, rocket, parmesan"
    }
  ],
  "summary": null,
  "menuUrl": "http://www.homeslicepizza.co.uk/menu/",
  "scrapeStartedAt": "2026-09-26T11:54:31.804Z",
  "scrapeFinishedAt": "2026-09-26T11:54:33.733Z"
}
```

The timestamps identify when that business was fetched, not when Yelp last edited its listing. Ratings, reviews, prices, hours, and contact details can change later. Use `bizId` or `directUrl` when comparing two runs; business names alone are not unique.

### Use cases

- **Local market research:** Compare the number, locations, categories, prices, and ratings of businesses in a neighborhood. Search the same category across several cities to build a consistent comparison.
- **Sales prospecting:** Export public business websites, phone numbers, and addresses for businesses in an area. Review each record before outreach and follow the rules that apply to your use.
- **Competitive monitoring:** Schedule the same input regularly, then compare `reviewCount`, `aggregatedRating`, hours, closed status, and advertised amenities by `bizId`.
- **Location planning:** Map public latitude and longitude values where present and combine them with category and price data to spot clusters or gaps.
- **Directory enrichment:** Refresh a directory of known businesses with direct URLs, contact links, opening hours, menu URLs, and current categories.
- **Hospitality research:** Compare restaurant cuisine, service options, photo coverage, and customer rating totals across neighborhoods without opening every profile manually.

### How much does it cost to scrape Yelp?

The current result price is **$0.001 per business**, or **$1.00 per 1,000 dataset items**. The event fires when a business is written to the dataset. Search candidates that do not become output items do not create a result event. Platform usage is included in the result price; there is no separate photo event in this actor.

| Example | Calculation | Result charge |
| --- | --- | --- |
| 100 businesses | 100 × $0.001 | $0.10 |
| 1,000 businesses | 1,000 × $0.001 | $1.00 |
| 5,000 businesses | 5,000 × $0.001 | $5.00 |

These examples describe output pricing, not a guarantee that one search can find that many businesses. The completed verification run emitted 35 businesses, corresponding to **$0.035** in result events at this price. A 5,000-result project needs many distinct term and location pairs or direct URLs because Yelp exposes a finite number of results per search.

Raising `maxImages` above the photos carried on the business page adds gallery requests and may make the run take longer. It does not add a separate event charge. When keeping cost and run time predictable matters most, begin with one term, one location, a modest `searchLimit`, and the default photo setting. Check the actual dataset size before scaling up.

### Tips for best results

Use specific terms that match how businesses describe themselves. A broad term such as `Restaurants` can produce a different mix from `Pizza`, even in the same city. Search terms and locations are combined as a Cartesian product, so large lists can create many page requests and overlapping results. Start narrow, inspect the first run, then add terms or nearby locations to cover more ground.

For direct collection, paste the complete URL of each known business. The scraper follows the business alias and uses the canonical page URL in output. You can mix direct URLs with searches to cover important businesses that a given search ranking does not show. Repeated URLs in the same run are deduplicated. When joining runs, deduplicate by `bizId`.

Use `maxItems` when you need a strict overall dataset cap. Use `searchLimit` when you need a per-query cap; it does not force Yelp to return that many listings. Set `maxImages` to zero when you need only business metadata, or keep it at 20 for a representative visual sample. More photos require additional gallery pages and can increase elapsed time for each business.

For periodic monitoring, save your original input and run it on the same schedule each time. Compare results using IDs, and keep the run timestamp alongside each snapshot. A missing business in a later search may have moved in ranking rather than closed; use a direct URL for profiles that must always be checked.

### Limits

Yelp reports up to roughly **240 results for a single search query** in the tested search response. This actor caps pagination at 24 pages of ten results. Query wording and location strongly affect which businesses appear inside that window. For broader coverage, split a large area into smaller locations or use related but distinct search terms. The result limit is a cap on processed candidates, not a promise of that many complete profiles.

Some search responses are incomplete or include sponsored results before ordinary listings. The scraper reads complete business records that are present and skips links that are not direct business pages. If a search page or business page is blocked or unavailable, the run can return fewer items than requested. Access behavior can vary by location and over time.

Optional fields reflect what the business profile publishes. A website, menu, history, inspection score, special hours, or highlights may be absent. Some regions do not display public health inspection data. `claimed` is `null` when the page does not provide a claim status. `verified` describes the displayed owner verification status and is separate from license information. Coordinates are included when present in the page data or map link.

Opening hours and amenities are published business information and may be outdated. A photo caption can be empty, and the number of usable photos can be lower than `maxImages` if the gallery has fewer images or mixes photos with videos. This actor returns business summaries and rating totals, not individual customer reviews or reviewer details.

The completed verification run searched `Pizza` in `London`, requested up to 40 profiles, and returned 35 unique businesses. All 35 had a name, ID, direct URL, address object, rating, review count, categories, and at least one photo link. That observation demonstrates the tested input, not a permanent success rate for every city or query.

### Use in automated workflows

The input and output are structured for repeatable collection. Two example requests for an automated workflow are:

> Find up to 40 pizza businesses in London. Return their names, ratings, review totals, public websites, and direct URLs.

> Recheck these five Yelp business URLs weekly. Flag any change in opening hours, closed status, price indicator, or review count.

The first request maps to `searchTerms`, `locations`, and `searchLimit`; the second maps to `directUrls` and a recurring schedule. Keep business IDs in your downstream table so changed names and URLs do not create duplicate records.

### FAQ

#### Is it legal to scrape Yelp business data?

The actor collects public business profile information. Review Yelp's terms and the rules that apply to your location and intended use. If you combine the data with personal information, assess privacy obligations before collecting or sharing it. This actor does not collect individual review text or reviewer profiles.

#### Do I need to provide proxies?

The default input has proxy routing enabled. You can change `proxyConfiguration` if your run requires different routing. Access restrictions can still affect a particular query or page; a proxy setting cannot guarantee access to every profile.

#### How fast is a run?

Time depends on search page count, the number and size of business profiles, photo limits, and temporary blocks. In the completed London verification, 35 business items were delivered in about two minutes. Treat that as an observed example rather than a fixed speed for other inputs.

#### Can I schedule and monitor repeated runs?

Yes. Save the input, run it on a schedule, and compare dataset items by `bizId`. Keep `scrapeStartedAt` or the run time with each snapshot. For important known businesses, use direct URLs so a shift in search ranking does not hide them.

#### Can I export to a spreadsheet?

Yes. The dataset can be downloaded as CSV or JSON. The `address`, `operationHours`, `aboutTheBusiness`, and `images` fields contain nested data; flatten those objects if your spreadsheet workflow requires one value per column.

#### Why is a field empty?

Yelp may not publish that fact for the business or region. For example, inspection scores are regional, menu links depend on the business, and owner-written history may never have been added. A missing value is preserved as `null` or an empty list; it is not guessed from another source.

#### Why did a search return fewer businesses than `searchLimit`?

The limit is a maximum. Yelp may expose fewer matches, repeat a business in several searches, or refuse access to a page temporarily. Some response pages include only the complete records available before the response ends. Try a more specific location, another relevant term, or direct URLs for profiles you must include.

### Integrations

Use the dataset through the platform API or connect it to webhooks, scheduled jobs, spreadsheets, and workflow automation. The stable `bizId` and `directUrl` fields make it practical to join results with an existing directory. For change tracking, store one row per business and collection date, then compare rating totals, contact details, hours, and closed status over time.

### Support

Open an issue on the Issues tab; we reply within 24h and add fields on request.

# Actor input Schema

## `searchTerms` (type: `array`):

Keywords or business types. Each term is searched in every location.

## `locations` (type: `array`):

Cities or areas for each search term, such as London or New York, NY.

## `searchLimit` (type: `integer`):

Maximum business profiles to process for each term and location pair. Yelp exposes about 240 per search.

## `directUrls` (type: `array`):

Business or search URLs. These can be combined with search terms and locations.

## `maxImages` (type: `integer`):

Maximum photo links per profile. Zero skips photos; higher limits may require extra gallery pages.

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

Hard cap across all searches and direct URLs. Zero means no additional cap.

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

Proxy settings for requests to public pages.

## Actor input object example

```json
{
  "searchTerms": [
    "Pizza"
  ],
  "locations": [
    "London"
  ],
  "searchLimit": 10,
  "directUrls": [],
  "maxImages": 20,
  "maxItems": 0,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

## `businesses` (type: `string`):

Business profiles 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 = {
    "searchTerms": [
        "Pizza"
    ],
    "locations": [
        "London"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("mlg14/yelp-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 = {
    "searchTerms": ["Pizza"],
    "locations": ["London"],
}

# Run the Actor and wait for it to finish
run = client.actor("mlg14/yelp-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 '{
  "searchTerms": [
    "Pizza"
  ],
  "locations": [
    "London"
  ]
}' |
apify call mlg14/yelp-scraper --silent --output-dataset

```

## MCP server setup

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