# Yelp Scraper — Businesses, Reviews, Phones & Ratings (`tortuga/yelp-scraper`) Actor

Scrape Yelp search results, business details and reviews: rating, review count, categories, price, address, phone, website, hours, reviewer names and owner replies.

- **URL**: https://apify.com/tortuga/yelp-scraper.md
- **Developed by:** [Trevor Ortega](https://apify.com/tortuga) (community)
- **Categories:** Lead generation
- **Stats:** 1 total users, 0 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $3.00 / 1,000 business scrapeds

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

## Yelp Scraper

Scrape Yelp search results, business pages and reviews into a clean dataset: business name, rating, review count, categories, price level ($-$$$$), street and city, neighborhoods, weekly opening hours, rank in the search and the review snippet. Turn on details for the phone number, website, full address with ZIP code and coordinates, claimed status, amenities and menu link. Add reviews to get every review's rating, text, date, reviewer name and profile link, photos, votes and the owner's reply.

Search by keyword and location ("plumbers" in "Chicago, IL") or paste Yelp URLs. No login and no Yelp API key. Download the results as JSON, CSV or Excel, or pull them through the API into your CRM, Google Sheets or an LLM pipeline. You pay per item you get.

### What data does Yelp Scraper extract?

Every business (the `result` event):

| Field | Description |
|---|---|
| `type` | `"business"` |
| `bizId`, `alias`, `url` | Yelp business id, the slug in `/biz/<alias>`, and the canonical yelp.com URL |
| `name` | Business name |
| `rating`, `reviewCount` | Star rating (1-5, one decimal) and number of reviews |
| `categories` | Yelp categories, e.g. `["Coffee & Tea", "Bakeries"]` |
| `priceRange` | `$` to `$$$$` when Yelp shows one |
| `address`, `city`, `neighborhoods` | Street address (empty for service-area businesses that hide it), city, neighborhoods |
| `hours` | Weekly opening hours: `[{"day": "Mon", "hours": "7:00 AM - 5:00 PM"}, ...]` |
| `photoUrl` | Main photo |
| `snippet`, `highlights` | Review snippet or specialty line shown in the search result; business highlights ("Free consultations") |
| `rank`, `isAd` | Position in the organic results (1 = first); `true` for sponsored results (off by default) |
| `searchTerm`, `searchLocation`, `sourceUrl`, `detailsScraped`, `scrapedAt` | Provenance |

With **Include business details** on, or for business URLs (the extra `detail` event):

| Field | Description |
|---|---|
| `phone` | Phone number as Yelp shows it, e.g. `(212) 254-2246` |
| `website`, `menuUrl` | Business website and menu link |
| `fullAddress`, `region`, `postalCode`, `country` | `205 E Houston St, New York, NY 10002`, state / region code, ZIP, country code |
| `latitude`, `longitude` | Coordinates (mobile page variant; `null` when only the desktop page answered) |
| `isClaimed`, `isClosed` | Claimed by the owner; permanently closed |
| `amenities` | Active amenities and attributes: "Takes reservations", "Wheelchair accessible", "Outdoor seating", ... |
| `summary`, `specialties`, `yearEstablished` | "About the business" texts when the owner filled them in |

With **Max reviews per business** above 0 (the `review` event), one item per review:

| Field | Description |
|---|---|
| `type` | `"review"` |
| `reviewId`, `reviewUrl` | Review id and a link that opens the review on Yelp |
| `rating`, `text`, `language` | Stars (1-5), full review text, language code |
| `date`, `localDate` | When it was posted (UTC and the business's local time) |
| `reviewerName`, `reviewerId`, `reviewerProfileUrl` | Reviewer display name as Yelp shows it ("Marissa B."), user id and profile link |
| `reviewerLocation`, `reviewerReviewCount`, `reviewerFriendCount`, `reviewerPhotoCount`, `reviewerEliteYear` | Public reviewer stats; Elite year when the badge is shown |
| `usefulCount`, `funnyCount`, `coolCount` | Votes |
| `photos` | Photos attached to the review (full size) |
| `ownerReply` | `{text, date, ownerName, ownerRole}` when the business replied publicly, else `null` |
| `businessId`, `businessAlias`, `businessName`, `businessUrl`, `scrapedAt` | The business the review belongs to |

### How to scrape Yelp businesses

1. Enter one or more **Search terms** ("pizza", "dentist", "auto repair") and a **Location** ("San Francisco, CA", a ZIP code or a neighborhood), and/or paste **Start URLs**: Yelp search pages (filters you set on yelp.com stay in the URL) or business pages (`yelp.com/biz/...`).
2. Set **Max items** to cap the run and the cost. Businesses and reviews both count.
3. Turn on **Include business details** if you need phone numbers and websites (one more request per business).
4. Click **Start**. Results appear in the **Dataset** tab, with a Businesses view and a Reviews view.

Yelp lists 10 businesses per search page and at most 240 per search. To collect more, split the area: search several neighborhoods or ZIP codes.

### How to scrape Yelp reviews

Paste business URLs into **Start URLs**, set **Max reviews per business** (for example 100), choose the **Review order** (Yelp's relevance order, newest, oldest, highest or lowest rated, Elite reviewers first) and turn on **Reviews only** if you do not need the business rows. Reviews come 20 per request. Yelp filters reviews by language; set **Review language** (default `en`) to collect reviews written in another language.

You can also scrape reviews of every business in a search: set search terms and a location plus **Max reviews per business**.

### Monitor changes on a schedule

Turn on **Only new or changed items** and save the input as a task with a Schedule (Apify Console: **Saved tasks** -> **Schedule**, for example daily). Each run remembers what earlier runs of the same input returned and outputs only what is new: reviews posted since the last run (read newest first; paging stops as soon as a page reaches reviews from an earlier run, so a daily check of one business usually costs one review request plus its business page) and businesses that newly appear in your search. Without reviews, a business is output again when its rating, price range or closed status changed; unchanged businesses are skipped before their business page is opened, so they cost neither an Unblocker request nor an event. Add **Only reviews newer than** (`7 days`, `24 hours` or a date like `2026-10-01`) to bound how far back the first run reads. Monitored items carry `changeType` (`new` or `changed`) and, for changed businesses, `previousValues` with the old values (e.g. `{"rating": 4.5}`); use **Monitoring key** to keep several schedules on the same input apart.

### Input example

```json
{
  "searchTerms": ["coffee"],
  "location": "Seattle, WA",
  "startUrls": [{ "url": "https://www.yelp.com/biz/katzs-delicatessen-new-york" }],
  "maxItems": 200,
  "includeDetails": true,
  "maxReviewsPerBusiness": 20,
  "reviewsSort": "newest",
  "proxyConfiguration": { "useApifyProxy": true, "apifyProxyGroups": ["UNBLOCKER"] }
}
```

### Output example

A business from a search page:

```json
{
  "type": "business",
  "bizId": "FVzl8rDPiTWEtrNEuCu-Xg",
  "alias": "storyville-coffee-company-seattle-9",
  "name": "Storyville Coffee Company",
  "url": "https://www.yelp.com/biz/storyville-coffee-company-seattle-9",
  "rating": 4.5,
  "reviewCount": 2623,
  "categories": ["Coffee & Tea", "Bakeries"],
  "priceRange": "$$$",
  "address": "94 Pike St",
  "city": "Seattle",
  "neighborhoods": ["Downtown"],
  "hours": [{ "day": "Mon", "hours": "7:00 AM - 5:00 PM" }, { "day": "Tue", "hours": "7:00 AM - 5:00 PM" }],
  "photoUrl": "https://s3-media0.fl.yelpcdn.com/bphoto/TecGMknBtZNXGSey2DnFyQ/ls.jpg",
  "snippet": "I love the vibe inside the coffee shop. It's moody and cozy and has a lovely view from their...",
  "highlights": [],
  "isAd": false,
  "rank": 1,
  "searchTerm": "coffee",
  "searchLocation": "Seattle, WA",
  "detailsScraped": false,
  "scrapedAt": "2026-10-05T20:12:00+00:00"
}
```

Detail fields of a business page:

```json
{
  "name": "Katz's Delicatessen",
  "phone": "(212) 254-2246",
  "website": "https://www.katzsdelicatessen.com",
  "menuUrl": "https://localmenu.katzsdelicatessen.com/",
  "fullAddress": "205 E Houston St, New York, NY 10002",
  "postalCode": "10002",
  "latitude": 40.72228023,
  "longitude": -73.98738542,
  "isClaimed": true,
  "isClosed": false,
  "amenities": ["Offers delivery", "Offers take-out", "Wheelchair accessible", "ADA-compliant main entrance"]
}
```

A review:

```json
{
  "type": "review",
  "reviewId": "YKECrz89NBigWkOexmA7-w",
  "reviewUrl": "https://www.yelp.com/biz/katzs-delicatessen-new-york?hrid=YKECrz89NBigWkOexmA7-w",
  "rating": 3,
  "text": "Very busy upon arrival. I was moved to a line but the line was not the correct line for what I wanted to order...",
  "language": "en",
  "date": "2026-08-26T17:53:15Z",
  "reviewerName": "Marissa B.",
  "reviewerProfileUrl": "https://www.yelp.com/user_details?userid=3h_y01VB9oZR72W7e1DMMA",
  "reviewerLocation": "Farmington, NY",
  "reviewerReviewCount": 366,
  "reviewerEliteYear": "2026",
  "usefulCount": 0,
  "photos": ["https://s3-media0.fl.yelpcdn.com/bphoto/WwQSxmpLKGGwnXQfsxplvg/o.jpg"],
  "ownerReply": {
    "text": "We're glad to hear that you enjoyed your visit, but so sorry that you felt confused by the lines! ...",
    "date": "2026-09-11T23:52:06Z",
    "ownerName": "Jake D.",
    "ownerRole": "OWNER"
  },
  "businessName": "Katz's Delicatessen",
  "businessUrl": "https://www.yelp.com/biz/katzs-delicatessen-new-york"
}
```

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

Pay per event, plus the Apify usage of the run:

| Event | Price | When |
|---|---|---|
| `result` | $0.003 | Each business saved |
| `detail` | $0.001 | Extra per business whose page was opened (details on, or business URLs) |
| `review` | $0.00024 | Each review saved |

So 1,000 businesses from search cost $3, with phones and websites $4, and 1,000 reviews cost $0.24, before proxy usage.

**Proxy note.** Yelp answers datacenter and residential IPs with a DataDome captcha, so the default proxy is Apify's **Unblocker**. It is billed to your Apify account per request (10 Unblocker units, about $0.0015 on the free plan, less on paid plans), and on Yelp that includes the requests Yelp refuses, which are retried automatically: in our tests about half of the attempts got through, so plan on about $0.003 per page delivered. One page is a search page with 10 businesses, one business page, or 20 reviews. That adds roughly $0.30 per 1,000 businesses from search, $3 per 1,000 business pages (details), and $0.15 per 1,000 reviews, plus a little compute. The run's SUMMARY record (key-value store) reports `unblockerRequests` (every attempt, the billed number) and `successfulRequests` so you can check the proxy cost of each run.

### Does it work without a Yelp account or API key?

Yes. It reads the public pages and the public review feed that yelp.com uses itself. It does not log in and does not need the Yelp Fusion API (which returns at most 3 review excerpts per business).

### Why do some requests fail and get retried?

Yelp's bot protection refuses part of the requests even through the Unblocker. The scraper retries each page on a rotation of Yelp's equivalent hosts (www.yelp.com, m.yelp.com, yelp.ca, yelp.co.uk; the data is identical and the output always links to yelp.com) with up to six attempts. Pages take 5-60 seconds each, so a run of 100 businesses usually finishes in a few minutes. Every attempt counts as an Unblocker request (see the proxy note above).

### Is it legal to scrape Yelp?

The Actor collects only what Yelp shows publicly to every visitor without logging in: business listings, public reviews with the reviewer's display name (first name and last initial) and public profile link, and public owner replies. Nothing behind a login, no private messages, no masked data. Reviews and reviewer names are personal data in some jurisdictions (GDPR, CCPA): have a legitimate purpose, keep only what you need, and check Yelp's terms and your local law before reusing the data. If you are unsure, consult a lawyer.

# Actor input Schema

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

What to search for on Yelp, e.g. "plumbers", "pizza", "dentist". Each term is searched in the location below. Leave empty when you only use Start URLs.

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

Where to search: city and state ("San Francisco, CA"), a neighborhood, a ZIP code or a full address. Required with search terms. Works for every country Yelp covers.

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

Yelp search URLs (yelp.com/search?find_desc=...\&find_loc=..., filters in the URL are kept) and business URLs (yelp.com/biz/...). Business URLs always return full details; add reviews with 'Max reviews per business'.

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

Stop after this many dataset items in total (businesses plus reviews). You pay per item, so this caps the cost.

## `maxPagesPerSearch` (type: `integer`):

Yelp lists 10 businesses per page and at most 24 pages (240 results) per search.

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

Open each business page found in search for phone, website, full address with ZIP and coordinates, weekly hours, claimed status, amenities and menu link. One extra request and the 'detail' event per business.

## `includeAds` (type: `boolean`):

Also save the sponsored (ad) businesses Yelp mixes into search results. They have no rank and are marked isAd: true.

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

Scrape up to this many reviews for every business (0 = no reviews). Each review is one dataset item with rating, text, date, reviewer name and profile link, photos and the owner's reply. 20 reviews per request.

## `reviewsSort` (type: `string`):

Order in which reviews are collected.

## `reviewsLanguage` (type: `string`):

Two-letter language code of the reviews to collect (Yelp filters reviews by language), e.g. en, es, fr, de.

## `reviewsOnly` (type: `boolean`):

Save only review items, not the business items (use with business URLs to scrape reviews). Defaults to 20 reviews per business when 'Max reviews per business' is 0.

## `reviewsNewerThan` (type: `string`):

Only reviews newer than: a date (YYYY-MM-DD) or a period such as '7 days' or '24 hours'. Reviews are then read newest first, one page at a time, and paging stops at the first page that reaches older reviews, so you do not pay Unblocker requests for old reviews. Leave empty for no date limit.

## `onlyNewOrChanged` (type: `boolean`):

Remember what earlier runs of this input returned and output only what is new or changed: new reviews (read newest first; paging stops where the previous run's reviews begin) and new businesses. Without reviews, a business is also output again when its rating, price range or closed status changed (review counts are not watched); with reviews it is output on the first run only. Unchanged businesses are skipped before their business page is opened. Use with a Schedule; unchanged items are not charged.

## `monitoringKey` (type: `string`):

Name for this watch, so several schedules on the same search stay separate; defaults to a key derived from the search input. Only used with 'Only new or changed items'.

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

Yelp shows a DataDome captcha to datacenter and residential proxies, so the default is Apify's Unblocker proxy (group UNBLOCKER). It is billed to your Apify account per request: 10 Unblocker units, about $0.0015 on the free plan and less on paid plans. Yelp refuses about half of the attempts, which are retried and also billed, so expect about $0.003 per page delivered. One page is a search page with 10 businesses, one business page, or 20 reviews.

## Actor input object example

```json
{
  "searchTerms": [
    "pizza"
  ],
  "location": "San Francisco, CA",
  "maxItems": 10,
  "maxPagesPerSearch": 1,
  "includeDetails": false,
  "includeAds": false,
  "maxReviewsPerBusiness": 0,
  "reviewsSort": "relevance",
  "reviewsLanguage": "en",
  "reviewsOnly": false,
  "onlyNewOrChanged": false,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "UNBLOCKER"
    ]
  }
}
```

# Actor output Schema

## `results` (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 = {
    "searchTerms": [
        "pizza"
    ],
    "location": "San Francisco, CA",
    "maxItems": 10,
    "maxPagesPerSearch": 1,
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "UNBLOCKER"
        ]
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("tortuga/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"],
    "location": "San Francisco, CA",
    "maxItems": 10,
    "maxPagesPerSearch": 1,
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["UNBLOCKER"],
    },
}

# Run the Actor and wait for it to finish
run = client.actor("tortuga/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"
  ],
  "location": "San Francisco, CA",
  "maxItems": 10,
  "maxPagesPerSearch": 1,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "UNBLOCKER"
    ]
  }
}' |
apify call tortuga/yelp-scraper --silent --output-dataset

```

## MCP server setup

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