# Uber Eats Scraper — Menus, Reviews & Leads (`memo23/uber-eats-scraper`) Actor

Uber Eats scraper for restaurants and menus. Search by city or paste store URLs. One row per store with priced dishes, ratings, reviews, phone, hours, and optional emails plus a lead score. Filter by locale or skip stores you already have. Delivery or pickup. JSON, CSV or Excel.

- **URL**: https://apify.com/memo23/uber-eats-scraper.md
- **Developed by:** [Muhamed Didovic](https://apify.com/memo23) (community)
- **Categories:** E-commerce, Automation, Lead generation
- **Features:** Limited permissions
- **Stats:** 357 total users, 58 monthly users, 96.4% runs succeeded, 1 bookmarks
- **User rating**: 4.87 out of 5 stars

## Pricing

from $2.50 / 1,000 results

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

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

### Overview

Extract structured restaurant and menu data from **Uber Eats** store pages: store identity, SEO metadata, ratings, address, hours, cuisines, full menu (JSON-LD sections and a flattened item list), optional customization trees, review samples, and delivery hints. Each input URL produces one rich dataset item suitable for price monitoring, menu syndication, or market research.

The **Uber Eats Store Scraper** is an Apify actor that loads public Uber Eats **store detail** URLs, parses embedded application state and JSON-LD, and returns a single JSON object per store with menu items, images, and analytics-style rollups.

With this scraper, you get **complete menu coverage** where exposed in the page payload (sections, item names, descriptions, prices, images), plus **store-level context** such as open/closed status, rating summary, phone number, and formatted address.

**Key metrics** include per-item prices, aggregate menu analytics (min/max/average price, image coverage, customization stats), and review text samples hydrated from the store page state.

The actor is built for **reliable high-concurrency fetching** using Crawlee with an HTTP client tuned for real browser fingerprints, with proxy support for stable runs.

Whether you are aggregating menus across brands, comparing pricing by location, or building a food-data product, this actor returns consistent JSON you can pipe into databases or spreadsheets.

***

### Features

- **Store page extraction**:
  - Accepts standard Uber Eats store URLs (with locale path or `/store/{slug}/{id}` form)
  - Resolves canonical URLs, titles, descriptions, and hero imagery

- **Comprehensive menu data**:
  - **menuSections**: Schema.org `Menu`/`MenuSection`/`MenuItem` structure from JSON-LD where present
  - **menuItems**: Flattened list with section names, prices, currency, images, UUIDs, and (optionally) full customization groups/options

- **Ratings, reviews, and trust signals**:
  - **ratingValue** and **reviewCount** from structured data or embedded state
  - **reviewSamples**: reviewer-visible comments and dates from embedded `storeReviews`

- **Location & operations**:
  - Postal address object and **addressText**, **phoneNumber**, **hours**, **isOpen** / **isOrderable**, **closedMessage**
  - **cuisineList**, **currencyCode**, **priceBucket**, FAQ count, ETA range when detectable

- **Analytics & diagnostics**:
  - **analytics** object summarizing section count, item counts, price and image coverage, customization coverage
  - HTTP **statusCode**, **contentType**, **bodyLength**, optional saved **htmlFile** reference, **scrapedAt** timestamp

- **Efficient crawling**:
  - Cheerio-based parsing with Impit HTTP client (Firefox fingerprint, HTTP/3)
  - Configurable concurrency, retries, and Apify proxy integration

***

### How to Use

1. **Set Up**: Ensure you have an Apify account and access to the Apify platform.
2. **Provide Input Data**: Add one or more **Uber Eats store URLs** to `startUrls`.
3. **Adjust Scraper Settings**: Configure settings like `maxItems`, `maxConcurrency`, and `maxRequestRetries` to optimize performance.
4. **Run the Scraper**: Execute the actor on the Apify platform.
5. **Download Results**: Export the scraped data in your preferred format (JSON, CSV, Excel).

#### Usage Limitations

**Free Users**: Non-paying users are limited to scraping **50 listings** per run. To access unlimited scraping and all features, please upgrade to a paid Apify account.

**Paid Users**: Enjoy unlimited scraping, multiple start URLs, and full access to all scraper features.

***

### Input Configuration

To use the scraper, configure the input parameters as follows:

```json
{
    "startUrls": [
        {
            "url": "https://www.ubereats.com/store/jersey-mikes-subs-creekside/B5-ImzTAQf2En_RCiqU62A"
        },
        {
            "url": "https://www.ubereats.com/en-US/store/jersey-mikes-subs-creekside/B5-ImzTAQf2En_RCiqU62A"
        }
    ],
    "maxItems": 300,
    "maxConcurrency": 100,
    "minConcurrency": 1,
    "maxRequestRetries": 100,
    "includeItemCustomizations": false,
    "reviewsSince": "2025-01-01",
    "maxReviews": 100,
    "proxy": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ]
    }
}
```

#### Input Fields Explanation

There are **two ways to run the scraper** — use either (or both):

**A) Search by location (discovery mode)** — find every store in a city/area without needing URLs:

