# Yelp Business Profile, Hours & Menu Extractor (`dataspryva/yelp-business-profile-hours-and-menu-extractor`) Actor

Search Yelp or fetch specific business URLs for profile data, hours, services, photo galleries, owner replies, coordinates, and reviews with reaction counts.

- **URL**: https://apify.com/dataspryva/yelp-business-profile-hours-and-menu-extractor.md
- **Developed by:** [Dataspryva](https://apify.com/dataspryva) (community)
- **Categories:** Lead generation, Automation, Travel
- **Features:** Limited permissions
- **Stats:** 1 total users, 0 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.49 / 1,000 results

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.
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?

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 Business Profile, Hours & Menu Extractor

Yelp business pages are useful for more than star ratings. This actor collects the profile itself — address, phone, website, categories, price level, weekly opening hours — plus the published menu with dish names, descriptions, prices, and sections. Reviews, photo galleries, owner replies, and search metadata are included alongside the profile so one run can feed a directory, a competitive analysis, or a menu-price monitor.

### What it collects

- **Two ways to start.** Search Yelp by keyword and location, or paste business URLs, aliases, or 22-character Yelp business IDs.
- **Full business profile.** Name, stable business ID, alias, categories, business type, cuisine, address, coordinates, price level, phone, website, claim status, and a short description.
- **Weekly hours.** Opening hours for each day of the week, including split shifts such as lunch and dinner service.
- **Menus.** Section names, dish names, descriptions, prices, currencies, and item URLs from the business's public Yelp menu. Menus are optional and appear only when the business has published one.
- **Photos.** Up to 100 gallery photos per business, with a legacy `photoCategories` grouping for older integrations.
- **Reviews.** Rating, text, author, date, language, photos, Elite status, reaction counts, and owner replies. The review feed is paged, so you can collect far more than the handful Yelp shows on the first screen.
- **Search context.** Organic rank, sponsored flag, the query and location that found each business, and the source page.
- **Honest status rows.** Missing pages, blocks, and data-contract changes become explicit records instead of a silently empty dataset.
- **No empty placeholders.** Fields Yelp does not provide are omitted at every depth.

### Data source and access

The actor reads Yelp's public search pages, public menu pages, and the same unauthenticated GraphQL endpoint the website uses. It needs no Yelp account, login, cookie, or API key. When Yelp blocks a request, the actor retries through Apify Proxy automatically; you do not have to configure a proxy.

### Output

Each business is one row with `recordType: "business"`. Reviews and menu items are nested arrays.

#### Business fields

| Field | Description |
|---|---|
| `businessId` / `encId` | Yelp's 22-character business ID |
| `alias` | Yelp URL slug, for example `prince-street-pizza-new-york-2` |
| `name` | Business name |
| `type` | Business type derived from Yelp's root category, e.g. `Restaurant` |
| `categories` | Category titles |
| `categoryAliases` | Yelp category aliases |
| `cuisine` | Cuisine-style categories for restaurants |
| `address` | Nested address object: `streetAddress`, `addressLocality`, `addressRegion`, `postalCode`, `addressCountry`, `formatted` |
| `city`, `state`, `zip`, `country` | Flattened address components |
| `lat`, `lng` | Coordinates |
| `coordinates` | `{ latitude, longitude }` legacy object |
| `rating` | Average star rating |
| `reviewCount` | Review total shown on the profile |
| `reviewsTotal` | Reviews available in the public feed |
| `price` / `priceRange` | Price level such as `$` or `$$` |
| `phone` | Business phone |
| `website` | Business website |
| `hours` | Weekly hours keyed by short day name, each value an array of time ranges |
| `photos` / `images` | Gallery photo URLs |
| `photoCategories` | `{ "all": [...] }` compatibility grouping |
| `menu` | Menu items (see below) |
| `isClosed` | Whether Yelp marks the business as closed |
| `isClaimed` | Whether the profile is claimed |
| `claimStatus` | `claimed` or `unclaimed` |
| `description` | Short Yelp summary |
| `reviews` | Review objects |
| `ownerReplies` | Owner replies flattened from reviews: `reviewId`, `reviewDate`, `reply`, `replyDate`, `author` |
| `url` / `directUrl` | Yelp business page URL |
| `bizId` | Legacy alias of `alias` |
| `aggregatedRating` | Legacy rating as text |
| `searchRank` | Organic position in search mode |
| `sponsored` | Whether the card first appeared as sponsored |
| `searchQuery`, `searchLocation`, `searchCategory` | Search context |
| `sourceUrl` | Page the record came from |
| `scrapedAt` | ISO timestamp |

#### Menu items

Each entry in `menu` contains:

| Field | Description |
|---|---|
| `section` | Menu section, e.g. `Pizza`, `Desserts`, `Drinks` |
| `name` | Dish name |
| `description` | Dish description when published |
| `price` | Display price, e.g. `$38.50` |
| `priceValue` | Numeric price for sorting and comparison |
| `priceCurrency` | ISO currency code, e.g. `USD` |
| `url` | Yelp menu-item URL |

#### Reviews

Review objects keep both the current field names and the older pro-actor aliases:

`id`/`reviewId`, `rating`, `text`, `author`/`reviewerName`, `authorId`, `authorUrl`, `authorLocation`, `authorReviewCount`, `date`, `dateLocal`, `photos`, `ownerReply`, `elite`, `eliteYear`, `language`, `isFirstReview`, `checkInCount`, `reactions`, `isUsefulCount`, `isFunnyCount`, `isCoolCount`.

#### Status rows

A row with `recordType: "status"` explains anything that prevented collection:

- `status`: `not_found`, `blocked`, `api_drift`, `no_results`, or `invalid_input`
- `message`: plain-language explanation
- `sourceUrl`, `businessId`, `operation`, and `documentId` when relevant
- `scrapedAt`

Filter on `recordType` before loading business rows.

### Input

| Field | Type | Default | Description |
|---|---|---|---|
| `mode` | string | `search` | `search` for keyword discovery, `direct` for specific businesses |
| `searchQueries` | array | `[]` | Keywords for `mode=search`; each is combined with each location |
| `locations` | array | `[]` | Cities, neighborhoods, or ZIP codes for `mode=search` |
| `startUrls` | array | `[]` | Yelp search URLs in `search` mode, or business URLs, aliases, and IDs in `direct` mode |
| `categories` | array | `[]` | Yelp category names or aliases |
| `sortBy` | string | `recommended` | `recommended`, `rating`, or `reviewCount` |
| `price` | array | `[]` | `$`, `$$`, `$$$`, `$$$$` |
| `minRating` | number | `0` | Drop businesses below this rating |
| `maxItems` | integer | `50` | Maximum business rows (1–500) |
| `includeReviews` | boolean | `true` | Turn off to skip the review feed |
| `maxReviewsPerBusiness` | integer | `20` | Reviews per business (1–1000) |
| `reviewSort` | string | `DATE_DESC` | Review ordering |
| `reviewRatings` | array | `["5","4","3","2","1"]` | Star ratings to include |
| `reviewLanguage` | string | `en` | Review language, or `all` for every language Yelp reports |
| `includeMenu` | boolean | `true` | Fetch and parse published menus |
| `includeHours` | boolean | `true` | Include weekly hours |
| `maxPhotos` | integer | `100` | Gallery photos per business (0–100) |
| `proxyConfiguration` | object | automatic | Optional route override; no proxy setup is required |

#### Legacy input keys

Saved tasks from the previous version keep working. The actor reads the old keys in code and maps them to the new options; they are not shown in the input form:

| Old key | New option |
|---|---|
| `searchTerms` | `searchQueries` |
| `searchQuery` / `searchSort` | `searchQueries` / `sortBy` |
| `locations` / `location` | `locations` |
| `directUrls` / `searchUrls` | `startUrls` |
| `searchLimit` | `maxItems` |
| `reviewLimit` | `maxReviewsPerBusiness`; `0` turns reviews off |
| `searchPrice` / `searchCategory` | `price` / `categories` |
| `fetchMenu` | `includeMenu` |
| `fetchHours` | `includeHours` |
| `fetchPhotos` / `maxPhotos` | `maxPhotos` |
| `proxy` | `proxyConfiguration` |
| `runTimeoutSecs` | Ignored; row caps bound each run |

#### Example: search with menus and hours

```json
{
  "mode": "search",
  "searchQueries": ["pizza"],
  "locations": ["New York, NY"],
  "minRating": 4,
  "maxItems": 10,
  "includeMenu": true,
  "includeHours": true,
  "maxReviewsPerBusiness": 10
}
```

#### Example: exact businesses with deep reviews

```json
{
  "mode": "direct",
  "startUrls": [
    "https://www.yelp.com/biz/prince-street-pizza-new-york-2",
    "https://www.yelp.com/biz/joes-pizza-new-york-148"
  ],
  "includeMenu": true,
  "maxReviewsPerBusiness": 100,
  "reviewSort": "RATING_DESC",
  "maxPhotos": 100
}
```

#### Example: profiles and menus only

```json
{
  "mode": "search",
  "searchQueries": ["ramen"],
  "locations": ["Seattle, WA"],
  "includeReviews": false,
  "maxItems": 25
}
```

### Use cases

- Build a local-business directory with hours, contact data, and coordinates.
- Monitor menu prices and dish changes across restaurant competitors.
- Compare ratings, review volume, and price levels in a market.
- Export reviews with owner responses for sentiment and service analysis.
- Enrich CRM or mapping data with stable Yelp business IDs.
- Track search ranking and sponsored placement for target keywords.

### FAQ

**Do I need a proxy or a Yelp account?**
No. The actor uses public pages and an unauthenticated endpoint. It routes through Apify Proxy automatically when Yelp blocks a request, and you can leave `proxyConfiguration` empty.

**Why is `menu` missing for some restaurants?**
Yelp only publishes menus for businesses that added one, mostly US restaurants. The actor checks the public menu page and omits `menu` when there is nothing to parse.

**How many search results can I get?**
Yelp caps a single search at 240 results, ten per page. `maxItems` can go up to 500 across multiple queries, locations, or direct URLs.

**How deep can reviews go?**
Up to 1,000 per business. The feed is requested in pages of 49, so deeper runs use more requests.

**Why is `reviewsTotal` lower than `reviewCount`?**
Yelp's public review feed excludes reviews it does not recommend, so the feed total can be slightly below the profile count.

**Are hours and menus live?**
Yes. Every run reads the current public page and menu. Yelp does not expose historical hours or menu versions.

**What happens when Yelp changes its format?**
The actor writes an `api_drift` status row with the operation and source URL instead of returning an empty dataset. The run status message also reports it.

**Can I search outside the US?**
Yes. Yelp search and business pages work across its supported markets. Menu coverage is lower outside the US.

### Limitations

- A single search stops at Yelp's 240-result ceiling. Split by neighborhood or category for more.
- The public review feed leaves out not-recommended reviews.
- Menus are published voluntarily; businesses without a menu simply have no `menu` key.
- Amenity/service lists from the old page layout are not part of Yelp's public GraphQL contract and are not included.

# Actor input Schema

## `mode` (type: `string`):

Search Yelp by keyword and location, or look up specific business URLs, aliases, or 22-character business IDs.

## `searchQueries` (type: `array`):

Keywords to search for in mode=search, for example 'pizza', 'plumber', or 'coffee shop'. Each query is combined with each location.

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

Cities, neighborhoods, or ZIP codes to search in mode=search, for example 'New York, NY' or '10012'.

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

In mode=search, paste Yelp search URLs. In mode=direct, paste business URLs, aliases, or 22-character business IDs.

## `categories` (type: `array`):

Optional Yelp category aliases or names used to narrow search results, for example 'pizza', 'coffee', or 'home_services'.

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

Ordering for mode=search result pages.

## `price` (type: `array`):

Optional price levels for mode=search. Leave empty for every price level.

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

Drop businesses rated below this value. Set to 0 to keep every rating.

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

Hard cap on business records in the dataset. Search is limited to Yelp's 240-result ceiling per query.

## `includeReviews` (type: `boolean`):

Turn off to collect only business profiles, hours, and menus and skip the review feed.

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

Maximum reviews collected for each business. Reviews are paged 49 at a time from Yelp's public review feed.

## `reviewSort` (type: `string`):

Ordering applied to each business's review feed.

## `reviewRatings` (type: `array`):

Only collect reviews with these star ratings. Choose one or more values.

## `reviewLanguage` (type: `string`):

Language for the review feed. Choose 'all' to page through every language Yelp reports for the business.

## `includeMenu` (type: `boolean`):

Fetch the public menu page for restaurants and extract sections, dishes, descriptions, and prices.

## `includeHours` (type: `boolean`):

Include weekly opening hours in each business record.

## `maxPhotos` (type: `integer`):

Maximum gallery photos per business (0-100). Set to 0 to skip photos.

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

Optional. The actor automatically uses Apify Proxy when Yelp blocks a request; add a country or custom proxy here only when you need a specific route.

## Actor input object example

```json
{
  "mode": "direct",
  "searchQueries": [
    "pizza",
    "sushi"
  ],
  "locations": [
    "New York, NY",
    "Los Angeles, CA"
  ],
  "startUrls": [
    "https://www.yelp.com/biz/prince-street-pizza-new-york-2"
  ],
  "categories": [],
  "sortBy": "recommended",
  "price": [],
  "minRating": 0,
  "maxItems": 1,
  "includeReviews": true,
  "maxReviewsPerBusiness": 3,
  "reviewSort": "DATE_DESC",
  "reviewRatings": [
    "5",
    "4",
    "3",
    "2",
    "1"
  ],
  "reviewLanguage": "en",
  "includeMenu": true,
  "includeHours": true,
  "maxPhotos": 20
}
```

# Actor output Schema

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

Dataset containing Yelp business records with profiles, hours, menus, photos, owner replies, and reviews, plus explicit status records for missing or blocked targets.

# 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 = {
    "mode": "direct",
    "searchQueries": [],
    "locations": [],
    "startUrls": [
        "https://www.yelp.com/biz/prince-street-pizza-new-york-2"
    ],
    "categories": [],
    "sortBy": "recommended",
    "price": [],
    "minRating": 0,
    "maxItems": 1,
    "includeReviews": true,
    "maxReviewsPerBusiness": 3,
    "reviewSort": "DATE_DESC",
    "reviewRatings": [
        "5",
        "4",
        "3",
        "2",
        "1"
    ],
    "reviewLanguage": "en",
    "includeMenu": true,
    "includeHours": true,
    "maxPhotos": 20
};

