{
  "openapi": "3.0.1",
  "info": {
    "title": "Yelp Scraper — Business Leads, Unlimited Reviews & Emails",
    "description": "Yelp scraper for business leads: paste any Yelp search or business URL and get name, phone, website, address, hours, rating, categories and price level, plus unlimited reviews past Yelp's 3-review cap. Optional contact emails and AI review analysis. JSON, CSV or Excel. No browser.",
    "version": "0.0",
    "x-build-id": "2odACaHKLXiGAF1J1"
  },
  "servers": [
    {
      "url": "https://api.apify.com/v2"
    }
  ],
  "paths": {
    "/acts/memo23~yelp-scraper/run-sync-get-dataset-items": {
      "post": {
        "operationId": "run-sync-get-dataset-items-memo23-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/memo23~yelp-scraper/runs": {
      "post": {
        "operationId": "runs-sync-memo23-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/memo23~yelp-scraper/run-sync": {
      "post": {
        "operationId": "run-sync-memo23-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",
        "properties": {
          "searchTerms": {
            "title": "🔍 Search terms",
            "type": "array",
            "description": "What to search for on Yelp — e.g. `plumbers`, `coffee shops`, `dentists`. Each term is combined with **Search location** below, so 3 terms x 1 location = 3 searches. No need to build a Yelp URL yourself. Leave empty if you are passing full URLs in **Start URLs**.",
            "items": {
              "type": "string"
            }
          },
          "searchLocation": {
            "title": "📍 Search location",
            "type": "string",
            "description": "City, ZIP or region the search terms apply to — e.g. `Chicago, IL`, `90210`, `London`. Required when **Search terms** is used; ignored otherwise."
          },
          "searchSortBy": {
            "title": "Sort search results by",
            "enum": [
              "",
              "rating",
              "review_count"
            ],
            "type": "string",
            "description": "Result ordering for generated searches. Applies to **Search terms** only.",
            "default": ""
          },
          "startUrls": {
            "title": "Start URLs (optional)",
            "type": "array",
            "description": "Optional — use this instead of, or alongside, the search fields above. Accepts Yelp search URLs like `https://www.yelp.com/search?find_desc=grill&find_loc=Los+Angeles` and individual business URLs like `https://www.yelp.com/biz/fresh-corn-grill-west-hollywood`. Both kinds can be mixed in one run.",
            "items": {
              "type": "string"
            }
          },
          "maxItems": {
            "title": "Maximum number of items OR limit the results per crawl",
            "type": "integer",
            "description": "Maximum number of items that will be scraped. One item = one business row. Default: 1000 — set a lower cap (e.g. 50) for test runs.",
            "default": 1000
          },
          "fetchBusinessDetails": {
            "title": "Fetch full business details",
            "type": "boolean",
            "description": "When <b>enabled</b> (default), the scraper visits each business profile to extract the full record: hours, website, description, amenities/services, full photo gallery, claimed status, and timezone.<br><br>When <b>disabled</b>, only the data already present in the search listing is returned: name, rating, review count, price level, categories, phone, address, and 1 hero photo. This is significantly <b>faster and cheaper</b> — useful when you only need a directory-style overview.<br><br>Note: direct business URLs (yelp.com/biz/...) in <b>Start URLs</b> are always fetched in full — they have no search listing to fall back on. Enabling <b>Scrape Reviews</b> also switches this back on automatically.",
            "default": true
          },
          "scrapeReviews": {
            "title": "Scrape Reviews",
            "type": "boolean",
            "description": "If enabled, scraper will fetch reviews for each business using GraphQL. Requires <b>Fetch full business details</b> — if that toggle is off, it is enabled automatically for the run.",
            "default": false
          },
          "maxReviews": {
            "title": "Max Reviews",
            "type": "integer",
            "description": "Maximum number of reviews to scrape per business (if scraping reviews is enabled).",
            "default": 1000
          },
          "reviewsSort": {
            "title": "Sort reviews by",
            "enum": [
              "DATE_DESC",
              "DATE_ASC",
              "RELEVANCE_DESC"
            ],
            "type": "string",
            "description": "Order in which reviews are collected, which decides the set you get when Max reviews is lower than the business's total. DATE_DESC is newest first (default), DATE_ASC oldest first, RELEVANCE_DESC is Yelp's own \"Yelp Sort\". Needs Scrape reviews.",
            "default": "DATE_DESC"
          },
          "reviewStarRatings": {
            "title": "Only these star ratings",
            "type": "array",
            "description": "Keep only reviews with these star ratings; Yelp filters before anything is fetched, so filtered-out reviews are never billed. Example: [\"1\", \"2\"] for complaints only. Leave empty for all ratings (default). Needs Scrape reviews.",
            "items": {
              "type": "string",
              "enum": [
                "5",
                "4",
                "3",
                "2",
                "1"
              ],
              "enumTitles": [
                "★★★★★ 5 stars",
                "★★★★ 4 stars",
                "★★★ 3 stars",
                "★★ 2 stars",
                "★ 1 star"
              ]
            }
          },
          "reviewsFrom": {
            "title": "Only reviews posted on or after",
            "type": "string",
            "description": "Keep only reviews posted on or after this date (YYYY-MM-DD), e.g. 2026-09-01. With Newest first it stops paging at the first older review, so scheduled \"new reviews since last run\" monitoring stays cheap. Leave empty for no date limit (default). Needs Scrape reviews."
          },
          "reviewsLanguage": {
            "title": "Review language",
            "type": "string",
            "description": "Two-letter code of the review language to fetch, e.g. en, es, fr, de. Yelp returns one language per request, so a business's Spanish reviews need \"es\". Default en.",
            "default": "en"
          },
          "reviewInsights": {
            "title": "📊 Review insights — auto-analyze the reviews (billed per business)",
            "type": "boolean",
            "description": "Requires scrapeReviews. Adds one `review_insights` summary row per business: rating breakdown & sentiment split, the recurring complaint themes (service, wait times, quality, cleanliness, pricing…), top negative-review terms, and request snippets. Instant, no AI/keys required. Billed per business analyzed.",
            "default": false
          },
          "painPointAnalysis": {
            "title": "🤖 AI pain-point analysis — LLM review teardown (paid plans, billed per business)",
            "type": "boolean",
            "description": "Requires scrapeReviews. Runs an AI (Claude) pass over each business's reviews and adds a `painPointAnalysis` row: clustered pain-point themes with severity & recurrence, requested improvements, quick wins, overall sentiment and a short summary — turn a pile of reviews into an operational to-do list. Requires a paid Apify plan (uses AI credits) and is billed per business analyzed. For a free, no-AI version use Review insights above.",
            "default": false
          },
          "useCachedData": {
            "title": "Use cached data (faster, cheaper)",
            "type": "boolean",
            "description": "When <b>enabled</b> (default), business and search pages that were already scraped recently are served from a shared cache instead of being re-fetched — noticeably faster and cheaper. Cached rows are marked with <code>servedFromCache: true</code> and a <code>dataAsOf</code> timestamp so you always know how fresh the data is. Reviews (when enabled) are always fetched fresh. Turn this <b>off</b> to force every page to be scraped live.",
            "default": true
          },
          "maxCacheAgeDays": {
            "title": "Max cache age (days)",
            "minimum": 0,
            "type": "integer",
            "description": "Freshness limit for cached pages. A cached record older than this many days is re-scraped live. Set <b>0</b> to always bypass the cache and fetch everything fresh. Leave empty to use sensible defaults (business details ~21 days, search listings ~14 days). Only applies when <b>Use cached data</b> is enabled."
          },
          "enrichEmails": {
            "title": "Enrich with contact emails (experimental, billed per email)",
            "type": "boolean",
            "description": "If enabled, tries to find a contact email for each business by reading its own website's contact/about pages (e.g. info@, contact@). Adds contactEmail and contactWebsite columns plus a detailed emailEnrichment object. Best-effort, billed per contact email found; only charged when an email is returned, never for misses.",
            "default": false
          },
          "maxConcurrency": {
            "title": "Max Concurrency",
            "minimum": 1,
            "maximum": 20,
            "type": "integer",
            "description": "Maximum number of pages that can be processed at the same time (max 20). When <b>Fetch full business details</b> is enabled, the effective value is additionally capped to what the run's memory can sustain (about 8 at 1024MB) to prevent out-of-memory failures.",
            "default": 20
          },
          "minConcurrency": {
            "title": "Min Concurrency",
            "type": "integer",
            "description": "Minimum number of pages that will be processed at the same time.",
            "default": 1
          },
          "maxRequestRetries": {
            "title": "Max Request Retries",
            "type": "integer",
            "description": "Number of times the crawler will retry a failed request before giving up.",
            "default": 100
          },
          "proxy": {
            "title": "Proxy configuration (optional override)",
            "type": "object",
            "description": "Leave empty — the actor already routes all traffic through its own built-in residential proxy at no extra cost to you. Only set this if you want to use your own proxies.<br><br>Heads-up: selecting Apify's RESIDENTIAL group here will make the run fail before it starts unless your account has that add-on enabled."
          },
          "proxyProviders": {
            "title": "Proxy providers (preference order)",
            "type": "array",
            "description": "Paying users only. Preference list of providers from the shared proxy-pool KV store. First usable provider wins; add fallbacks like ['evomi','decodo'] for resilience. Rotation is centralised — credentials are updated in the KV store, not in this input. Defaults to ['evomi'].",
            "items": {
              "type": "string"
            },
            "default": [
              "evomi"
            ]
          }
        }
      },
      "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
                  },
                  "PROXY_UNBLOCKER_UNITS": {
                    "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
                  },
                  "PROXY_UNBLOCKER_UNITS": {
                    "type": "integer",
                    "example": 0
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}