- **Search location** (`address`): A city or full address, e.g. `Chicago, IL` or `1407 N Main St, Blacksburg VA`. The actor geocodes it and pulls all matching Uber Eats stores, then scrapes each one. Leave empty if you're using Start URLs.
- **Search keyword** (`searchQuery`): Optional dish/cuisine term such as `pizza`, `sushi`, `tacos`. **Leave it empty to browse every store at the location** (all verticals — restaurants, grocery, convenience, etc.). With a keyword, results are filtered to that term.
- **Match store name** (`matchStoreName`): Turn this on when the Search keyword is the name of one specific restaurant or shop (e.g. `La La Land`). The run returns the first store whose name matches the keyword (one store per run); case, accents, punctuation, `&`, the branch in brackets and a ` - tagline` are ignored when comparing. Uber Eats search otherwise fills the results with other nearby stores. If no store with that name is on Uber Eats near the address, the run succeeds with no results and says so in the status message. Ignored when the keyword is empty.
- **Store type / vertical** (`storeType`): Preferred vertical — `ALL` (recommended) plus the full Uber Eats set: `RESTAURANTS`, `GROCERY`, `SHOP`, `CONVENIENCE`, `PHARMACY`, `PRESCRIPTIONS`, `PERSONAL_CARE`, `BABY`, `SPECIALITY_FOODS`, `PET_SUPPLIES`, `FLOWERS`, `RETAIL`, `BEAUTY`, `ELECTRONICS`, `ALCOHOL`. Narrower verticals are best‑effort; Uber may still return a mixed set.
- **Dining mode** (`diningMode`): `DELIVERY` (default) or `PICKUP` feed.
- **Latitude / Longitude** (`latitude`, `longitude`): Optional. Provide precise coordinates to skip geocoding (useful when a city name is ambiguous). Overrides `address`.
- **Locale** (`locale`): Optional Uber Eats locale prefix (`en-US`, `en-GB`, `es-MX`) applied to discovered store URLs that have none yet.
- **Country code** (`addressCountry`): Optional ISO 3166-1 alpha-2 code (`US`, `GB`, `MX`) that pins Nominatim when a city name exists in more than one country.
- **Exclude store IDs** (`excludeStores`): Store UUIDs or path IDs to skip on a city re-run.

> **Location accuracy & proxies:** Uber Eats geolocates the search by the **proxy's exit IP**, not just the coordinates. For accurate city targeting the actor automatically pins the proxy exit to the geocoded city (supported on Decodo/Smartproxy residential). Discovery therefore requires a residential proxy; with a city‑targetable provider you get exactly the requested city, otherwise results fall back to the proxy's region.

**B) Scrape specific store URLs** — paste exact stores to scrape:

- **Start URLs** (`startUrls`): Optional. Public **Uber Eats store** URLs. Supported shapes include:
  - `https://www.ubereats.com/store/{store-slug}/{store-id}`
  - `https://www.ubereats.com/{locale}/store/{store-slug}/{store-id}` (locale prefix, e.g. `en-US`)
  - `https://www.ubereats.com/store-browse-uuid/{uuid}` (and with a locale prefix). Uber redirects to the canonical `/store/...` URL; the actor follows the redirect and resolves `storeId` / slug from the final URL.

**Shared options:**

- **Max Items** (`maxItems`): Maximum number of stores to process per run (one dataset item per successful store page). Default is `10000` (set to `0` for unlimited).
- **Max Concurrency** (`maxConcurrency`): Maximum number of pages processed simultaneously. Default is `100`.
- **Min Concurrency** (`minConcurrency`): Minimum number of pages processed simultaneously. Default is `1`.
- **Max Request Retries** (`maxRequestRetries`): Number of retries for failed requests. Default is `100`.
- **Include item customizations** (`includeItemCustomizations`): When `true`, enriches `menuItems` with full customization group/option payloads (much larger output).
- **Reviews Since** (`reviewsSince`): Optional ISO date (e.g. `2025-01-01` or `2025-01-01T00:00:00Z`). When set, only reviews with `createdAt >= reviewsSince` are returned. Useful for incremental scheduled runs (Mon/Wed/Fri) so you don't re-fetch / re-charge for old reviews.
- **Max Reviews** (`maxReviews`): Optional cap on the number of reviews returned per store (applied after `reviewsSince` filtering). Set to `0` or omit for no cap. Uber Eats only shows a small sample of written reviews per store (usually about 10 to 20), and many stores show none at all even with thousands of star ratings. This setting limits that sample; it cannot return more reviews than Uber Eats displays on the store page.
- **Proxy Configuration** (`proxy`): Settings for reliable and anonymous scraping. Supports custom proxy URLs.

***

### Output Structure

The scraper produces structured JSON output: **one object per store URL** with nested menu data, metadata, and optional debug fields.

#### Sample Data

