# iFood Restaurant Scraper (`yasmany.casanova/ifood-scraper`) Actor

Extract the store profile, full menu with prices, and customer reviews for any iFood Brazil restaurant, from its URL. Clean, structured JSON, one row per record.

- **URL**: https://apify.com/yasmany.casanova/ifood-scraper.md
- **Developed by:** [Yasmany Grijalba Casanova](https://apify.com/yasmany.casanova) (community)
- **Categories:** E-commerce, Developer tools, Automation
- **Stats:** 264 total users, 23 monthly users, 95.8% runs succeeded, 9 bookmarks
- **User rating**: 5.00 out of 5 stars

## Pricing

$25.00 / 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?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## iFood Restaurant Scraper

Extract **restaurant profiles, menus with prices, and customer reviews** from iFood, the largest food delivery platform in Latin America.

[![Apify Actor](https://img.shields.io/badge/Apify-Actor-blue?logo=apify)](https://apify.com)
[![Residential Proxy](https://img.shields.io/badge/Proxy-Residential-orange)](https://apify.com/proxy)
[![Brazil](https://img.shields.io/badge/Region-Brazil-green)](https://www.ifood.com.br)

***

> ℹ️ **Residential Proxy Required**
> This Actor requires a residential proxy for reliable data extraction. You have two options:
>
> 1. **Apify Residential Proxy**, available on Apify paid plans. See [apify.com/pricing](https://apify.com/pricing).
> 2. **Your own residential proxy**, provided via the `customProxyUrl` input field (works on any Apify plan, including Free).
>
> Runs without a residential proxy will stop immediately with a clear message, so you are not charged for unnecessary compute time.

***

### Quick Start

Every run works from a restaurant's `store_id`, and nothing else. It is the last segment of the restaurant's iFood URL:

```
https://www.ifood.com.br/delivery/sao-paulo-sp/pizza-hut-paraiso/f1b60ced-d490-499f-976b-6d3d717d927e
                                                                 └──────────── store_id ────────────┘
```

#### Extract a restaurant menu

```json
{
  "mode": "menu",
  "store_id": "f1b60ced-d490-499f-976b-6d3d717d927e"
}
```

#### Get restaurant details

```json
{
  "mode": "store_info",
  "store_id": "f1b60ced-d490-499f-976b-6d3d717d927e"
}
```

#### Get customer reviews

```json
{
  "mode": "reviews",
  "store_id": "f1b60ced-d490-499f-976b-6d3d717d927e",
  "maxReviews": 100
}
```

> **Tip**: to get the delivery fee and delivery time to a specific address, add `latitude` and `longitude`. See [Delivery pricing](#delivery-pricing).

***

### Features

| Feature | Description |
|---------|-------------|
| **Complete Menu Data** | Full menus with list prices, promotional prices, descriptions and images |
| **Detailed Store Profiles** | Address, CNPJ, ratings, delivery terms and opening hours by day |
| **Customer Reviews** | Reviews with ratings, comments and order dates, up to 1000 per run |
| **Chain and Pix** | The chain a restaurant belongs to, and whether it accepts Pix |
| **Real-Time Data** | Prices, availability and hours read live at the moment of the run |
| **Structured JSON Output** | Clean, validated data ready for analysis |

***

### Use Cases

- **Price monitoring**: track menu prices and promotions over time
- **Competitor analysis**: compare ratings, delivery times, fees and offerings
- **Market research**: analyze pricing and assortment by restaurant or chain
- **Reputation monitoring**: follow customer reviews and sentiment
- **Menu intelligence**: measure category depth, item counts and discount depth

***

### Input Parameters

Two inputs run the Actor. The rest apply to one mode each, and the input form
groups them that way.

| Parameter | Applies to | Description |
|-----------|-----------|-------------|
| `mode` | all | `store_info`, `menu` or `reviews` |
| `store_id` | all | **Required.** Restaurant UUID, the last segment of its iFood URL |
| `maxReviews` | `reviews` | Reviews to fetch, 1-1000. Default: `100` |
| `latitude` | `store_info` | Delivery address, to price delivery to it. See below |
| `longitude` | `store_info` | The other half of that address |
| `useApifyProxy` | all | Enable Apify Proxy. Default: `true` |
| `proxyGroups` | all | Proxy group for Apify Proxy. Default: `["RESIDENTIAL"]` |
| `proxyCountry` | all | Proxy country code. Default: `BR` |
| `customProxyUrl` | all | Your own residential proxy; overrides the three settings above |
| `maxRetries` | all | Retry attempts for failed requests (1-10). Default: `3` |
| `timeout` | all | Request timeout in seconds. Default: `30` |

#### Delivery pricing

`store_info` answers "what does this restaurant charge to deliver here?" only if
you say where *here* is. Add `latitude` and `longitude` and five fields fill in
for that address:

| With an address | Without |
|-----------------|---------|
| `distance` 1.5 | `null` |
| `delivery_fee` 8.99 | `null` |
| `type_delivery_fee` FIXED | `null` |
| `delivery_time` 30 | `null` |
| `has_order_tracking` true | `null` |

Measured on the same restaurant, same minute. The other 31 fields of
`store_info` are identical either way, and `menu` and `reviews` ignore the
coordinates entirely, so leave them out unless you want delivery terms.

***

### Output Examples

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

A run that reaches iFood but finds nothing writes a single 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.

#### Store Info

Truncated here where the real record is long.

```json
{
  "mode": "store_info",
  "store_id": "f1b60ced-d490-499f-976b-6d3d717d927e",
  "name": "Pizza Hut - Paraíso",
  "description": "PIMENTA VERDE ALIMENTOS LTDA.",
  "cnpj": "09.060.964/0131-88",
  "main_category": "Pizza",
  "cuisine_categories": ["Comida Italiana"],
  "chain_name": "Pizza Hut",
  "street_name": "AV BRIGADEIRO LUIS ANTONIO",
  "street_number": "2893",
  "district": "JARDIM PAULISTA",
  "city": "SAO PAULO",
  "state": "SP",
  "zip_code": "01401000",
  "latitude": "-23.571795",
  "longitude": "-46.652905",
  "user_rating": 4.7,
  "user_rating_count": 266,
  "price_range": "CHEAPEST",
  "opening_hours_text": "Mon 10:00-22:30; Tue 15:00-22:30; Wed 10:00-23:59; ...",
  "opening_hours": [
    { "day_of_week": "MONDAY", "opens_at": "10:00", "closes_at": "22:30" }
  ],
  "available": true,
  "has_delivery": true,
  "has_takeout": true,
  "delivery_fee": 6.99,
  "delivery_time": 21,
  "preparation_time": 14,
  "minimum_order_value": 30,
  "accepts_pix": true
}
```

#### Menu

One row per item, carrying its section. This one is on promotion.

```json
{
  "mode": "menu",
  "store_id": "f1b60ced-d490-499f-976b-6d3d717d927e",
  "category_code": "f297e196-e5cb-4ed3-8817-71ccbb0a2d71",
  "category_name": "Sobremesas",
  "id": "99acbd4a-729e-4d05-b911-1e21f42c1909",
  "code": "99acbd4a-729e-4d05-b911-1e21f42c1909",
  "description": "Slider Nutella",
  "details": "Mini disco de 8,5cm de massa pan coberta com Nutella",
  "logo_url": "https://static-images.ifood.com.br/image/upload/...",
  "unit_price": 15.9,
  "promotional_price": 10.34,
  "availability": "AVAILABLE",
  "external_code": "91559",
  "need_choices": false,
  "product_tags": [{ "group": "PORTION_SIZE", "tags": ["NOT_APPLICABLE"] }]
}
```

> **Read `promotional_price` before comparing prices.** About one item in five is discounted at any time, and the discount reaches 40%. `unit_price` is always the list price.

#### Reviews

```json
{
  "mode": "reviews",
  "store_id": "f1b60ced-d490-499f-976b-6d3d717d927e",
  "total_reviews_available": 273,
  "total_ratings": 11135,
  "customer_name": "Maximo",
  "rating": 4.0,
  "grade": 4.0,
  "comment": "Comprei Grande, mas, tamanho menor, não é grande",
  "comment_date": "2026-01-09T23:36:55.725000+00:00",
  "order_date": "2026-01-09T22:41:29.799000+00:00",
  "order_number": 3635,
  "status": "ACE",
  "moderated": false
}
```

***

### Data Fields

Every row also carries `mode` and `scraped_at`.

**Null is not zero.** A number is `null` when iFood states no value and `0` only when it really says zero, which keeps free delivery apart from an unpublished fee, and an unrated restaurant apart from one rated zero stars.

<details>
<summary><strong>Store Info Mode, 36 fields</strong></summary>

**Identity**

| Field | Type | Description |
|-------|------|-------------|
| `store_id` | string | iFood identifier for the restaurant |
| `name` | string | Restaurant name |
| `description` | string | Description written by the restaurant |
| `cnpj` | string | Company tax id, in the standard Brazilian format |
| `main_category` | string | Primary food category |
| `cuisine_categories` | array | Cuisine types, such as Comida Italiana |
| `store_type` | string | Merchant type code |
| `chain_name` | string | Chain the restaurant belongs to, empty if independent |
| `logo` | string | Restaurant logo URL |

**Location**

| Field | Type | Description |
|-------|------|-------------|
| `street_name` | string | Street name |
| `street_number` | string | Street number |
| `district` | string | Neighborhood or district |
| `city` | string | City name |
| `state` | string | State abbreviation |
| `zip_code` | string | Postal code |
| `country` | string | Country code |
| `latitude` | string | The restaurant's own latitude |
| `longitude` | string | The restaurant's own longitude |
| `distance` | number | Km from the coordinates you sent, `null` when you sent none |

**Reputation and hours**

| Field | Type | Description |
|-------|------|-------------|
| `user_rating` | number | Average rating, 0 to 5 stars |
| `user_rating_count` | number | Total number of ratings |
| `price_range` | string | iFood's price band for the restaurant |
| `opening_hours_text` | string | The full week on one line, for spreadsheets |
| `opening_hours` | array | One entry per shift: day, opening time, closing time |

A restaurant open past midnight has a closing time smaller than its opening time, and a day with a lunch and a dinner shift has two entries.

**Service and delivery**

| Field | Type | Description |
|-------|------|-------------|
| `available` | boolean | Whether the restaurant is open right now |
| `has_delivery` | boolean | Delivers to the customer |
| `has_takeout` | boolean | Offers takeaway |
| `has_scheduling` | boolean | Accepts scheduled orders |
| `has_order_tracking` | boolean | Offers live order tracking to that address |
| `delivery_fee` | number | Delivery fee in BRL to that address |
| `type_delivery_fee` | string | How the delivery fee is charged |
| `delivery_time` | number | Estimated delivery time to that address, in minutes |
| `takeout_time` | number | Takeaway estimate, in minutes |
| `preparation_time` | number | Kitchen preparation estimate, in minutes |
| `minimum_order_value` | number | Minimum order amount in BRL |
| `accepts_pix` | boolean | Whether Pix is accepted |

`distance`, `delivery_fee`, `type_delivery_fee`, `delivery_time` and `has_order_tracking` all describe delivery to the address you supplied in `latitude` and `longitude`. Run without those and all five are `null`, because nobody asked the question. The other 31 fields are identical either way.

iFood also lists 30 or more payment codes, most of them internal and undocumented, so they are not published rather than shipped as codes nobody outside iFood can read. The merchant phone number is deliberately left out too: on smaller restaurants it is the owner's personal mobile.

</details>

<details>
<summary><strong>Menu Mode, 16 fields</strong></summary>

| Field | Type | Description |
|-------|------|-------------|
| `store_id` | string | Restaurant the item belongs to |
| `category_code` | string | Identifier of the section the item belongs to |
| `category_name` | string | Name of that section |
| `id` | string | Item identifier |
| `code` | string | Item code used by iFood |
| `external_code` | string | The restaurant's own code for the item |
| `description` | string | Item name |
| `details` | string | Item description |
| `logo_url` | string | Item image URL |
| `unit_price` | number | List price in BRL |
| `promotional_price` | number | Discounted price when the item is on promotion, null otherwise |
| `unit_original_price` | number | Price before discount, when the item is discounted |
| `unit_min_price` | number | Lowest price among the item's options |
| `availability` | string | Whether the item is currently available |
| `need_choices` | boolean | Whether the item requires choosing options |
| `product_tags` | array | Tags iFood applies to the product |

The options behind `need_choices`, such as sizes and extras, are not included: the catalog lists items without them, so returning them would mean one extra request per item.

</details>

<details>
<summary><strong>Reviews Mode, 12 fields</strong></summary>

| Field | Type | Description |
|-------|------|-------------|
| `store_id` | string | Restaurant the review belongs to |
| `total_reviews_available` | number | How many evaluations can be retrieved, written comment or not |
| `total_ratings` | number | Every order this restaurant has had rated. The reputation volume |
| `customer_name` | string | Customer's first name |
| `rating` | number | Rating, 1 to 5 stars |
| `grade` | number | The same score as `rating`; iFood publishes both |
| `comment` | string | Customer comment. Most evaluations are a rating with no text |
| `comment_date` | string | When the review was posted (ISO 8601) |
| `order_date` | string | When the order was placed (ISO 8601) |
| `order_number` | number | Order reference number |
| `status` | string | Review status |
| `moderated` | boolean | Whether review was moderated |

Reviews carry first names only, as iFood publishes them.

</details>

***

### Integrations

Use the **API** tab on the Actor page to get ready-to-use code examples in **Python**, **JavaScript**, and **cURL**. The examples are auto-generated with the correct parameters for immediate integration.

***

### Coverage

This Actor covers **restaurants** on iFood, anywhere in Brazil: profiles, menus and reviews for a restaurant you name.

iFood's grocery and pharmacy storefronts are not part of this Actor. Their catalogs are far larger, change on a different schedule and are served through a separate part of the platform, so they are offered as a managed data feed instead. For supermarket or pharmacy assortment and pricing, see [datamarket.cnovatech.com.br](https://datamarket.cnovatech.com.br) or write to contato@cnovatech.com.br.

***

### FAQ

<details>
<summary><strong>Where do I find the store_id?</strong></summary>

Open the restaurant on ifood.com.br and look at the address bar. The URL ends in a UUID, and that UUID is the `store_id`: `ifood.com.br/delivery/<city>/<restaurant-name>/<store_id>`.

Copy only the UUID, not the restaurant name or the rest of the URL.

</details>

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

Check that the `store_id` is current and that your coordinates fall inside the restaurant's delivery area. Coordinates from the restaurant's own city are a good control.

A run that found nothing writes one row with `status: "NO_RESULTS"` and a message explaining why, so the dataset is never simply empty.

</details>

<details>
<summary><strong>Why is the menu empty?</strong></summary>

Menus are served while a store is open, so try again during its opening hours. Some restaurants also keep incomplete catalogs on iFood.

</details>

<details>
<summary><strong>Why do I get fewer reviews than the restaurant's rating count?</strong></summary>

Because they count different things. `total_ratings` is every order that was rated; `total_reviews_available` is the smaller set iFood serves back, and most of those carry a star rating with no text. One store measured had 11,135 rated orders behind 273 retrievable evaluations. Retrieving a few hundred reviews for a restaurant with thousands of orders is expected, not a failure.

</details>

<details>
<summary><strong>Why am I getting timeout errors?</strong></summary>

Ensure a residential proxy is configured, raise `timeout` to 60, and bear in mind that iFood is slower during peak hours (lunch and dinner).

</details>

***

### Support

Check the FAQ above first. For anything else, use the **Issues** tab on the Actor page and we will get back to you.

# Actor input Schema

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

Type of data to extract: store\_info (restaurant details), menu (complete menu with prices), or reviews.

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

Restaurant UUID, required for every mode. Open the restaurant on ifood.com.br and copy the last segment of its URL: in https://www.ifood.com.br/delivery/sao-paulo-sp/some-restaurant/f1b60ced-d490-499f-976b-6d3d717d927e the ID is f1b60ced-d490-499f-976b-6d3d717d927e.

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

Maximum number of reviews to fetch (reviews mode only). Default: 100, Max: 1000.

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

Latitude of the delivery address, e.g. "-23.5608786". Give both coordinates or neither.

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

Longitude of the delivery address, e.g. "-46.6570743". Give both coordinates or neither.

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

Enable Apify Residential Proxy for reliable data extraction. Requires an Apify paid plan. Free-plan users can provide their own residential proxy via 'customProxyUrl' below.

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

Proxy group for Apify Proxy. \['RESIDENTIAL'] is what every mode uses.

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

Country code for the proxy (ISO 3166-1 alpha-2). BR is recommended.

## `customProxyUrl` (type: `string`):

Your own proxy, in the form http://user:pass@host:port. Overrides the Apify Proxy settings above. Optional: it simply replaces Apify Proxy and works on any plan, including Free.

## `maxRetries` (type: `integer`):

Maximum retry attempts for failed requests.

## `timeout` (type: `integer`):

Request timeout in seconds.

## Actor input object example

```json
{
  "mode": "menu",
  "store_id": "f1b60ced-d490-499f-976b-6d3d717d927e",
  "maxReviews": 100,
  "useApifyProxy": true,
  "proxyGroups": [
    "RESIDENTIAL"
  ],
  "proxyCountry": "BR",
  "customProxyUrl": "http://user:pass@proxy.example.com:8000/",
  "maxRetries": 3,
  "timeout": 30
}
```

# Actor output Schema

## `restaurants` (type: `string`):

All extracted records with every field, whatever mode the run used

## `storeInfoView` (type: `string`):

View of detailed store information (store\_info mode)

## `menuView` (type: `string`):

View of menu categories and items (menu mode)

## `reviewsView` (type: `string`):

View of customer reviews and ratings (reviews mode)

# 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 = {
    "mode": "menu",
    "store_id": "f1b60ced-d490-499f-976b-6d3d717d927e",
    "maxReviews": 100,
    "useApifyProxy": true,
    "proxyGroups": [
        "RESIDENTIAL"
    ],
    "proxyCountry": "BR",
    "maxRetries": 3,
    "timeout": 30
};

// Run the Actor and wait for it to finish
const run = await client.actor("yasmany.casanova/ifood-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 = {
    "mode": "menu",
    "store_id": "f1b60ced-d490-499f-976b-6d3d717d927e",
    "maxReviews": 100,
    "useApifyProxy": True,
    "proxyGroups": ["RESIDENTIAL"],
    "proxyCountry": "BR",
    "maxRetries": 3,
    "timeout": 30,
}

# Run the Actor and wait for it to finish
run = client.actor("yasmany.casanova/ifood-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 '{
  "mode": "menu",
  "store_id": "f1b60ced-d490-499f-976b-6d3d717d927e",
  "maxReviews": 100,
  "useApifyProxy": true,
  "proxyGroups": [
    "RESIDENTIAL"
  ],
  "proxyCountry": "BR",
  "maxRetries": 3,
  "timeout": 30
}' |
apify call yasmany.casanova/ifood-scraper --silent --output-dataset

```

## MCP server setup

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