# Uber Eats Restaurant Scraper (`yasmany.casanova/uber-eats-restaurant-scraper`) Actor

Scrape Uber Eats restaurant data at scale. Search restaurants by location, then pull store profiles with ratings and complete menus with prices. One row per record, ready for price monitoring and competitor analysis.

- **URL**: https://apify.com/yasmany.casanova/uber-eats-restaurant-scraper.md
- **Developed by:** [Yasmany Grijalba Casanova](https://apify.com/yasmany.casanova) (community)
- **Categories:** E-commerce, Automation, Developer tools
- **Stats:** 15 total users, 0 monthly users, 100.0% runs succeeded, 2 bookmarks
- **User rating**: 5.00 out of 5 stars

## Pricing

from $19.90 / 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

## Uber Eats Restaurant Scraper

Extract **restaurants, menus, and store details** from Uber Eats across the United States: with ratings, delivery fees, ETAs, menu prices, and more.

[![Apify Actor](https://img.shields.io/badge/Apify-Actor-blue?logo=apify)](https://apify.com)
[![No Proxy Needed](https://img.shields.io/badge/Proxy-Not%20Required-green)](https://apify.com/proxy)
[![United States](https://img.shields.io/badge/Region-United%20States-green)](https://www.ubereats.com)

***

### Quick Start

#### Search restaurants near Miami

```json
{
  "mode": "stores",
  "query": "Pizza",
  "latitude": "25.7617",
  "longitude": "-80.1918",
  "max_results": 100
}
```

Each row of that run carries a `store_id`. Feed it to the other two modes.

#### Get restaurant details

```json
{
  "mode": "store_info",
  "store_id": "c13edd5e-4ad7-4a86-80f3-a8fc6f114a17"
}
```

#### Extract the full menu

```json
{
  "mode": "menu",
  "store_id": "c13edd5e-4ad7-4a86-80f3-a8fc6f114a17"
}
```

> **Use `store_id`, not the `url`, to chain a `stores` run into the other modes.** The
> `url` in a search result uses a short identifier that these modes cannot resolve, while
> `store_id` from the same row always works. Pasting a restaurant's address bar from
> ubereats.com into `store_url` works too, because those URLs end in the UUID.

> **Tip**: Use `categories` mode first to discover available food types in your target location before searching for stores.

***

### Features

| Feature | Description |
|---------|-------------|
| **Search Restaurants** | Find hundreds of restaurants by query and location with full pagination |
| **Complete Menu Data** | Full menus with prices, descriptions, availability, and Uber Eats' Popular flag |
| **Detailed Store Profiles** | Address, ratings, delivery info, operating hours, and phone |
| **Real-Time Data** | Fresh data directly from Uber Eats platform in structured JSON |

***

### Use Cases

- **Market Research**: Analyze restaurant offerings and pricing across different US locations to understand market trends
- **Price Monitoring**: Track menu prices and delivery fees over time to stay competitive or find the best deals
- **Competitor Analysis**: Compare restaurants by ratings, delivery times, fees, and menu offerings
- **Lead Generation**: Build targeted lists of restaurants in specific areas for sales and marketing outreach

***

### Input Parameters

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `mode` | string | none | **Required.** Operation mode: `store_info`, `menu`, `stores`, or `categories` |
| `store_url` | string | none | Full Uber Eats store URL. Required for `store_info` and `menu` modes |
| `store_id` | string | none | Store UUID (alternative to `store_url`). Format: `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx` |
| `query` | string | none | Search term (e.g., "Pizza", "Sushi"). Required for `stores` mode |
| `latitude` | string | `"25.7617"` | Location latitude. Required for `stores` and `categories` modes |
| `longitude` | string | `"-80.1918"` | Location longitude. Required for `stores` and `categories` modes |
| `max_results` | integer | `1000` | Maximum restaurants to extract in `stores` mode (up to 5000) |
| `request_waiting` | integer | `5` | Delay between requests in seconds (minimum: 3) |
| `useApifyProxy` | boolean | `false` | Enable Apify Proxy. Off by default; see the note below |
| `proxyGroups` | array | `["RESIDENTIAL"]` | Proxy group, used only when `useApifyProxy` is on |
| `proxyCountry` | string | `"US"` | Proxy country code, used only when `useApifyProxy` is on |

> **No proxy needed, and turning one on will break the run.** Uber Eats answers the
> Apify platform's own address directly, so the default settings work as they are and
> you pay for no proxy traffic. Requests routed through Apify's residential pool came
> back `403` on all four modes when measured, so `useApifyProxy` ships off. Turn it on
> only if you have a proxy Uber Eats accepts.
>
> A run whose proxy cannot be configured now stops with a message instead of quietly
> continuing unproxied.

***

### Output Examples

The dataset holds **one item per record**: one per menu item, one per restaurant found,
one per store profile. Every row repeats the run context (`mode` and `scraped_at`) so an
exported CSV or Excel file stands on its own.

> **Upgrading from v1.0:** `menu` and `stores` used to write a single item with every
> record nested under `data`. Code reading `items[0].data[...]` gets nothing now and
> should iterate the dataset directly. Field names are unchanged, and `store_info` and
> `categories` are not affected.

A run that reaches Uber Eats but finds nothing writes one row with `status: "NO_RESULTS"`
and a message saying why, so a run is never silently empty. When data cannot be retrieved
at all, the run **fails** with a message instead of finishing green.

#### Stores Mode

One row per restaurant. `latitude` and `longitude` are the restaurant's own; the
coordinates you searched with come back as `search_latitude` and `search_longitude`.

**`delivery_fee` is `null` unless the restaurant shows a fee badge**, which was 1 of 60
restaurants measured. Null means Uber Eats published no fee for that listing, not free
delivery. `delivery_fee_text` carries the badge as written, because the fee is often
conditional: "$0 Delivery Fee on $10+" is not the same offer as "$0 Delivery Fee".

```json
{
  "mode": "stores",
  "scraped_at": "2026-09-09T03:59:26.023621+00:00",
  "query": "pizza",
  "search_latitude": "25.7617",
  "search_longitude": "-80.1918",
  "store_id": "efcba862-6a16-48d3-8078-dd48b36f9bd1",
  "name": "Dimelo Pizzeria",
  "slug": "dimelo-pizzeria",
  "rating": 4.7,
  "review_count": "150+",
  "eta_min": 43,
  "eta_max": 63,
  "delivery_fee": 0,
  "delivery_fee_text": "$0 Delivery Fee on $10+",
  "image_url": "https://tb-static.uber.com/prod/image-proc/processed_images/f011b236ceb047f92f1f1b0432f56c0b/3ac2b39ad528f8c8c5dc77c59abb683d.jpeg",
  "is_orderable": true,
  "latitude": 25.8913,
  "longitude": -80.3555,
  "url": "https://www.ubereats.com/store/dimelo-pizzeria/78uoYmoWSNOAeN1Is2-b0Q"
}
```

#### Store Info Mode

A single row. `hours` and `location` are nested objects, trimmed here.

```json
{
  "mode": "store_info",
  "scraped_at": "2026-09-09T03:46:36.639254+00:00",
  "name": "MCDONALDS (PALMETTO BAY)",
  "store_id": "c13edd5e-4ad7-4a86-80f3-a8fc6f114a17",
  "slug": "mcdonalds-palmetto-bays",
  "rating": {
    "ratingValue": 4.5,
    "reviewCount": "7000+"
  },
  "location": {
    "address": "18295 S Dixie Hwy, PERRINE, FL 33157",
    "streetAddress": "18295 S DIXIE HWY",
    "city": "PERRINE",
    "country": "US",
    "postalCode": "33157",
    "region": "FL",
    "latitude": 25.6008566,
    "longitude": -80.352438,
    "geo": {
      "city": "palmetto-bay-fl",
      "country": "us",
      "neighborhood": "perrine-palmetto-bay-fl",
      "region": "fl"
    },
    "locationType": "DEFAULT"
  },
  "hours": [
    {
      "dayRange": "Sunday",
      "sectionHours": [
        {
          "startTime": 240,
          "endTime": 659,
          "sectionTitle": "Breakfast"
        }
      ]
    }
  ],
  "phone": "+13052387129"
}
```

#### Menu Mode

One row per item. This one is flagged Popular by Uber Eats.

```json
{
  "mode": "menu",
  "scraped_at": "2026-09-09T03:53:23.240178+00:00",
  "name": "Medium French Fries",
  "description": "",
  "product_id": "dd1daf3c-e93e-5030-965d-fe67e67077c9",
  "category": "FEATURED ITEMS",
  "store_id": "c13edd5e-4ad7-4a86-80f3-a8fc6f114a17",
  "available": "Y",
  "has_customizations": "Y",
  "price_to": 4.29,
  "price_from": null,
  "discount": null,
  "endorsement": "Y",
  "image": "https://tb-static.uber.com/prod/image-proc/processed_images/c821c5db2718d95dd1fd68002987ec27/c67fc65e9b4e16a553eb7574fba090f1.jpeg",
  "created_at": "2026-09-08",
  "hour": "23:53:23"
}
```

**`price_from` and `discount` are `null` unless the item is on promotion**, and promotions
are rare: zero of 1,320 items measured across two stores carried one. A `price_to` of `0`
is a genuinely free item, such as a condiment packet, not a missing price.

#### Categories Mode

A single row carrying the category list.

```json
{
  "mode": "categories",
  "scraped_at": "2026-09-09T03:46:49.066376+00:00",
  "latitude": "25.7617",
  "longitude": "-80.1918",
  "total": 19,
  "categories": [
    {
      "title": "Pizza",
      "titleTerm": "Pizza",
      "type": "restaurant_category"
    },
    {
      "title": "Sushi",
      "titleTerm": "Sushi",
      "type": "restaurant_category"
    },
    {
      "title": "Tacos",
      "titleTerm": "Tacos",
      "type": "restaurant_category"
    }
  ]
}
```

***

### City Coordinates

| City | Latitude | Longitude |
|------|----------|-----------|
| Miami, FL | 25.7617 | -80.1918 |
| New York, NY | 40.7128 | -74.0060 |
| Los Angeles, CA | 34.0522 | -118.2437 |
| Chicago, IL | 41.8781 | -87.6298 |
| Houston, TX | 29.7604 | -95.3698 |
| San Francisco, CA | 37.7749 | -122.4194 |

> **Get coordinates**: Open [Google Maps](https://maps.google.com), right-click any US location, and copy the coordinates. First number = latitude, second = longitude.

***

### FAQ

<details>
<summary><strong>Why am I getting no results?</strong></summary>

- Verify your coordinates are within the United States
- Check that your search query matches available restaurants
- Leave `useApifyProxy` off, which is the default. A residential proxy returns 403 here
- Try increasing `request_waiting` to 8-10 seconds during peak hours

</details>

<details>
<summary><strong>Can I scrape restaurants outside the US?</strong></summary>

This actor is configured specifically for the US market. International markets have different API structures and require separate implementations.

</details>

<details>
<summary><strong>What is the maximum number of restaurants I can extract?</strong></summary>

Up to 5,000 restaurants per run using the `max_results` parameter. Pagination is handled automatically at 80 stores per page. For larger datasets, run the actor multiple times with different locations or queries.

</details>

***

### Support

1. **Check the FAQ** above for common solutions
2. **Test with default values** using the Miami coordinates example
3. **Contact support** through [Apify](https://apify.com) for additional assistance

# Actor input Schema

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

Type of data to extract from Uber Eats

## `store_url` (type: `string`):

Complete Uber Eats store URL (for store_info and menu modes). Copy it directly from the Uber Eats website.

## `store_id` (type: `string`):

Uber Eats store UUID - alternative to store_url. Get this ID from stores mode first.

## `query` (type: `string`):

Search query or category (e.g., 'Pizza', 'McDonald's', 'Sushi'). Required for stores mode.

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

Location latitude in the United States. Use Google Maps to find coordinates: right-click any location and copy the first number.

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

Location longitude in the United States. Use Google Maps to find coordinates: right-click any location and copy the second number.

## `max_results` (type: `integer`):

Maximum number of stores to extract (for stores mode). Pagination is handled automatically (80 stores per page).

## `request_waiting` (type: `integer`):

Delay between requests to avoid rate limiting. Minimum 3 seconds.

## `useApifyProxy` (type: `boolean`):

Off by default, and off is what works. Uber Eats answers the Apify platform's own address directly, while requests from the residential pool come back 403 in all four modes. Turn this on only if you have a proxy group that Uber Eats accepts.

## `proxyGroups` (type: `array`):

Apify Proxy groups, used only when 'Use Apify Proxy' is on. RESIDENTIAL was measured returning 403 on every mode.

## `proxyCountry` (type: `string`):

Country code, used only when 'Use Apify Proxy' is on.

## Actor input object example

```json
{
  "mode": "stores",
  "store_url": "https://www.ubereats.com/store/mcdonalds-palmetto-bay/c13edd5e-4ad7-4a86-80f3-a8fc6f114a17",
  "store_id": "c13edd5e-4ad7-4a86-80f3-a8fc6f114a17",
  "query": "Pizza",
  "latitude": "25.7617",
  "longitude": "-80.1918",
  "max_results": 500,
  "request_waiting": 5,
  "useApifyProxy": true,
  "proxyGroups": [
    "RESIDENTIAL"
  ],
  "proxyCountry": "US"
}
```

# Actor output Schema

## `categories_output` (type: `string`):

Food categories available at the given location

## `stores_output` (type: `string`):

One row per restaurant matching the search

## `store_info_output` (type: `string`):

Detailed information about one restaurant

## `menu_output` (type: `string`):

One row per menu item, with prices

# 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 = {
    "useApifyProxy": false
};

// Run the Actor and wait for it to finish
const run = await client.actor("yasmany.casanova/uber-eats-restaurant-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 = { "useApifyProxy": False }

# Run the Actor and wait for it to finish
run = client.actor("yasmany.casanova/uber-eats-restaurant-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 '{
  "useApifyProxy": false
}' |
apify call yasmany.casanova/uber-eats-restaurant-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,yasmany.casanova/uber-eats-restaurant-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/TzMsZ218Tnyjyj865/builds/dPMFYiANAvTjuU2Nd/openapi.json
