---
title: "Restaurant data"
description: "Restaurant data from chain websites, Google Maps, Yelp, Tripadvisor and delivery apps: locations, opening hours, menus, item prices and ratings."
canonical: https://www.zenrows.com/use-cases/restaurant-data
updated: 2026-10-18
---

# Restaurant data

Restaurant data covers locations, opening hours, menus, item prices, ratings and delivery availability for restaurants and chains. Zenrows collects it from chain websites, store locators, Google Maps, Yelp, Tripadvisor and delivery apps, through protected and JavaScript-heavy pages, and returns structured menu and location records for competitive intelligence and market analysis.

## Sample output: one chain location and its menu

```json
{
  "brand": "Example Pizza Co.",
  "store_id": "ld-0417",
  "address": "12 Example High Street, Leeds LS1 0XX, UK",
  "lat": 53.7974,
  "lng": -1.5438,
  "status": "open",
  "first_seen": "2026-09-02",
  "hours": { "mon_thu": "11:00-23:00", "fri_sat": "11:00-00:00", "sun": "12:00-22:00" },
  "services": ["dine_in", "collection", "delivery"],
  "currency": "GBP",
  "menu": [
    { "category": "Pizzas", "item": "Margherita, large", "price": 15.99, "calories": 2010, "new": false },
    { "category": "Pizzas", "item": "Spicy honey pepperoni, large", "price": 18.49, "calories": 2380, "new": true },
    { "category": "Sides", "item": "Garlic bread", "price": 5.49, "calories": 640, "new": false },
    { "category": "Deals", "item": "Two large pizzas and a side", "price": 32.0, "calories": null, "new": false }
  ],
  "rating": { "source": "google_maps", "score": 4.3, "count": 1286 },
  "scraped_at": "2026-10-17T04:30:00Z"
}
```

An illustrative store record from a chain's own locator and menu pages, joined with its Google Maps rating. `first_seen` and `new` come from diffing each run against the last.

## What restaurant data teams collect

Restaurant brands, suppliers, delivery platforms and analysts all start from the same unit: one location, its menu and how customers rate it. What changes is the question they ask of it.

| Job | Data you collect | Typical sources |
|---|---|---|
| Competitor menu and price tracking | Item names, sizes, prices, deals and calories per location | Chain websites, online ordering pages |
| Store openings and closures | Location list, address, coordinates, status, first and last seen | Store locators, Google Maps |
| New product launches | Items added or removed from menus, limited-time offers | Chain menus diffed weekly |
| Site selection and white space | Restaurant density by cuisine and brand per postcode or trade area | Google Maps, Yelp, local directories |
| Reputation and ratings | Score, review count, recent review text, cuisine tags | Google Maps, Yelp, Tripadvisor, OpenTable |
| Delivery presence | Which locations deliver, through which apps, at what menu price | Uber Eats, DoorDash, Grubhub, Deliveroo (see food delivery data scraping) |
| Sales leads for suppliers | Independent restaurants with cuisine, size signals and contact page | Yelp, Google Maps, restaurant websites |

## Why restaurant menu scraping breaks

A menu page is easy to read once. Across several hundred locations, prices vary by store, many menus are drawn by ordering widgets in the browser, and the directories that hold ratings are well protected.

| What breaks | What it looks like | How Zenrows handles it |
|---|---|---|
| Menus rendered by ordering widgets | The menu HTML is empty until a JavaScript ordering app loads | Adaptive Stealth Mode escalates to browser rendering when the menu needs it and returns the full menu |
| Prices set per location | The same item costs more at an airport or city-centre store | Fetch each location's menu URL and keep `store_id` on every price |
| Store locators with hundreds of pages | Locations are spread across city and region pages and map tiles | [Crawl](/products/crawl) follows the locator and keeps only store pages that match a pattern |
| Protected directories | Review directories return CAPTCHA pages from protections such as [DataDome](/bypass/datadome) | Zenrows' stealth gets through DataDome, Cloudflare and similar protections |
| Menus as PDFs or images | Independent restaurants post a scanned menu instead of HTML | Output filters collect the PDF menu links, and screenshots capture image menus |
| Inconsistent menu layouts across brands | Each chain needs its own selectors, which break after redesigns | Extract returns menu items and prices as JSON without per-brand selectors |

## How to collect restaurant data with Zenrows

1. **Map the locations.** Crawl each brand's store locator and keep the pages that match its store URL pattern, such as `/restaurants/` or `/locations/`.
2. **Fetch each menu.** Send every store's menu URL to Fetch in Adaptive Stealth Mode, which renders the ordering widget and gets through protected sites when a page needs it.
3. **Structure the menu.** Use Extract for menus across many brands, or name the fields with CSS selectors when you track one brand closely.
4. **Add ratings.** Join each location with its Google Maps, Yelp or Tripadvisor rating by name and address.
5. **Diff every run.** Compare locations and menu items with the last run to flag openings, closures, launches and price changes.

```bash cURL
# Collect every store page from a chain's locator
curl -X POST "https://api.zenrows.com/v1/crawls" \
  -H "X-API-Key: YOUR_ZENROWS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "STORE_LOCATOR_URL",
    "max_items": 2000,
    "max_pages": 2000,
    "include_patterns": ["/restaurants/"],
    "exclude_patterns": ["/careers/", "/news/"]
  }'
# Omitting output_format returns URLs only. Read them with
# GET https://api.zenrows.com/v1/crawls/{crawl_id}
```