// Run the Actor and wait for it to finish
const run = await client.actor("dataspryva/yelp-business-profile-hours-and-menu-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 = {
    "mode": "direct",
    "searchQueries": [],
    "locations": [],
    "startUrls": ["https://www.yelp.com/biz/prince-street-pizza-new-york-2"],
    "categories": [],
    "sortBy": "recommended",
    "price": [],
    "minRating": 0,
    "maxItems": 1,
    "includeReviews": True,
    "maxReviewsPerBusiness": 3,
    "reviewSort": "DATE_DESC",
    "reviewRatings": [
        "5",
        "4",
        "3",
        "2",
        "1",
    ],
    "reviewLanguage": "en",
    "includeMenu": True,
    "includeHours": True,
    "maxPhotos": 20,
}

# Run the Actor and wait for it to finish
run = client.actor("dataspryva/yelp-business-profile-hours-and-menu-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 '{
  "mode": "direct",
  "searchQueries": [],
  "locations": [],
  "startUrls": [
    "https://www.yelp.com/biz/prince-street-pizza-new-york-2"
  ],
  "categories": [],
  "sortBy": "recommended",
  "price": [],
  "minRating": 0,
  "maxItems": 1,
  "includeReviews": true,
  "maxReviewsPerBusiness": 3,
  "reviewSort": "DATE_DESC",
  "reviewRatings": [
    "5",
    "4",
    "3",
    "2",
    "1"
  ],
  "reviewLanguage": "en",
  "includeMenu": true,
  "includeHours": true,
  "maxPhotos": 20
}' |
apify call dataspryva/yelp-business-profile-hours-and-menu-extractor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,dataspryva/yelp-business-profile-hours-and-menu-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/CWMtH7pdWHSYg09fK/builds/7mYG1lPFI7rSncbGE/openapi.json
