# Yelp Scraper — Business + Reviews + Menu + Photos in One Call (`godberry/yelp-scraper`) Actor

Extract a Yelp business profile + all reviews (auto-expanded, ISO dates) + menu + photos + amenities + hours in one call. Worldwide — any Yelp country domain, residential-proxy ready. No Fusion key, no 3-review cap. Pay-per-event: $0.004/business, $0.0008/review.

- **URL**: https://apify.com/godberry/yelp-scraper.md
- **Developed by:** [Tomas Lebedinskas](https://apify.com/godberry) (community)
- **Categories:** Lead generation, Travel
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, NaN bookmarks
- **User rating**: No ratings yet

## Pricing

from $4.00 / 1,000 business profiles

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/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are a software tools running on the Apify platform, for all kinds of web data extraction and automation use cases.
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.

In JavaScript/TypeScript projects, use official [JavaScript/TypeScript client](https://docs.apify.com/api/client/js.md):

```bash
npm install apify-client
```

In Python projects, use official [Python client library](https://docs.apify.com/api/client/python.md):

```bash
pip install apify-client
```

In shell scripts, use [Apify CLI](https://docs.apify.com/cli/docs.md):

````bash
# MacOS / Linux
curl -fsSL https://apify.com/install-cli.sh | bash
# Windows
irm https://apify.com/install-cli.ps1 | iex
```bash

In AI frameworks, you might use the [Apify MCP server](https://docs.apify.com/platform/integrations/mcp.md).

If your project is in a different language, use 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 Scraper — Business + Reviews + Menu + Photos in One Call

Extract Yelp business data at scale — the full business profile, all reviews (auto-expanded, with ISO dates), the menu, and the photo gallery — in a single call. No Yelp Fusion API key. No 3-review cap. Works on **any Yelp country domain**.

Maintained by [Godberry Studios](https://godberrystudios.com). Questions or a regional domain acting up? [hello@godberrystudios.com](mailto:hello@godberrystudios.com).

### How to use (30 seconds)

1. Open any [Yelp](https://www.yelp.com) business in your browser.
2. Copy the URL from the address bar.
3. Paste it into **Yelp URLs** below and click **Start**.

That's it. Add as many URLs as you like — they run in one batch. Free-text queries (`Joe's Pizza Bleecker Street New York`) and bare slugs (`the-original-pancake-house-portland`) also work — the actor resolves them the way a human would on Yelp's search page.

> **Which domains work best.** The regional domains — `yelp.de`, `yelp.co.uk`, `yelp.fr`, `yelp.com.au`, `yelp.it`, `yelp.es`, … — are the reliable ones; set the **Proxy** country to match the domain (e.g. proxy `DE` for `yelp.de`) for clean runs. `yelp.com` and `yelp.ca` are the exception: DataDome defends them very aggressively and frequently 403s requests from cloud platforms *even through a residential proxy* — for those, your best bet is a residential proxy on an IP you control. The actor ships with **Apify Residential proxy** as the default (Apify bills residential data by the gigabyte, separately from this actor's pay-per-event charges); on the regional domains you can switch to the cheaper datacenter proxy in the **Proxy** option.

### What you get

**Per business (~33 fields)** — name, Yelp slug, canonical URL, regional domain, full address (street, city, state/region, postcode, country), latitude/longitude, phone in E.164 *and* local format, website, average rating, total review count, price level, categories, photo-gallery URLs (full resolution), hero image, hours for each weekday, claimed-by-owner flag, amenities, extracted email when present, plus a scrape timestamp.

**Per review (16 fields)** — reviewer name, profile URL, Elite status, lifetime review count, location, star rating, full (auto-expanded) review text, the raw relative date ("3 weeks ago"), an absolute **ISO-8601 date**, helpful/cool/funny counts, review photos, and the owner's reply (text + dates) when one exists.

**Per menu item (5 fields)** — name, price, category, photo, description. Restaurants with a Yelp menu page only.

#### Field coverage — what's reliable vs. occasional

These come back populated on essentially every business: name, address, city/postcode/country, phone, website, rating, review count, price level, categories, hours, amenities, photos, reviews. The **menu** populates for restaurants that have a Yelp menu page. These are in the schema for completeness but Yelp surfaces them inconsistently — or no longer at all — so expect them to be empty more often than not: `latitude`/`longitude`, `socialLinks`, `neighborhoods`, `ownerName`, `healthScore`, `establishedYear`, `extractedEmail`, and `popularDishes`. We'd rather you know that up front than be surprised in the dataset.

### Why this one

- **Bundled output.** Business profile + every review + menu + photos come back in **one run**, in one record — not split across separate actors you'd have to pay for and stitch together yourself.
- **Worldwide.** The regional domain is auto-detected from the URL; the phone is normalized to E.164 with the right country code; review dates are parsed across ~15 languages, both relative and absolute. ~31 country domains are pre-verified (see *Worldwide support* below) and an unrecognized `yelp.<tld>` still works.
- **ISO dates.** Every review carries an absolute ISO-8601 date alongside Yelp's relative string, so trend lines and filters Just Work.
- **No Fusion API limits.** The official Yelp Fusion API caps you at 3 reviews per business and requires a key. This doesn't.
- **GDPR-aware.** An `includePersonalData` toggle that defaults **off** on EU/EEA + UK + Switzerland domains, so you only pull reviewer profile data when you mean to.
- **Pay-per-event pricing.** You pay for what comes back — a few tenths of a cent per business, fractions of a cent per review — fair on small lookups, predictable on large pulls.
- **Resilient.** Real Chrome (not a headless shell), residential proxy by default, and it detects DataDome challenges — both the redirect kind and the embedded-iframe kind served at the original URL — and retries on a fresh session. If a deep review pull gets rate-limited mid-pagination, you get what was collected rather than a failed run; if retries are exhausted you still get the business profile.

### Pricing

Pay-per-event — you pay only for what comes back.

| Event | Price |
| --- | --- |
| Run start | $0.001 |
| Per business returned | $0.004 |
| Per review returned | $0.0008 |
| Per menu item returned | $0.0005 |

**Example.** One restaurant with 50 reviews + 20 menu items = `$0.001 + $0.004 + (50 × $0.0008) + (20 × $0.0005)` = **$0.055** in actor charges.

Two things to keep in mind on total cost: (1) the default **Residential proxy** — needed to get past DataDome — is billed by Apify by the gigabyte, separately from the events above (usually a few cents per business); on most regional domains you can switch to the cheaper datacenter proxy. (2) Apify's free plan includes **$5/month of platform usage** — that covers anywhere from tens to a few hundred small runs depending on your proxy choice. Plenty to decide whether this fits your workflow.

### Input

```json
{
  "urls": [
    "https://www.yelp.co.uk/biz/dishoom-london",
    "https://www.yelp.de/biz/burgermeister-berlin-3",
    "https://www.yelp.com.au/biz/the-grounds-of-alexandria-alexandria",
    "Burgermeister Berlin"
  ],
  "maxReviewsPerBusiness": 100,
  "sortReviews": "newest",
  "onlyWithText": false,
  "includeMenu": true,
  "includePhotos": true,
  "includeOwnerResponses": true,
  "includePersonalData": false,
  "proxyConfiguration": { "useApifyProxy": true, "apifyProxyGroups": ["RESIDENTIAL"] }
}
````

#### Input fields

| Field | Type | Default | Description |
| --- | --- | --- | --- |
| `urls` | string\[] | required | Yelp business URLs, business slugs, or business-name search queries — one per line |
| `maxReviewsPerBusiness` | int | 100 | Cap reviews per business (1–1000; Yelp paginates 10 per page) |
| `sortReviews` | enum | `newest` | `relevant` (Yelp Sort) / `newest` / `oldest` / `highest` / `lowest` — applied via Yelp's review sort |
| `onlyWithText` | bool | false | Skip star-only reviews that have no text |
| `includeMenu` | bool | true | Extract the menu when the business has a Yelp menu page (restaurants only) |
| `includePhotos` | bool | true | Include the full-resolution photo-gallery URLs |
| `includeOwnerResponses` | bool | true | Capture the owner's reply under each review, when present |
| `includePersonalData` | bool | false on EU/EEA/UK/CH domains; true elsewhere | GDPR toggle — reviewer name, profile URL, location, Elite status, lifetime review count |
| `flattenForSpreadsheet` | bool | false | Joins array fields with " | " and pushes reviews/menu as separate rows — clean CSV/Excel export |
| `proxyConfiguration` | object | Apify Residential | Standard Apify proxy input. Residential is the default because DataDome blocks datacenter IPs. **On a regional domain, set the proxy country to match the domain** (e.g. `apifyProxyCountry: "DE"` for `yelp.de`) — that's the reliable setup. `yelp.com`/`yelp.ca` are still often 403'd even through a residential proxy on a cloud platform; a residential proxy on an IP you control via `proxyUrls` is the best option there. |

### Worldwide support

Works on **any Yelp country domain** — the regional domain is auto-detected from the URL, the phone is normalized to E.164 with the right country code, and review dates are parsed across ~15 languages (relative *and* absolute). Pre-verified domains (~31): `yelp.com` `.ca` `.co.uk` `.ie` `.fr` `.de` `.at` `.ch` `.it` `.es` `.pt` `.nl` `.be` `.pl` `.cz` `.dk` `.fi` `.no` `.se` `.com.au` `.co.nz` `.com.sg` `.com.ph` `.com.hk` `.com.tw` `.co.jp` `.com.br` `.com.mx` `.com.ar` `.cl` `.com.tr`. An unrecognized `yelp.<tld>` still works — it just falls back to the page's structured data for the country.

`includePersonalData` defaults to **false** on EU/EEA + UK + Switzerland domains (`.co.uk .ie .fr .de .at .ch .it .es .pt .nl .be .pl .cz .dk .fi .no .se`) for GDPR safety — set it `true` to override.

### Anti-bot posture

Yelp uses DataDome (TLS/JA3 fingerprinting, JS challenges, IP-reputation blocking). This actor:

- routes through **Apify Residential proxy by default** — DataDome blocks datacenter/cloud IPs, so a residential exit is required; set the proxy country to match the regional domain for clean runs. (`yelp.com`/`yelp.ca` are the hard case — even a residential proxy on a cloud platform is frequently blocked; a residential IP you control gives the best odds.) You can bring your own `proxyUrls`, pick a country, or disable the proxy for testing.
- runs the **real Chrome** binary, not Playwright's `chromium_headless_shell` (the latter is fingerprinted by every modern anti-bot)
- hides the `navigator.webdriver` flag and uses a desktop-class viewport and a current Chrome User-Agent
- detects DataDome challenges — both redirect-style and the embedded-iframe kind served at the original URL — and treats them as transient errors, so it retries with a fresh proxy session
- on a deep review pull, if DataDome rate-limits the pagination it returns what it collected rather than failing the whole business; if retries are exhausted it still delivers the business profile

### Use cases

- **Restaurant intelligence** — bundle reviews + menu + photos for chain-vs-chain analysis
- **Lead generation** — claimed-business data with a normalized E.164 phone (and an email where Yelp shows one) for outreach
- **Reputation monitoring** — feed reviews into a sentiment pipeline; ISO dates make trend lines easy
- **Competitive analysis** — pull competitors in an area and sort by rating or review velocity
- **AI / NLP training data** — full review text + ISO dates + reviewer profiles in clean JSON

### Limits worth knowing

- **Search input resolves to the top result.** Pass a Yelp `/search` URL or a free-text query and the actor opens the first business it returns — it does not yet crawl a whole result page. For a multi-business pull, supply the business URLs directly. (Yelp itself caps search results at 240 — narrow by neighborhood or zipcode if you're building such a list.)
- **`yelp.com` / `yelp.ca` are the hard case.** DataDome defends them very aggressively — they're frequently 403'd from cloud platforms *even through a residential proxy*. The regional domains (`.de`, `.co.uk`, `.fr`, `.com.au`, `.it`, …) are the reliable ones; for `yelp.com`/`yelp.ca`, supply a residential proxy on an IP you control via `proxyUrls`.
- **Deep review pulls** can get rate-limited by DataDome after a few hundred reviews even with a residential proxy; past that, the actor returns what it got rather than failing.
- **Free-plan Apify users** are capped at 1 business and 10 reviews per run, with an upgrade message — Apify pay-per-event events don't pay developers when triggered by free-plan accounts. Any paid Apify subscription unlocks the full output.
- **Selectors can shift.** A few of Yelp's `aria-label` strings differ across regional domains; if a particular domain breaks, email us — those live in one `SELECTORS` constant and we patch quickly.

### Reliability

Once this actor has 30 days of production runs, we'll publish its 30-day `SUCCEEDED` rate here and refresh it weekly. Target: ≥99%.

### License

MIT. Author: Godberry Studios — <hello@godberrystudios.com>.

# Actor input Schema

## `urls` (type: `array`):

One per line. Easiest: open a Yelp business in your browser and paste its URL. Also accepts business slugs (the-original-pancake-house-portland), Yelp search URLs (yelp.com/search?find\_desc=...), and free-text queries ("Joe's Pizza Bleecker Street"). Worldwide — works on any Yelp country domain: .com, .ca, .co.uk, .ie, .fr, .de, .at, .ch, .it, .es, .pt, .nl, .be, .pl, .cz, .dk, .fi, .no, .se, .com.au, .co.nz, .com.sg, .com.ph, .com.hk, .com.tw, .co.jp, .com.br, .com.mx, .com.ar, .cl, .com.tr. IMPORTANT: yelp.com and yelp.ca are heavily defended by DataDome and are frequently blocked from cloud platforms even with a residential proxy — the regional domains (.de, .co.uk, .fr, .com.au, .it, .es, …) are the reliable ones today; for yelp.com/.ca, a residential proxy you control gives the best odds. (A search URL or free-text query resolves to the top result only — pass business URLs directly for a multi-business pull. Yelp caps search results at 240.)

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

Maximum reviews to extract per business (Yelp paginates 10 per page). Higher = longer run + higher pay-per-event cost. Note: Yelp's DataDome anti-bot tends to rate-limit deep pagination after a few hundred reviews even with a residential proxy — past that the actor returns what it got rather than failing. 1000 is the hard ceiling.

## `sortReviews` (type: `string`):

How to sort the reviews before extracting.

## `onlyWithText` (type: `boolean`):

Skip star-only reviews that have no text content.

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

Extract the menu (item name, price, category, photo, description) when the business has a Yelp menu page. Restaurants only — other business types return an empty menu.

## `includePhotos` (type: `boolean`):

Include the full photo-gallery URL list (full-resolution) on each business record.

## `includeOwnerResponses` (type: `boolean`):

Capture the business owner's reply under each review, when present (most reviews don't have one).

## `includePersonalData` (type: `boolean`):

Reviewer name, profile URL, location, Elite status, and review-count. Default OFF when scraping an EU/EEA + UK + Switzerland domain (.co.uk, .ie, .fr, .de, .at, .ch, .it, .es, .pt, .nl, .be, .pl, .cz, .dk, .fi, .no, .se) for GDPR safety — set true to override. Default ON elsewhere.

## `flattenForSpreadsheet` (type: `boolean`):

Recommended when exporting to CSV. Array fields (photos, categories, amenities) are joined with ' | '; reviews/menu are pushed as separate row series anchored to businessAlias. Leave off for JSON consumers.

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

Yelp uses DataDome anti-bot, which blocks datacenter IPs — so this defaults to Apify Residential. Best practice: set the proxy country to match the regional domain you're scraping (e.g. proxy DE for yelp.de, GB for yelp.co.uk). yelp.com / yelp.ca are the hard case — they're often blocked even through a residential proxy on a cloud platform; a residential proxy on an IP you control via proxyUrls is the best option there. You can also disable the proxy for local testing.

## Actor input object example

```json
{
  "urls": [
    "https://www.yelp.co.uk/biz/dishoom-london",
    "https://www.yelp.de/biz/burgermeister-berlin-3",
    "https://www.yelp.com.au/biz/the-grounds-of-alexandria-alexandria",
    "Burgermeister Berlin"
  ],
  "maxReviewsPerBusiness": 100,
  "sortReviews": "newest",
  "onlyWithText": false,
  "includeMenu": true,
  "includePhotos": true,
  "includeOwnerResponses": true,
  "includePersonalData": true,
  "flattenForSpreadsheet": false,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "GB"
  }
}
```

# 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 = {
    "urls": [
        "https://www.yelp.co.uk/biz/dishoom-london"
    ],
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ],
        "apifyProxyCountry": "GB"
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("godberry/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 = {
    "urls": ["https://www.yelp.co.uk/biz/dishoom-london"],
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
        "apifyProxyCountry": "GB",
    },
}

# Run the Actor and wait for it to finish
run = client.actor("godberry/yelp-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "urls": [
    "https://www.yelp.co.uk/biz/dishoom-london"
  ],
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "GB"
  }
}' |
apify call godberry/yelp-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=godberry/yelp-scraper",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

```json
{
    "openapi": "3.0.1",
    "info": {
        "title": "Yelp Scraper — Business + Reviews + Menu + Photos in One Call",
        "description": "Extract a Yelp business profile + all reviews (auto-expanded, ISO dates) + menu + photos + amenities + hours in one call. Worldwide — any Yelp country domain, residential-proxy ready. No Fusion key, no 3-review cap. Pay-per-event: $0.004/business, $0.0008/review.",
        "version": "0.4",
        "x-build-id": "rhw82VQYTqpFu7CpO"
    },
    "servers": [
        {
            "url": "https://api.apify.com/v2"
        }
    ],
    "paths": {
        "/acts/godberry~yelp-scraper/run-sync-get-dataset-items": {
            "post": {
                "operationId": "run-sync-get-dataset-items-godberry-yelp-scraper",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for its completion, and returns Actor's dataset items in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        },
        "/acts/godberry~yelp-scraper/runs": {
            "post": {
                "operationId": "runs-sync-godberry-yelp-scraper",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor and returns information about the initiated run in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/runsResponseSchema"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/acts/godberry~yelp-scraper/run-sync": {
            "post": {
                "operationId": "run-sync-godberry-yelp-scraper",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for completion, and returns the OUTPUT from Key-value store in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        }
    },
    "components": {
        "schemas": {
            "inputSchema": {
                "type": "object",
                "required": [
                    "urls"
                ],
                "properties": {
                    "urls": {
                        "title": "Yelp URLs, slugs, or business searches",
                        "type": "array",
                        "description": "One per line. Easiest: open a Yelp business in your browser and paste its URL. Also accepts business slugs (the-original-pancake-house-portland), Yelp search URLs (yelp.com/search?find_desc=...), and free-text queries (\"Joe's Pizza Bleecker Street\"). Worldwide — works on any Yelp country domain: .com, .ca, .co.uk, .ie, .fr, .de, .at, .ch, .it, .es, .pt, .nl, .be, .pl, .cz, .dk, .fi, .no, .se, .com.au, .co.nz, .com.sg, .com.ph, .com.hk, .com.tw, .co.jp, .com.br, .com.mx, .com.ar, .cl, .com.tr. IMPORTANT: yelp.com and yelp.ca are heavily defended by DataDome and are frequently blocked from cloud platforms even with a residential proxy — the regional domains (.de, .co.uk, .fr, .com.au, .it, .es, …) are the reliable ones today; for yelp.com/.ca, a residential proxy you control gives the best odds. (A search URL or free-text query resolves to the top result only — pass business URLs directly for a multi-business pull. Yelp caps search results at 240.)",
                        "items": {
                            "type": "string"
                        }
                    },
                    "maxReviewsPerBusiness": {
                        "title": "Max reviews per business",
                        "minimum": 1,
                        "maximum": 1000,
                        "type": "integer",
                        "description": "Maximum reviews to extract per business (Yelp paginates 10 per page). Higher = longer run + higher pay-per-event cost. Note: Yelp's DataDome anti-bot tends to rate-limit deep pagination after a few hundred reviews even with a residential proxy — past that the actor returns what it got rather than failing. 1000 is the hard ceiling.",
                        "default": 100
                    },
                    "sortReviews": {
                        "title": "Sort reviews by",
                        "enum": [
                            "relevant",
                            "newest",
                            "oldest",
                            "highest",
                            "lowest"
                        ],
                        "type": "string",
                        "description": "How to sort the reviews before extracting.",
                        "default": "newest"
                    },
                    "onlyWithText": {
                        "title": "Only reviews with text",
                        "type": "boolean",
                        "description": "Skip star-only reviews that have no text content.",
                        "default": false
                    },
                    "includeMenu": {
                        "title": "Include menu (restaurants)",
                        "type": "boolean",
                        "description": "Extract the menu (item name, price, category, photo, description) when the business has a Yelp menu page. Restaurants only — other business types return an empty menu.",
                        "default": true
                    },
                    "includePhotos": {
                        "title": "Include photos",
                        "type": "boolean",
                        "description": "Include the full photo-gallery URL list (full-resolution) on each business record.",
                        "default": true
                    },
                    "includeOwnerResponses": {
                        "title": "Include owner responses",
                        "type": "boolean",
                        "description": "Capture the business owner's reply under each review, when present (most reviews don't have one).",
                        "default": true
                    },
                    "includePersonalData": {
                        "title": "Include reviewer personal data (GDPR)",
                        "type": "boolean",
                        "description": "Reviewer name, profile URL, location, Elite status, and review-count. Default OFF when scraping an EU/EEA + UK + Switzerland domain (.co.uk, .ie, .fr, .de, .at, .ch, .it, .es, .pt, .nl, .be, .pl, .cz, .dk, .fi, .no, .se) for GDPR safety — set true to override. Default ON elsewhere.",
                        "default": true
                    },
                    "flattenForSpreadsheet": {
                        "title": "Flatten output for spreadsheet (CSV / Excel)",
                        "type": "boolean",
                        "description": "Recommended when exporting to CSV. Array fields (photos, categories, amenities) are joined with ' | '; reviews/menu are pushed as separate row series anchored to businessAlias. Leave off for JSON consumers.",
                        "default": false
                    },
                    "proxyConfiguration": {
                        "title": "Proxy",
                        "type": "object",
                        "description": "Yelp uses DataDome anti-bot, which blocks datacenter IPs — so this defaults to Apify Residential. Best practice: set the proxy country to match the regional domain you're scraping (e.g. proxy DE for yelp.de, GB for yelp.co.uk). yelp.com / yelp.ca are the hard case — they're often blocked even through a residential proxy on a cloud platform; a residential proxy on an IP you control via proxyUrls is the best option there. You can also disable the proxy for local testing.",
                        "default": {
                            "useApifyProxy": true,
                            "apifyProxyGroups": [
                                "RESIDENTIAL"
                            ]
                        }
                    }
                }
            },
            "runsResponseSchema": {
                "type": "object",
                "properties": {
                    "data": {
                        "type": "object",
                        "properties": {
                            "id": {
                                "type": "string"
                            },
                            "actId": {
                                "type": "string"
                            },
                            "userId": {
                                "type": "string"
                            },
                            "startedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "finishedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "status": {
                                "type": "string",
                                "example": "READY"
                            },
                            "meta": {
                                "type": "object",
                                "properties": {
                                    "origin": {
                                        "type": "string",
                                        "example": "API"
                                    },
                                    "userAgent": {
                                        "type": "string"
                                    }
                                }
                            },
                            "stats": {
                                "type": "object",
                                "properties": {
                                    "inputBodyLen": {
                                        "type": "integer",
                                        "example": 2000
                                    },
                                    "rebootCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "restartCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "resurrectCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "computeUnits": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "options": {
                                "type": "object",
                                "properties": {
                                    "build": {
                                        "type": "string",
                                        "example": "latest"
                                    },
                                    "timeoutSecs": {
                                        "type": "integer",
                                        "example": 300
                                    },
                                    "memoryMbytes": {
                                        "type": "integer",
                                        "example": 1024
                                    },
                                    "diskMbytes": {
                                        "type": "integer",
                                        "example": 2048
                                    }
                                }
                            },
                            "buildId": {
                                "type": "string"
                            },
                            "defaultKeyValueStoreId": {
                                "type": "string"
                            },
                            "defaultDatasetId": {
                                "type": "string"
                            },
                            "defaultRequestQueueId": {
                                "type": "string"
                            },
                            "buildNumber": {
                                "type": "string",
                                "example": "1.0.0"
                            },
                            "containerUrl": {
                                "type": "string"
                            },
                            "usage": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "integer",
                                        "example": 1
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "usageTotalUsd": {
                                "type": "number",
                                "example": 0.00005
                            },
                            "usageUsd": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "number",
                                        "example": 0.00005
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
```