```python Python
# pip install requests
import json
import requests

# Example selectors: match them to the brand's menu page
params = {
    "apikey": "YOUR_ZENROWS_API_KEY",
    "url": "STORE_MENU_URL",
    "mode": "auto",  # Adaptive Stealth Mode renders the ordering widget when needed
    "css_extractor": json.dumps({
        "category": ".menu-section h2",
        "item": ".menu-item .item-name",
        "price": ".menu-item .item-price",
        "calories": ".menu-item .item-kcal",
    }),
}
menu = requests.get("https://api.zenrows.com/v1/", params=params).json()
print(menu)
```

```bash CLI
npx -y @zenrows/cli init
# Every store page behind a chain's locator
zenrows crawl create STORE_LOCATOR_URL --include-patterns /restaurants/ --max-pages 1000 --follow
# Structured menu items from any brand's menu page, no selectors
zenrows extract STORE_MENU_URL --autoparse
```

```text Claude Code (MCP)
# Connect once
claude mcp add zenrows -e ZENROWS_API_KEY=YOUR_ZENROWS_API_KEY -- npx -y @zenrows/mcp

# Then ask:
Use Zenrows to extract every item, price and calorie count from STORE_MENU_URL, grouped by menu section, then add the same store's Google Maps rating and review count. Return one JSON record with the store address.
```

## Which Zenrows product fits

| If you need to | Use | Why |
|---|---|---|
| List every location of a chain | [Crawl](/products/crawl) | Follows the store locator on one domain and returns the store URLs |
| Read one store's menu with your own field names | [Fetch](/products/fetch) | Renders the ordering widget and returns the fields you name with CSS selectors |
| Collect menus across many brands | [Extract](/products/extract) | Automatic structured extraction returns items and prices as JSON with no per-brand selectors |
| Pull a menu or rating from the terminal or a cron job | [CLI](/cli) | One command fetches or extracts a page, ready to pipe into your scripts |

## What success looks like

| Measure | What good looks like | How to check it |
|---|---|---|
| Location coverage | Your store count per brand matches the brand's own locator | Compare unique `store_id` per brand with the locator total |
| Menu completeness | Every tracked location has a current menu with a price on each item | Report items without a price per brand per run |
| Change detection lead time | Openings, closures and launches appear within one refresh of going live | Compare `first_seen` with the date the brand announced the change |
| Per-location price fidelity | Prices differ by store where the brand prices locally, and you keep that difference | Sample stores in different areas and check prices by hand |
| Stakeholder trust | Marketing, property and operations teams use the feed without re-checking it | Count corrections requested per month |

## Restaurant data FAQ

### What is restaurant data?

Restaurant data is structured information about restaurants and chains: names, brands, addresses, coordinates, opening hours, cuisine, menus with item prices and calories, services such as delivery or collection, and ratings and reviews. Restaurant brands, food suppliers, delivery platforms, investors and real estate teams use it for pricing, site selection, sales and market analysis.

### How do I scrape restaurant menus?

Find each location's menu URL, usually from the store locator, then fetch it with Adaptive Stealth Mode, which escalates to browser rendering because most chain menus load through an ordering widget. Read items and prices with CSS selectors or let Extract return them as JSON. Repeat per location, since many chains price items differently by store.

### Where does restaurant menu data come from?

The most accurate menu data comes from the brand's own website and ordering pages, because that is where prices are set. Delivery apps such as Uber Eats and DoorDash show menus too, which can carry a mark-up over in-store prices. Google Maps, Yelp and Tripadvisor add ratings, hours and photos rather than complete priced menus.

### Can I scrape Uber Eats and DoorDash menus?

Yes. Delivery app menus are rendered with JavaScript and depend on the delivery address, so you set the address before reading prices and fees. Zenrows handles this with page actions that fill in the address before the page is returned, or with a Browser session. The food delivery data scraping page covers address-based pricing, fees and delivery times in detail.

### How do I track restaurant openings and closures?

Crawl each brand's store locator on a schedule and store every store ID with the date it first and last appeared. A new ID is an opening; an ID that disappears for two runs in a row is a likely closure. Google Maps marks some closures too, which helps confirm what the locator shows.

### How do I get restaurant location data?

Start with each chain's store locator, which lists every location with its address, hours, services and store ID. Crawl the locator, keep the pages that match its store URL pattern, and extract the fields as JSON. For independents, search Google Maps or Yelp by city and cuisine, then add coordinates and ratings to each record and deduplicate by address.

### Is it legal to scrape restaurant data?

Menus, prices, addresses and opening hours are published for customers and widely collected for competitive research. Reviews and photos are user content that you should not republish, and reviewer names are personal data. Check each site's terms and ask your legal team about your specific use.

## Related use cases

- [Review scraping](https://www.zenrows.com/use-cases/review-scraping.md): Review text, ratings, dates and replies from Amazon, Google, Yelp, Trustpilot and G2, every page.
- [Competitive intelligence](https://www.zenrows.com/use-cases/competitive-intelligence.md): Pricing, hiring, reviews, launches and partners across your market, structured for battlecards and strategy.
- [Competitor price scraping](https://www.zenrows.com/use-cases/price-scraping.md): Competitor prices, promotions, shipping and stock per SKU from retailers and marketplaces, refreshed on your schedule.
- [Retail data scraping](https://www.zenrows.com/use-cases/retail-data.md): Store locations, store-level prices, promotions and click-and-collect stock from grocery and high-street chains.

Start free: https://app.zenrows.com/register. Docs: https://docs.zenrows.com