*The record below is the **first item** from `data.json` (Jersey Mike's Subs example). The full export is very large; for readability this sample keeps all scalar fields and analytics as in the source object, shows the **first four** `heroImageUrls`, the **first two** `menuSections` with **two** dishes each, and **three** `menuItems` chosen from items without heavy customization payloads. Counts such as `menuItemCount` and `analytics` reflect the full scrape—not the shortened arrays.*

```json
{
    "source": "ubereats_detail_html",
    "url": "https://www.ubereats.com/store/jersey-mikes-subs-creekside/B5-ImzTAQf2En_RCiqU62A?diningMode=PICKUP&pl=JTdCJTIyYWRkcmVzcyUyMiUzQSUyMjE0MDclMjBOJTIwTWFpbiUyMFN0JTIyJTJDJTIycmVmZXJlbmNlJTIyJTNBJTIyaGVyZSUzQWFmJTNBc3RyZWV0c2VjdGlvbiUzQWlRanFNei05UUYwT1FIYXRmOU9qR0QlM0FDZ2dJQkNDZDc3YXVBeEFCR2dReE5EQTMlMjIlMkMlMjJyZWZlcmVuY2VUeXBlJTIyJTNBJTIyaGVyZV9wbGFjZXMlMjIlMkMlMjJsYXRpdHVkZSUyMiUzQTM3LjI0NzUzJTJDJTIybG9uZ2l0dWRlJTIyJTNBLTgwLjQxNTEyJTdE&surfaceName=",
    "restaurantUrl": "https://www.ubereats.com/store/jersey-mikes-subs-creekside/B5-ImzTAQf2En_RCiqU62A",
    "shopName": "jersey-mikes-subs-creekside",
    "shopId": "B5-ImzTAQf2En_RCiqU62A",
    "message": "success: menu found.",
    "hasMenu": true,
    "menuType": "Menu",
    "storeId": "B5-ImzTAQf2En_RCiqU62A",
    "storeUuid": "079f889b-34c0-41fd-849f-f4428aa53ad8",
    "storeSlug": "jersey-mikes-subs-creekside",
    "citySlug": "houston",
    "locale": null,
    "htmlLocale": "en-US",
    "originalUrl": "https://www.ubereats.com/store/jersey-mikes-subs-creekside/B5-ImzTAQf2En_RCiqU62A?diningMode=PICKUP&pl=JTdCJTIyYWRkcmVzcyUyMiUzQSUyMjE0MDclMjBOJTIwTWFpbiUyMFN0JTIyJTJDJTIycmVmZXJlbmNlJTIyJTNBJTIyaGVyZSUzQWFmJTNBc3RyZWV0c2VjdGlvbiUzQWlRanFNei05UUYwT1FIYXRmOU9qR0QlM0FDZ2dJQkNDZDc3YXVBeEFCR2dReE5EQTMlMjIlMkMlMjJyZWZlcmVuY2VUeXBlJTIyJTNBJTIyaGVyZV9wbGFjZXMlMjIlMkMlMjJsYXRpdHVkZSUyMiUzQTM3LjI0NzUzJTJDJTIybG9uZ2l0dWRlJTIyJTNBLTgwLjQxNTEyJTdE&surfaceName=",
    "canonicalUrl": "https://www.ubereats.com/store/jersey-mikes-subs-creekside/B5-ImzTAQf2En_RCiqU62A",
    "loadedUrl": "https://www.ubereats.com/store/jersey-mikes-subs-creekside/B5-ImzTAQf2En_RCiqU62A?diningMode=PICKUP&pl=JTdCJTIyYWRkcmVzcyUyMiUzQSUyMjE0MDclMjBOJTIwTWFpbiUyMFN0JTIyJTJDJTIycmVmZXJlbmNlJTIyJTNBJTIyaGVyZSUzQWFmJTNBc3RyZWV0c2VjdGlvbiUzQWlRanFNei05UUYwT1FIYXRmOU9qR0QlM0FDZ2dJQkNDZDc3YXVBeEFCR2dReE5EQTMlMjIlMkMlMjJyZWZlcmVuY2VUeXBlJTIyJTNBJTIyaGVyZV9wbGFjZXMlMjIlMkMlMjJsYXRpdHVkZSUyMiUzQTM3LjI0NzUzJTJDJTIybG9uZ2l0dWRlJTIyJTNBLTgwLjQxNTEyJTdE&surfaceName=",
    "title": "Jersey Mike's Subs (Creekside)",
    "seoTitle": "Order Jersey Mike's Subs (Creekside) - Menu & Prices - Houston Delivery | Uber Eats",
    "description": "Use your Uber account to order delivery from Jersey Mike's Subs (Creekside) in Houston. Browse the menu, view popular items, and track your order.",
    "image": "https://tb-static.uber.com/prod/image-proc/processed_images/dc10f9e54832652cfaea6214699a22b8/19ec62ba51fde35ba0aff5b84321c5af.jpeg",
    "heroImageUrls": [
        "https://tb-static.uber.com/prod/image-proc/processed_images/dc10f9e54832652cfaea6214699a22b8/19ec62ba51fde35ba0aff5b84321c5af.jpeg",
        "https://tb-static.uber.com/prod/image-proc/processed_images/dc10f9e54832652cfaea6214699a22b8/c67d558c633281f9c71fc6a90ef470ae.jpeg",
        "https://tb-static.uber.com/prod/image-proc/processed_images/dc10f9e54832652cfaea6214699a22b8/8681e511c0910e0052cb155c50f01f89.jpeg",
        "https://tb-static.uber.com/prod/image-proc/processed_images/dc10f9e54832652cfaea6214699a22b8/6ea4f72e303000fbf403bbb36f971f85.jpeg"
    ],
    "ratingValue": 4.9,
    "reviewCount": 1000,
    "phoneNumber": "+13468085967",
    "address": {
        "@type": "PostalAddress",
        "addressLocality": "Spring",
        "addressRegion": "TX",
        "postalCode": "77389",
        "addressCountry": "US",
        "streetAddress": "25640 Kuykendahl Rd"
    },
    "addressText": "25640 Kuykendahl Rd, Spring, TX, 77389, US",
    "pllStatus": "200",
    "faqCount": 8,
    "currencyCode": "USD",
    "priceBucket": "$",
    "cuisineList": [
        "Sandwich",
        "American",
        "Bar Food"
    ],
    "isOrderable": true,
    "isOpen": true,
    "closedMessage": "",
    "hours": [
        {
            "dayRange": "Every Day",
            "sectionHours": [
                {
                    "startTime": 600,
                    "endTime": 1230,
                    "sectionTitle": ""
                }
            ]
        }
    ],
    "menuSections": [
        {
            "@type": "MenuSection",
            "name": "Free with $15 purchase",
            "hasMenuItem": [
                {
                    "@type": "MenuItem",
                    "name": "#7 Turkey and Provolone",
                    "description": "Provolone and Turkey. Served Mike's Way with onions, lettuce, tomato, vinegar, oil, oregano, and salt.",
                    "offers": {
                        "@type": "Offer",
                        "price": "11.94",
                        "priceCurrency": "USD"
                    },
                    "imageUrl": "https://tb-static.uber.com/prod/image-proc/processed_images/e092aa3249393c24f6edf40d7ca2af39/c67fc65e9b4e16a553eb7574fba090f1.jpeg"
                },
                {
                    "@type": "MenuItem",
                    "name": "Mike's Hot Italian",
                    "description": "Ham, salami & pepperoni with melted provolone cheese. Topped with grilled onions, lettuce, tomato and Hot Chopped Pepper Relish.",
                    "offers": {
                        "@type": "Offer",
                        "price": "10.73",
                        "priceCurrency": "USD"
                    },
                    "imageUrl": "https://tb-static.uber.com/prod/image-proc/processed_images/56eae964cde0acab6c6fc55337064832/c67fc65e9b4e16a553eb7574fba090f1.jpeg"
                }
            ]
        },
        {
            "@type": "MenuSection",
            "name": "Cold Subs",
            "hasMenuItem": [
                {
                    "@type": "MenuItem",
                    "name": "#7 Turkey and Provolone",
                    "description": "Provolone and Turkey. Served Mike's Way with onions, lettuce, tomato, vinegar, oil, oregano, and salt.",
                    "offers": {
                        "@type": "Offer",
                        "price": "11.94",
                        "priceCurrency": "USD"
                    },
                    "imageUrl": "https://tb-static.uber.com/prod/image-proc/processed_images/e092aa3249393c24f6edf40d7ca2af39/c67fc65e9b4e16a553eb7574fba090f1.jpeg"
                },
                {
                    "@type": "MenuItem",
                    "name": "#13 The Original Italian",
                    "description": "Provolone, ham, prosciuttini, cappacuolo, salami, and pepperoni. Served Mike's Way with onions, lettuce, tomato, vinegar, oil, oregano, and salt.",
                    "offers": {
                        "@type": "Offer",
                        "price": "13.13",
                        "priceCurrency": "USD"
                    },
                    "imageUrl": "https://tb-static.uber.com/prod/image-proc/processed_images/209a01090468cd8010e498c5183faea7/c67fc65e9b4e16a553eb7574fba090f1.jpeg"
                }
            ]
        }
    ],
    "menuSectionCount": 5,
    "menuSubsectionCount": 3,
    "menuItemCount": 39,
    "menuItems": [
        {
            "name": "#31 California Chicken Cheese Steak",
            "description": "Lettuce, tomato, mayo, and white American cheese (Not available in all locations).",
            "section": "Hot Subs",
            "price": 12.84,
            "currency": "USD",
            "imageUrl": "https://tb-static.uber.com/prod/image-proc/processed_images/d206b0e4d9dfb622a47210018c48e300/c67fc65e9b4e16a553eb7574fba090f1.jpeg",
            "hasImage": true
        },
        {
            "name": "Chips",
            "description": "",
            "section": "Sides, Drinks, & Desserts",
            "price": 2.34,
            "currency": "USD",
            "imageUrl": "https://tb-static.uber.com/prod/image-proc/processed_images/8e6b2d2fb9564795b2c343c185311f5e/c67fc65e9b4e16a553eb7574fba090f1.jpeg",
            "hasImage": true
        },
        {
            "name": "Cookie",
            "description": "Fresh-baked and delicious.",
            "section": "Sides, Drinks, & Desserts",
            "price": 1.8,
            "currency": "USD",
            "imageUrl": "https://tb-static.uber.com/prod/image-proc/processed_images/79b1243942eefeb750018d1cae8f654c/c67fc65e9b4e16a553eb7574fba090f1.jpeg",
            "hasImage": true
        }
    ],
    "analytics": {
        "totalSections": 5,
        "totalItems": 39,
        "itemsWithPrices": 39,
        "itemsWithoutPrices": 0,
        "minPrice": 1.8,
        "maxPrice": 13.86,
        "averagePrice": 10.22,
        "itemsWithImages": 39,
        "itemsWithoutImages": 0,
        "imageCoverage": 100,
        "priceCoverage": 100,
        "itemsWithCustomizations": 31,
        "itemsWithoutCustomizations": 8,
        "customizationCoverage": 79,
        "totalCustomizationGroups": 31,
        "totalCustomizationOptions": 2542
    },
    "reviewSamples": [
        {
            "contentUUID": "00000000-0000-0000-0000-000000000001",
            "eaterName": "Pamela G.",
            "reviewText": "The sandwich is just awesome",
            "formattedDate": "08/14/24",
            "createdAt": "2024-08-14T00:00:00",
            "timeSinceReview": "1 year ago"
        },
        {
            "eaterName": "Michael M.",
            "reviewText": "Always good",
            "formattedDate": "05/13/24",
            "createdAt": "2024-05-13T00:00:00"
        },
        {
            "eaterName": "Chase H.",
            "reviewText": "#13 is the best",
            "formattedDate": "02/27/24",
            "createdAt": "2024-02-27T00:00:00"
        },
        {
            "eaterName": "pamula S.",
            "reviewText": "Great sandwiches",
            "formattedDate": "02/02/24",
            "createdAt": "2024-02-02T00:00:00"
        },
        {
            "eaterName": "Cody P.",
            "reviewText": "Delicious!",
            "formattedDate": "10/15/23",
            "createdAt": "2023-10-15T00:00:00"
        },
        {
            "eaterName": "Jose G.",
            "reviewText": "Great Sandwiches and excellent service",
            "formattedDate": "10/11/23",
            "createdAt": "2023-10-11T00:00:00"
        },
        {
            "eaterName": "yadder M.",
            "reviewText": "Delicious",
            "formattedDate": "09/18/23",
            "createdAt": "2023-09-18T00:00:00"
        },
        {
            "eaterName": "Steve R.",
            "reviewText": "Excellent store.",
            "formattedDate": "07/22/23",
            "createdAt": "2023-07-22T00:00:00"
        },
        {
            "eaterName": "Linda J.",
            "reviewText": "The food is amazing and the delicious very driver was wonderful",
            "formattedDate": "07/10/23",
            "createdAt": "2023-07-10T00:00:00"
        },
        {
            "eaterName": "Sherley S.",
            "reviewText": "Delicious",
            "formattedDate": "06/24/23",
            "createdAt": "2023-06-24T00:00:00"
        },
        {
            "eaterName": "Claudia D.",
            "reviewText": "Fresh and delicious",
            "formattedDate": "05/27/23",
            "createdAt": "2023-05-27T00:00:00"
        },
        {
            "eaterName": "Vanessa G.",
            "reviewText": "Delicious!",
            "formattedDate": "03/26/23",
            "createdAt": "2023-03-26T00:00:00"
        },
        {
            "eaterName": "Mark N.",
            "reviewText": "Great",
            "formattedDate": "12/29/21",
            "createdAt": "2021-12-29T00:00:00"
        }
    ],
    "menuItemMentions": 40,
    "sectionMentions": 200,
    "reviewMentions": 1,
    "reactQueryQueryCount": 6,
    "catalogItemCount": 35,
    "itemCustomizationsEnabled": true,
    "customizationItemsCount": 31,
    "deliveryFeeText": null,
    "etaRange": "16–16 Min",
    "statusCode": 200,
    "contentType": "text/html; charset=utf-8",
    "bodyLength": 1399934,
    "htmlFile": "UBER_EATS_DETAIL-B5-ImzTAQf2En_RCiqU62A-1774802693942.html",
    "scrapedAt": "2026-03-29T16:45:03.903Z"
}
```

***

### Output Fields Explanation

Below is an explanation of the fields in the output JSON, categorized for clarity.

#### 1. Identification, URLs & source

- **source**: How the row was produced (e.g. `ubereats_detail_html`).
- **url** / **originalUrl** / **loadedUrl** / **canonicalUrl** / **restaurantUrl**: Request and canonical store links.
- **shopName**, **shopId**, **storeId**, **storeUuid**, **storeSlug**, **citySlug**: Store identifiers and slug paths.
- **locale**, **htmlLocale**: Language/locale hints from input or HTML.
- **message**: Short status such as menu discovery outcome.
- **hasMenu**, **menuType**: Whether a menu was found and its label.

#### 2. Store presentation & SEO

- **title**: Store display name.
- **seoTitle**, **description**: Meta title and description.
- **image**: Primary image URL.
- **heroImageUrls**: Gallery of hero/banner images from the page payload.

#### 3. Ratings, reviews & FAQs

- **ratingValue**: Average rating when available.
- **reviewCount**: Total ratings count from structured data (may exceed length of embedded review text).
- **reviewSamples**: Array of public reviews extracted from the embedded `storeReviews` payload. Each item contains:
  - `contentUUID` – stable Uber-side review identifier (use as a deduplication key for incremental runs).
  - `eaterName` – reviewer display name (e.g. `"Pamela G."`).
  - `reviewText` – verbatim review body.
  - `formattedDate` – locale-formatted date as shown in the UI (e.g. `"08/14/24"`, `MM/DD/YY` in the US locale).
  - `createdAt` – ISO 8601 timestamp from the page state (e.g. `"2024-08-14T00:00:00"`). Use this for programmatic parsing / deduplication / incremental fetching.
  - `timeSinceReview` – relative human label when Uber exposes one (e.g. `"2 weeks ago"`, may be `null`).
  - **Sort order**: returned in the order Uber serves them in the embedded state. This is **not** guaranteed to be strictly newest-first; always sort by `createdAt` client-side if you depend on chronology.
  - **Per-review numeric star rating**: Uber Eats does not expose a per-review numeric star rating publicly. Star ratings are only available aggregated at the store level (`ratingValue`, `reviewCount`).
  - **Date filtering**: pass the `reviewsSince` input (ISO date) to keep only reviews with `createdAt >= reviewsSince`. Use `maxReviews` to cap the number of returned reviews per store.
  - **Sample size**: Uber Eats only shows a small sample of written reviews per store (usually about 10 to 20), and many stores show none at all even with thousands of star ratings. In that case `reviewSamples` is an empty array while `ratingValue` and `reviewCount` are still filled. `maxReviews` limits that sample; it cannot return more reviews than Uber Eats displays on the store page.
- **faqCount**: Number of FAQ entries when exposed.

#### 4. Location, contact & cuisines

- **address**: JSON-LD `PostalAddress` object.
- **addressText**: Single-line formatted address.
- **latitude**, **longitude**: Store coordinates (from JSON-LD geo, the location-search card, or the URL place context).
- **geo**: `{ latitude, longitude }` convenience object (null when coordinates are unavailable).
- **phoneNumber**: Store phone when present.
- **cuisineList**: Cuisine tags (e.g. Sandwich, American).
- **currencyCode**, **priceBucket**: Currency and price band indicator.

#### 5. Ordering state & hours

- **supportedDiningModes**: Dining modes the store offers (e.g. `DELIVERY`, `PICKUP`) with labels.
- **locationType**: Storefront location type (e.g. `PHYSICAL`).
- **isOrderable**, **isOpen**: Whether the storefront accepts orders and is open.
- **closedMessage**: User-visible closed copy when applicable.
- **hours**: Structured opening hours (day ranges and section hours).

#### 6. Menu (structured & flat)

- **menuSections**: JSON-LD-style menu sections with nested `MenuItem` entries (name, description, `offers` price, `imageUrl`).
- **menuSectionCount**, **menuSubsectionCount**, **menuItemCount**: Counts for sections, subsections, and items.
- **menuItems**: Denormalized list of items (name, description, section, price, currency, image, UUIDs, optional `customizations` when enabled).

#### 7. Analytics & embedded-state hints

- **analytics**: Aggregates such as total items, min/max/average price, image and price coverage, customization coverage and option counts.
- **menuItemMentions**, **sectionMentions**, **reviewMentions**: Raw substring occurrence counts in HTML (debug/quality signal).
- **reactQueryQueryCount**: Number of React Query entries parsed from `__REACT_QUERY_STATE__`.
- **catalogItemCount**, **itemCustomizationsEnabled**, **customizationItemsCount**: Catalog sizing hints from embedded data.

#### 8. Delivery hints & HTTP metadata

- **deliveryFeeText**, **etaRange**: Delivery fee copy and ETA range when parsed from HTML.
- **promos**: Offer/promo labels (e.g. `"$5 off $25+"`) for stores found via location search. Empty for stores scraped directly by URL.
- **featuredItemsSections**: Uber's featured-items payload for the store when present.
- **pllStatus**: Internal PLL script status code string when present.
- **statusCode**, **contentType**, **bodyLength**: HTTP response metadata.
- **htmlFile**: Filename of optional saved HTML snapshot on disk (local/debug runs).
- **scrapedAt**: ISO timestamp when the row was produced.

***

### Benefits of the Uber Eats Store Scraper

- **Menu intelligence**: Capture full menus with prices and images in one request per store.
- **Competitive research**: Compare item pricing and assortment across locations and brands.
- **Data pipelines**: Clean JSON ready for warehouses, BI tools, or enrichment workflows.
- **Review snippets**: Quick access to verbatim customer feedback included in the initial page payload.
- **Operational flags**: Open/closed and orderable state for availability monitoring.

***

### Why Choose the Uber Eats Store Scraper?

The Uber Eats Store Scraper is built for teams that need **repeatable, structured restaurant data** without maintaining headless browsers for every run. It combines fast HTTP fetching with parsing of Uber’s embedded state and JSON-LD so you get menus and metadata in one JSON document per store.

**Use Cases:**

- Building or updating a restaurant menu database
- Tracking price or item changes over time
- Enriching location datasets with cuisines, hours, and ratings
- Sampling customer review text for sentiment or QA

***

### Technical Implementation

The actor uses a robust extraction pipeline:

1. **HTTP fetch**: Crawlee `CheerioCrawler` with an Impit-based client (Firefox profile, HTTP/3, optional TLS relaxations) and Apify proxy support.
2. **State & JSON-LD parsing**: Reads `__REACT_QUERY_STATE__` and JSON-LD blocks to recover store entity, menu, ratings, and reviews.
3. **Menu flattening & analytics**: Derives flat `menuItems`, optional customization trees, and summary statistics for coverage and pricing.

***

### Explore More Scrapers

If you found this actor useful, check out other scrapers at [memo23's Apify profile](https://apify.com/memo23). We offer a wide range of tools to enhance your web scraping and automation needs.

***

### Integrations

Output flows straight into your stack via Apify's native integrations — **Google Sheets, Slack, Zapier, Make, Airbyte, GitHub, and webhooks** — plus a full **REST API**, **JavaScript/Python clients**, and an **MCP server** for AI agents. Schedule runs and stream new results to your destination of choice with no extra code.

***

### Support

- For issues or feature requests, please use the **Issues** section of this actor in the Apify console.
- For further assistance, contact the author:
  - Author's website: <https://muhamed-didovic.github.io/>
  - Email: <muhamed.didovic@gmail.com>

***

### Additional Services

- Request customization or a full dataset: <muhamed.didovic@gmail.com>
- Need other platforms scraped? Contact <muhamed.didovic@gmail.com>
- For API services of this scraper, reach out to <muhamed.didovic@gmail.com>
- Custom integrations and automation solutions available

***

### Version History

- **v1.0**: Uber Eats store detail extraction (menu, reviews, analytics)
- Cheerio + embedded React Query state parsing
- Optional full item customization payloads

***

### Legal & Compliance

This scraper is designed for legitimate business and research purposes. Users are responsible for:

- Complying with **Uber** and **Uber Eats** terms of service and applicable laws
- Respecting robots.txt, rate limiting, and platform policies
- Using scraped data in accordance with applicable privacy and consumer regulations
- Obtaining necessary permissions for commercial use of data

***

### FAQ

**Q: How many stores can I scrape per run?**\
A: Free users can process up to **50** stores per run (same `maxItems` cap as other actors on this billing tier). Paid users can raise limits according to their plan.

**Q: Does the actor return the full menu?**\
A: Yes, when Uber Eats exposes the menu in the store page payload. Output includes both JSON-LD `menuSections` and a flattened `menuItems` list; enabling `includeItemCustomizations` adds large per-item customization trees.

**Q: Are prices and currency included?**\
A: Yes. Items include numeric prices and `currency` (typically `USD` for US stores), plus store-level `currencyCode`.

**Q: How fresh is the data?**\
A: Data reflects the store page at crawl time (`scrapedAt`).

**Q: Can I scrape without proxies?**\
A: You can, but residential or datacenter proxies are recommended for stable production runs.

**Q: I set `maxReviews` to 100 but got few or no reviews. Why?**\
A: Uber Eats only shows a small sample of written reviews per store (usually about 10 to 20), and many stores show none at all even with thousands of star ratings. The `reviewCount` figure (for example `30000+`) counts star ratings, not written reviews. `maxReviews` limits that sample; it cannot return more reviews than Uber Eats displays on the store page. Open the store on ubereats.com: if it has no reviews section there, the actor returns an empty `reviewSamples` array.

***

### 🤖 For AI Agents & LLM Apps

Compact reference for AI agents calling this actor via the [Apify MCP server](https://mcp.apify.com) or the Apify API (actor: `memo23/uber-eats-scraper`).

**Purpose:** Scrapes public Uber Eats store pages into one JSON object per store — store identity, ratings, address, hours, full menu (JSON-LD sections + flattened item list), analytics rollups, and review samples — via URL or by city/location discovery.

**Minimal input:**

```json
{
  "startUrls": [{ "url": "https://www.ubereats.com/store/jersey-mikes-subs-creekside/B5-ImzTAQf2En_RCiqU62A" }],
  "maxItems": 20
}
```

Location discovery variant: `{ "address": "Chicago, IL", "maxItems": 20 }` (geocodes the city and scrapes every matching store; requires a residential proxy).

**Output:** one row per store — `url`, `restaurantUrl`, `storeId`, `storeUuid`, `storeSlug`, `title`, `seoTitle`, `description`, `image`, `heroImageUrls[]`, `ratingValue`, `reviewCount`, `phoneNumber`, `address {addressLocality, addressRegion, postalCode, addressCountry, streetAddress}`, `addressText`, `latitude`, `longitude`, `cuisineList[]`, `currencyCode`, `priceBucket`, `isOpen`, `isOrderable`, `hours[]`, `menuSections[]`, `menuItemCount`, `menuItems[] {name, description, section, price, currency, imageUrl}`, `analytics {minPrice, maxPrice, averagePrice, priceCoverage, imageCoverage, ...}`, `reviewSamples[] {eaterName, reviewText, createdAt, contentUUID}`, `etaRange`, `scrapedAt`.

**Behaviors an agent should know:**

- `maxItems` defaults to 10000 (`0` = unlimited); free-tier accounts are capped at 50 stores per run — set an explicit cap.
- Two input paths, usable together: `startUrls` (direct store URLs, incl. `store-browse-uuid` links) and/or `address` location discovery; location discovery requires a residential proxy and pins the exit to the geocoded city.
- In discovery mode, empty `searchQuery` browses every store (all verticals); `storeType` narrows the vertical (best-effort); `diningMode` is `DELIVERY` or `PICKUP`. `matchStoreName: true` returns the first store whose name matches `searchQuery` (one store per run, for looking up a named venue); no match = SUCCEEDED with 0 items.
- `includeItemCustomizations: true` adds large per-item option/add-on trees; `reviewsSince` (ISO date) + `maxReviews` filter/cap reviews (sort by `createdAt` client-side — not guaranteed newest-first; no per-review star rating, aggregate only).
- Pay-per-event billing — see the Pricing tab on the actor page.

## apify-ubereats

***

### ⚠️ Disclaimer

This Actor is an independent tool and is not affiliated with, endorsed by, or sponsored by Uber Technologies, Inc. or any of its subsidiaries. All trademarks mentioned are the property of their respective owners.

The scraper accesses only publicly available Uber Eats store pages — no authenticated endpoints, paid features, or content behind the ubereats.com login wall. Users are responsible for ensuring their use complies with ubereats.com's Terms of Service, applicable data-protection law (GDPR, CCPA, etc.), and any contractual obligations of their own organization.

***

### SEO Keywords

uber eats scraper, scrape uber eats, ubereats.com scraper, uber eats API, Apify uber eats, restaurant menu scraper, food delivery scraper, menu price scraper, restaurant data scraper, store listing scraper, menu price monitoring, restaurant review data, food delivery market research, menu syndication data, restaurant analytics export, cuisine data extraction, local restaurant intelligence, food data aggregation

# Actor input Schema

## `address` (type: `string`):

Discover stores by location instead of pasting store URLs. Enter a city or full address (e.g. "Chicago, IL" or "1407 N Main St, Blacksburg VA"). The actor geocodes it and pulls every matching Uber Eats store, then scrapes each one. Leave empty if you're using Start URLs.

## `searchQuery` (type: `string`):

Optional dish/cuisine keyword, e.g. "pizza", "sushi", "tacos". Leave empty to BROWSE every store at the location (all verticals). With a keyword, results are filtered to that term.

## `matchStoreName` (type: `boolean`):

Use this when the Search keyword is the name of a specific restaurant or shop you want to find (e.g. "La La Land"). Returns the first store whose name matches the keyword (one store per run); Uber Eats search otherwise fills the results with other nearby stores. If no store with that name is on Uber Eats near the address, the run succeeds with no results. Ignored when the Search keyword is empty.

## `storeType` (type: `string`):

Preferred Uber Eats vertical for the location search. ALL is recommended; the narrower verticals are best-effort and Uber may still return mixed results. Only applies to location search.

## `diningMode` (type: `string`):

Delivery or pickup feed for location search.

## `latitude` (type: `string`):

Optional. Provide precise coordinates to skip geocoding (use with longitude). Useful when a city name is ambiguous.

## `longitude` (type: `string`):

Optional. Provide precise coordinates to skip geocoding (use with latitude).

## `locale` (type: `string`):

Optional locale prefix for store URLs with no locale yet (en-US, en-GB, es-MX). Leave empty to keep the site default.

## `addressCountry` (type: `string`):

Optional ISO 3166-1 alpha-2 country (US, GB, MX) to pin Nominatim geocoding when a city name exists in more than one country.

## `excludeStores` (type: `array`):

Skip these store UUIDs or path IDs (the last segment of /store/{slug}/{id}) so a city re-run does not scrape stores you already have.

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

Optional. Public Uber Eats store URLs to scrape directly (https://www.ubereats.com/store/{slug}/{id} or store-browse-uuid links). Use this instead of, or in addition to, location search.

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

Maximum number of listings to scrape per run. Set to 0 for unlimited. Default 10000; free-tier accounts are capped at 50 stores per run regardless.

## `maxConcurrency` (type: `integer`):

Maximum number of pages that can be processed at the same time.

## `minConcurrency` (type: `integer`):

Minimum number of pages that will be processed at the same time.

## `maxRequestRetries` (type: `integer`):

Number of times the crawler will retry a failed request before giving up.

## `includeItemCustomizations` (type: `boolean`):

When enabled, fetches Uber Eats item option trees such as sizes, toppings, add-ons, and nested suboptions from the item detail API. This increases run time and request volume.

## `reviewsSince` (type: `string`):

Optional. Only return reviews with a createdAt timestamp greater than or equal to this ISO date (e.g. 2025-01-01 or 2025-01-01T00:00:00Z). Useful for incremental scheduled runs to avoid re-charging for reviews you already have.

## `maxReviews` (type: `integer`):

Optional cap on the number of reviews returned per store. Applied after reviewsSince filtering. Set to 0 to disable. Uber Eats only shows a small sample of written reviews per store (usually about 10 to 20), and many stores show none at all even with thousands of star ratings. This setting limits that sample; it cannot return more reviews than Uber Eats displays on the store page.

## `enrichEmails` (type: `boolean`):

If enabled, finds a contact email for each result from its own website (or by discovering it from the name). Adds contactEmail + contactWebsite columns plus a detailed emailEnrichment object. Billed per contact email found; only charged when an email is returned, never for misses.

## `detectTech` (type: `boolean`):

If enabled (requires "Enrich with contact emails"), fetches each restaurant's own website and detects its tech: online-ordering / POS (Toast, Square, Clover, ChowNow, Olo…), reservations (OpenTable, Resy, Tock…), site platform, and analytics. Adds a techStack object. Great for sales prospecting.

## `proxy` (type: `object`):

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.

## Actor input object example

```json
{
  "matchStoreName": false,
  "storeType": "ALL",
  "diningMode": "DELIVERY",
  "startUrls": [
    {
      "url": "https://www.ubereats.com/mx-en/store/whataburger-1120-n-velasco-st/tn7g8jrHWleakVN6nVbaIw?diningMode=PICKUP&pl=JTdCJTIyYWRkcmVzcyUyMiUzQSUyMjE0MDclMjBOJTIwTWFpbiUyMFN0JTIyJTJDJTIycmVmZXJlbmNlJTIyJTNBJTIyaGVyZSUzQWFmJTNBc3RyZWV0c2VjdGlvbiUzQWlRanFNei05UUYwT1FIYXRmOU9qR0QlM0FDZ2dJQkNDZDc3YXVBeEFCR2dReE5EQTMlMjIlMkMlMjJyZWZlcmVuY2VUeXBlJTIyJTNBJTIyaGVyZV9wbGFjZXMlMjIlMkMlMjJsYXRpdHVkZSUyMiUzQTM3LjI0NzUzJTJDJTIybG9uZ2l0dWRlJTIyJTNBLTgwLjQxNTEyJTdE&surfaceName="
    }
  ],
  "maxItems": 10000,
  "maxConcurrency": 100,
  "minConcurrency": 1,
  "maxRequestRetries": 100,
  "includeItemCustomizations": false,
  "enrichEmails": false,
  "detectTech": false
}
```

# Actor output Schema

## `results` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "startUrls": [
        {
            "url": "https://www.ubereats.com/mx-en/store/whataburger-1120-n-velasco-st/tn7g8jrHWleakVN6nVbaIw?diningMode=PICKUP&pl=JTdCJTIyYWRkcmVzcyUyMiUzQSUyMjE0MDclMjBOJTIwTWFpbiUyMFN0JTIyJTJDJTIycmVmZXJlbmNlJTIyJTNBJTIyaGVyZSUzQWFmJTNBc3RyZWV0c2VjdGlvbiUzQWlRanFNei05UUYwT1FIYXRmOU9qR0QlM0FDZ2dJQkNDZDc3YXVBeEFCR2dReE5EQTMlMjIlMkMlMjJyZWZlcmVuY2VUeXBlJTIyJTNBJTIyaGVyZV9wbGFjZXMlMjIlMkMlMjJsYXRpdHVkZSUyMiUzQTM3LjI0NzUzJTJDJTIybG9uZ2l0dWRlJTIyJTNBLTgwLjQxNTEyJTdE&surfaceName="
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("memo23/uber-eats-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 = { "startUrls": [{ "url": "https://www.ubereats.com/mx-en/store/whataburger-1120-n-velasco-st/tn7g8jrHWleakVN6nVbaIw?diningMode=PICKUP&pl=JTdCJTIyYWRkcmVzcyUyMiUzQSUyMjE0MDclMjBOJTIwTWFpbiUyMFN0JTIyJTJDJTIycmVmZXJlbmNlJTIyJTNBJTIyaGVyZSUzQWFmJTNBc3RyZWV0c2VjdGlvbiUzQWlRanFNei05UUYwT1FIYXRmOU9qR0QlM0FDZ2dJQkNDZDc3YXVBeEFCR2dReE5EQTMlMjIlMkMlMjJyZWZlcmVuY2VUeXBlJTIyJTNBJTIyaGVyZV9wbGFjZXMlMjIlMkMlMjJsYXRpdHVkZSUyMiUzQTM3LjI0NzUzJTJDJTIybG9uZ2l0dWRlJTIyJTNBLTgwLjQxNTEyJTdE&surfaceName=" }] }

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

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

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

```

## CLI example

```bash
echo '{
  "startUrls": [
    {
      "url": "https://www.ubereats.com/mx-en/store/whataburger-1120-n-velasco-st/tn7g8jrHWleakVN6nVbaIw?diningMode=PICKUP&pl=JTdCJTIyYWRkcmVzcyUyMiUzQSUyMjE0MDclMjBOJTIwTWFpbiUyMFN0JTIyJTJDJTIycmVmZXJlbmNlJTIyJTNBJTIyaGVyZSUzQWFmJTNBc3RyZWV0c2VjdGlvbiUzQWlRanFNei05UUYwT1FIYXRmOU9qR0QlM0FDZ2dJQkNDZDc3YXVBeEFCR2dReE5EQTMlMjIlMkMlMjJyZWZlcmVuY2VUeXBlJTIyJTNBJTIyaGVyZV9wbGFjZXMlMjIlMkMlMjJsYXRpdHVkZSUyMiUzQTM3LjI0NzUzJTJDJTIybG9uZ2l0dWRlJTIyJTNBLTgwLjQxNTEyJTdE&surfaceName="
    }
  ]
}' |
apify call memo23/uber-eats-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,memo23/uber-eats-scraper"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/eLbbFqzciSzbfzW2D/builds/u1EbU9mFADbIoMIPG/openapi.json
