# TheFork Scraper - Restaurants, Menus & Reviews (`parsebird/thefork-scraper`) Actor

Extract restaurant data from TheFork: ratings, menus with prices, reviews, photos, cuisine tags, and booking info. Search any city or scrape specific restaurant URLs.

- **URL**: https://apify.com/parsebird/thefork-scraper.md
- **Developed by:** [ParseBird](https://apify.com/parsebird) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 1 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.39 / 1,000 restaurants

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

### TheFork Scraper - Restaurants, Menus & Reviews

Extract restaurant data from [TheFork](https://www.thefork.com/) (Europe's largest restaurant booking platform, also known as LaFourchette and El Tenedor) without touching a browser or writing a scraper yourself. Search any city and filter by cuisine, price, or rating, or feed in specific restaurant URLs to get full profiles: ratings, menus with prices, opening hours, photos, and customer reviews.

<table><tr>
<td style="border-left:4px solid #1C1917;padding:12px 16px;font-weight:600">
Search any city on TheFork with filters for cuisine, price range, rating, and active promotions — get menus with per-dish prices, food/service/ambience rating breakdowns, and each restaurant's most recent reviews.
</td>
</tr></table>

##### Copy to your AI assistant

```
Use the Apify actor "parsebird/thefork-scraper" via the ApifyClient. Example: client.actor("parsebird/thefork-scraper").call(run_input={"searchLocation": "Barcelona", "language": "es", "cuisineTagIds": ["436"], "minRating": 8, "maxRestaurants": 100, "maxReviews": 20}). Key inputs: searchLocation (string, city/neighborhood/address to search), startUrls (array of TheFork restaurant/city URLs, overrides searchLocation), language (en/fr/de/it/es, default en), reviewLanguage (default all), sort (popularity/avgRate/quality/promotion/newRestaurant/price/lastMonthAvg/lastMonthReservation, default popularity), bestRated (boolean), promotionsOnly (boolean), minPrice/maxPrice (integer), minRating/maxRating (1-10), cuisineTagIds (array of tag id strings), maxRestaurants (default 20, 0 = unlimited), maxReviews (default 20, 0 = skip -- TheFork's page reliably exposes about 20 recent reviews per restaurant regardless of a higher setting), maxPhotos (default 20, 0 = skip). Output: one dataset item per restaurant with identity, address/geolocation, ratings breakdown, price, menus with dish-level prices, payment methods, opening hours, photos, and a reviews array. Full API spec: https://apify.com/parsebird/thefork-scraper/api. Get an API token at https://console.apify.com/settings/integrations.
```

### What does TheFork Scraper do?

TheFork Scraper is a **TheFork API alternative** that turns TheFork's restaurant search and restaurant profile pages into structured, ready-to-use data — no manual copy-pasting, no HTML parsing, no anti-bot headaches. Point it at a city and it returns every matching restaurant's rating, price, cuisine, menu, and reviews as clean JSON.

- 🔍 **Search by location** — type any city, neighborhood, or address; typos and non-English spellings are resolved automatically
- 🍽️ **Full menus** — every section and dish with its price, pulled straight from the restaurant's à la carte and fixed-price menus
- ⭐ **Rating breakdown** — TheFork score, TripAdvisor score, and the food/service/ambience sub-ratings shown on the restaurant page
- 💬 **Customer reviews** — reviewer name, rating, review text, meal date, photos, and likes, newest first
- 🎯 **Filters** — cuisine, price range, TheFork rating, top-rated status, and active promotions
- 🔗 **Two input modes** — search a city, or scrape specific restaurant/search-result URLs directly
- 🌍 **Five languages** — thefork.com, .fr, .de, .it, and .es

Built on the [Apify platform](https://apify.com), so every run also comes with **scheduling** for recurring monitoring, instant **API and webhook access**, integrations with Zapier/Make/Google Sheets, and export to **JSON, CSV, Excel, HTML, or XML** straight from the run's dataset.

### Input parameters

| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
| `searchLocation` | string | No\* | — | City, neighborhood, or address to search |
| `startUrls` | array | No\* | — | TheFork restaurant, city, or search-result URLs to scrape directly |
| `language` | string | No | `en` | Output language / TheFork domain: `en`, `fr`, `de`, `it`, `es` |
| `reviewLanguage` | string | No | `all` | Only fetch reviews in this language |
| `sort` | string | No | `popularity` | Sort order for search results |
| `bestRated` | boolean | No | `false` | Only TheFork "Top Rated" restaurants |
| `promotionsOnly` | boolean | No | `false` | Only restaurants with an active discount |
| `minPrice` / `maxPrice` | integer | No | — | Average price per person range |
| `minRating` / `maxRating` | number | No | — | TheFork rating range (1–10) |
| `cuisineTagIds` | array | No | all | Cuisine filter (French, Italian, Japanese, Vegan, Seafood, and 27 more) |
| `maxRestaurants` | integer | No | 20 | Max restaurants to scrape (0 = unlimited) |
| `maxReviews` | integer | No | 20 | Max reviews per restaurant, newest first (0 = skip; TheFork reliably exposes ~20 per restaurant) |
| `maxPhotos` | integer | No | 20 | Max customer photos per restaurant (0 = skip) |

\*Provide either `searchLocation` or `startUrls`.

### What data can you extract from TheFork?

| Field | Description |
|-------|-------------|
| `name`, `slug`, `url`, `legacy_id` | Restaurant identity and canonical URL |
| `cuisine`, `tags`, `highlighted_tags`, `food_requirements` | Cuisine type and dietary/feature tags |
| `country`, `locality`, `street`, `zip_code`, `latitude`, `longitude` | Address and geolocation |
| `thefork_rating`, `tripadvisor_rating`, `food_rating`, `service_rating`, `ambience_rating` | Rating breakdown |
| `avg_price`, `avg_price_currency`, `price_summary` | Average price per person |
| `menus` | Every menu section and dish, with descriptions and prices |
| `payment_accepted`, `opening_hours` | Practical booking info |
| `is_bookable`, `has_active_promotion`, `best_promotion_discount_percentage` | Booking and offer status |
| `is_insider`, `rate_distinction`, `michelin` | Awards and distinctions, where TheFork publishes them |
| `main_photo_url`, `header_photos`, `restaurant_photos`, `customer_photos` | Official and customer photos |
| `reviews` | Reviewer name, rating, review text, meal date, photos, likes |

### Output example

```json
{
  "id": "29b5df71-a3d9-4bdd-933f-76e8155f180b",
  "legacy_id": 660609,
  "slug": "l-imperatif",
  "url": "https://www.thefork.com/restaurant/l-imperatif-r660609",
  "name": "L'Imperatif",
  "cuisine": "French",
  "city_name": "Paris",
  "street": "135 Boulevard Brune",
  "zip_code": "75014",
  "latitude": 48.8250292,
  "longitude": 2.3183516,
  "thefork_rating": 8.8,
  "thefork_review_count": 6795,
  "food_rating": 8.7,
  "service_rating": 8.9,
  "ambience_rating": 8.7,
  "avg_price": 19.0,
  "avg_price_currency": "EUR",
  "is_bookable": true,
  "has_active_promotion": true,
  "best_promotion_discount_percentage": 50,
  "payment_accepted": ["Credit Card", "Mastercard", "Visa", "Voucher"],
  "menus": [
    {
      "name": "À la carte",
      "menu_type": "A_LA_CARTE",
      "sections": [
        {
          "name": "Entrées",
          "items": [
            { "name": "Foie gras mi-cuit en terrine", "price": 15.9, "is_main_dish": false }
          ]
        }
      ]
    }
  ],
  "reviews": [
    {
      "id": "45518763-e169-11ef-8d9e-0e8af7bd4d53",
      "rating_value": 10,
      "meal_date": "2025-02-01T17:00:00.000Z",
      "review_body": "Excellent food and staff. Love this place and bring all my family and friends!",
      "reviewer_name": "Martin G.",
      "likes": 0
    }
  ]
}
```

### Use cases

- **Market research** — benchmark restaurant pricing, ratings, and cuisine mix across a city or neighborhood
- **Lead generation** — build a list of restaurants for a booking, POS, or marketing tool sales pipeline
- **Menu and price monitoring** — track competitor menus and prices over time with a scheduled run
- **Review analysis** — pull customer reviews at scale for sentiment analysis or reputation tracking
- **Site/app content** — power a restaurant directory, recommendation app, or travel guide with structured data

### How it works

1. Enter a `searchLocation` (e.g. "Barcelona") or paste TheFork restaurant/city URLs into `startUrls`.
2. Set optional filters: cuisine, price range, rating, top-rated only, or promotions only.
3. Set `maxRestaurants`, `maxReviews`, and `maxPhotos` to control how much data comes back per run.
4. Click **Start**. The actor resolves the location, pages through matching restaurants, and opens each restaurant's profile to collect ratings, menus, photos, and reviews.
5. Download the results as JSON, CSV, Excel, HTML, or XML from the **Dataset** tab, or pull them via the [Apify API](https://docs.apify.com/api/v2) / [Python](https://docs.apify.com/api/client/python/) / [JavaScript](https://docs.apify.com/api/client/js/) clients.

#### Python

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_API_TOKEN>")
run = client.actor("parsebird/thefork-scraper").call(run_input={
    "searchLocation": "Barcelona",
    "cuisineTagIds": ["436"],
    "minRating": 8,
    "maxRestaurants": 100,
})
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item["name"], item["thefork_rating"])
```

#### JavaScript

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

const client = new ApifyClient({ token: '<YOUR_API_TOKEN>' });
const run = await client.actor('parsebird/thefork-scraper').call({
    searchLocation: 'Barcelona',
    cuisineTagIds: ['436'],
    minRating: 8,
    maxRestaurants: 100,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items);
```

### How much does it cost to scrape TheFork?

TheFork Scraper uses [pay-per-event pricing](https://docs.apify.com/platform/actors/publishing/monetize#pay-per-event-pricing) — you pay for what you extract, not for compute time.

| Event | Price per event | Price per 1,000 |
|-------|-----------------|------------------|
| Restaurant extracted (includes full profile + first 50 reviews) | $0.00299 | **$2.99** |
| Review beyond the first 50 per restaurant | $0.00199 | **$1.99** |

Scraping 100 restaurants costs about **$0.30**. The first 50 reviews per restaurant are included in that price at no extra charge — and since TheFork's restaurant page reliably exposes only around 20 recent reviews per restaurant, the review-beyond-50 charge rarely applies in practice. Paid Apify plans (Bronze/Silver/Gold) get lower per-event rates automatically.

### Input / Output

The input form groups fields into **Search by location**, **Or scrape specific URLs**, **Filters**, and **Limits**, so first-time runs stay simple — just fill in a city and click Start. See the [Input tab](https://apify.com/parsebird/thefork-scraper/input-schema) for the full schema, and the [API tab](https://apify.com/parsebird/thefork-scraper/api) for the JSON API spec.

### Is it legal to scrape TheFork?

Scraping publicly available data, like restaurant names, menus, and ratings shown on public TheFork pages, is generally legal, in line with rulings such as *hiQ Labs v. LinkedIn*. You are responsible for how you use the extracted data — do not scrape personal data or use it in ways that violate TheFork's [terms of service](https://www.thefork.com/) or applicable data protection law (e.g. GDPR). Read more in Apify's [guide to the legality of web scraping](https://blog.apify.com/is-web-scraping-legal/).

### FAQ

**Does this actor need a proxy?**
Yes — TheFork blocks non-residential and unrestricted-country traffic. A France-pinned RESIDENTIAL proxy is enabled by default in the input; you don't need to configure anything unless you want a different country.

**Why do I only get around 20 reviews per restaurant?**
TheFork's restaurant page reliably renders about 20 of its most recent reviews. The actor pages for more up to `maxReviews`, and occasionally finds a few extra, but most restaurants will land around 20 regardless of a higher setting. You're only billed for reviews actually returned, so this never causes an overcharge.

**Can I scrape just one restaurant?**
Yes — put its URL (e.g. `https://www.thefork.com/restaurant/l-imperatif-r660609`) in `startUrls` and leave `searchLocation` empty.

**Does `cuisineTagIds` require an exact match?**
A restaurant matches if it has *any* of the selected cuisines — the filter is OR, not AND.

**Can I run this on a schedule?**
Yes — use Apify's built-in [Scheduler](https://docs.apify.com/platform/schedules) to run it daily, weekly, or on any cron interval for ongoing price or availability monitoring.

**Something looks off or missing — where do I report it?**
Open an issue on the actor's Issues tab, or reach out via the actor's Apify Store page. Feedback shapes what gets fixed next.

**Is there an API?**
Every Apify actor has a full REST API — see the [API tab](https://apify.com/parsebird/thefork-scraper/api) for ready-to-copy request examples in curl, Python, and JavaScript.

# Changelog

This Actor's version history is a separate document: https://apify.com/parsebird/thefork-scraper/changelog.md

# Actor input Schema

## `searchLocation` (type: `string`):

City, neighborhood, or address to search on TheFork. Typos and non-English names are resolved automatically. Ignored if Start URLs is filled in.

## `searchRadiusKm` (type: `integer`):

Radius around the location, in kilometers. Only applies to small towns/villages without their own TheFork city page — large cities always use their full city area.

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

TheFork restaurant, city, or search-result URLs to scrape directly, e.g. https://www.thefork.com/restaurant/l-imperatif-r660609 or https://www.thefork.com/restaurants/paris-c415144. Overrides Search location when filled in.

## `language` (type: `string`):

Which TheFork domain to scrape and which language the restaurant descriptions, tags, and UI text come back in.

## `reviewLanguage` (type: `string`):

Only fetch reviews written in this language. 'All languages' returns every review regardless of language.

## `sort` (type: `string`):

Order search results are returned in.

## `bestRated` (type: `boolean`):

Keep only restaurants TheFork marks as Top Rated.

## `promotionsOnly` (type: `boolean`):

Keep only restaurants that currently have a TheFork discount or special offer active.

## `minPrice` (type: `integer`):

Minimum average price per person, in the local currency (e.g. EUR).

## `maxPrice` (type: `integer`):

Maximum average price per person, in the local currency (e.g. EUR).

## `minRating` (type: `number`):

Minimum TheFork rating, on a 1-10 scale.

## `maxRating` (type: `number`):

Maximum TheFork rating, on a 1-10 scale.

## `cuisineTagIds` (type: `array`):

Keep only restaurants matching at least one of the selected cuisines.

## `maxRestaurants` (type: `integer`):

Maximum number of restaurants to scrape. Set to 0 for unlimited (every matching result). Ignored when Start URLs contains restaurant URLs directly.

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

Maximum reviews to fetch per restaurant, newest first (0 to skip). TheFork's restaurant page reliably exposes its ~20 most recent reviews; the actor pages for more up to this limit, but most restaurants will return around 20 regardless of a higher setting. You are only charged for reviews actually returned -- see Pricing in the README.

## `maxPhotos` (type: `integer`):

Maximum customer-submitted photos to include per restaurant. Official restaurant photos are always included regardless of this setting. Set to 0 to skip customer photos.

## `proxyConfiguration` (type: `object`):

TheFork blocks direct traffic. Apify's UNBLOCKER proxy group is enabled by default and handles this without needing a browser -- you shouldn't need to change it.

## Actor input object example

```json
{
  "searchLocation": "Paris",
  "searchRadiusKm": 10,
  "startUrls": [],
  "language": "en",
  "reviewLanguage": "all",
  "sort": "popularity",
  "bestRated": false,
  "promotionsOnly": false,
  "cuisineTagIds": [],
  "maxRestaurants": 5,
  "maxReviews": 5,
  "maxPhotos": 5,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "UNBLOCKER"
    ]
  }
}
```

# Actor output Schema

## `dataset` (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 = {
    "searchLocation": "Paris",
    "maxRestaurants": 5,
    "maxReviews": 5,
    "maxPhotos": 5,
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "UNBLOCKER"
        ]
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("parsebird/thefork-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 = {
    "searchLocation": "Paris",
    "maxRestaurants": 5,
    "maxReviews": 5,
    "maxPhotos": 5,
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["UNBLOCKER"],
    },
}

# Run the Actor and wait for it to finish
run = client.actor("parsebird/thefork-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 '{
  "searchLocation": "Paris",
  "maxRestaurants": 5,
  "maxReviews": 5,
  "maxPhotos": 5,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "UNBLOCKER"
    ]
  }
}' |
apify call parsebird/thefork-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,parsebird/thefork-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/IVRHe5Jf8sEnaE7vK/builds/tFvjIcT4Xc7as0DNy/openapi.json
