# OpenTable Restaurant Search API Scraper (`rl1987/opentable-api-scraper`) Actor

Search OpenTable restaurants by location (city/area/address or lat-lng) with an optional cuisine/keyword term, and export rich records (name, id, profile URL, cuisines, price band, rating, review count, neighbourhood, address, coordinates, photos).

- **URL**: https://apify.com/rl1987/opentable-api-scraper.md
- **Developed by:** [R.L.](https://apify.com/rl1987) (community)
- **Categories:** Travel, Lead generation, E-commerce
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 1 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.50 / 1,000 restaurant records

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.

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

## OpenTable Restaurant Search Scraper 🏠 — Scrape OpenTable Restaurant Search.com ID, Name, Profile URL, Cuisines

**OpenTable Restaurant Search Scraper** — Search OpenTable restaurants by location (city/area/address or lat-lng) with an optional cuisine/keyword term, and export rich records (name, id, profile URL, cuisines, price band, rating, review count, neighbourhood, address, coordinates, photos). It reads OpenTable Restaurant Search's own API directly, so it's **fast, reliable and complete** — no flaky HTML parsing, no headless browser. Export to **JSON, CSV, Excel, or an API**.

***

### ✨ Why use this OpenTable Restaurant Search Scraper?

- 🟢 **No code required** — set your input, click **Start**, download your data.
- ⚡ **Fast & accurate** — reads OpenTable Restaurant Search's own private API and returns structured JSON, not scraped HTML.
- 🧾 **Rich, structured data** — every field below, clean and ready to use.
- 🔁 **Full pagination** — collects results to your `maxItems` cap.
- 💸 **Transparent pay-per-result pricing** — pay only for the rows you get.
- 📤 **Export anywhere** — JSON, CSV, Excel, XML, or pull it live via the Apify API.

***

### 🎯 What can you do with OpenTable Restaurant Search data?

- **Market & competitor research** — analyse OpenTable Restaurant Search property listings at scale.
- **Price & availability monitoring** — track changes over time.
- **Data science & trend analysis** — build clean datasets.
- **Lead generation & enrichment** — feed OpenTable Restaurant Search data into your own tools.

***

### 📥 What data does the OpenTable Restaurant Search Scraper extract?

Each row includes: `id`, `name`, `url`, `cuisines`, `priceBand`, `priceBandId`, `rating`, `reviewCount`, `neighborhood`, `neighborhoodId`, `metroId`, `address`, `city`, `state` …and more. See the full **Data table** below.

***

### 🚀 How to scrape OpenTable Restaurant Search.com (3 steps)

1. **Set your input** — provide `location` or `q` (see the table below; defaults work out of the box).
2. **Pick options** — filters, a `proxyConfiguration`, and a `maxItems` cap.
3. **Run & export** — click **Start**, then download the dataset as JSON/CSV/Excel or fetch it via the API.

### 🔎 Deep scraping — details & reviews

Both are **off by default** and billed as separate pay-per-event surcharges, so a plain search
run stays cheap.

| Option | Adds | Event |
|---|---|---|
| **Include restaurant details** | **Phone number** (raw digits + formatted), website, opening hours, dining style, dress code, parking, payment options, menu link, popular dishes, max party size, booking window, bookings in the last 24h | `detail-row` |
| **Include diner reviews** | Review text, author, posted/reservation dates, and per-category ratings — overall, food, service, ambience, value, noise | `reviews-row` |

Reviews are paged automatically to `reviewsPerRestaurant` and can be ordered by
**newest**, **highest rated**, **lowest rated** or **recommended**. Enrichment runs in
parallel (`detailConcurrency`, default 8) — 30 restaurants with both options take roughly
25 seconds rather than several minutes.

#### ⚠️ Details need a US proxy

OpenTable geo-gates the restaurant-profile route. It does not return an error from a non-US
IP — it simply **hangs until the request times out**. Measured on the Apify platform, same
input, 5 restaurants:

| Proxy | Phones returned | Run time |
|---|---|---|
| Apify Proxy (default datacenter) | 3 / 5 | 133 s |
| **Apify Proxy — RESIDENTIAL, country US** | **5 / 5** | **24 s** |

**Residential US is therefore the default** — details work out of the box, no configuration
needed. It is not a speed penalty: a 100-record search-only run took 18.5 s on residential
vs 16.1 s on datacenter, and used only 0.00012 GB of residential transfer (~860k records per
10 GB).

The difference is platform usage cost — that same run was **$0.0028 residential vs $0.0004
datacenter**. Search and reviews are not geo-gated, so for large search-only jobs switching
`proxyConfiguration` to a datacenter group is the cheaper choice; keep residential whenever
details are on.

***

### ⚙️ Input

| Field | Type | Default | Description |
| --- | --- | --- | --- |
| `location` | string | `New York, NY` | City, area, or address to search around (e.g. "New York, NY", "San Francisco"). Resolved to coordinates at run time. |
| `q` | string | – | Optional cuisine or keyword (e.g. "sushi", "Indian"). |
| `lat` | number | – | Optional latitude override; skips the location lookup. |
| `lon` | number | – | Optional longitude override; skips the location lookup. |
| `maxItems` | integer | `100` | Maximum number of restaurants to return (0 = all pages). |
| `includeDetails` | boolean | `false` | Fetch each restaurant's full profile: <b>phone number</b>, website, opening hours, dining style, dress code, parking, payment options, menu link and popular dishes. One extra request per restaurant, billed as a separate detail-row event. <b>Requires a US proxy</b> — this route is geo-gated and simply hangs from non-US IPs. |
| `includeReviews` | boolean | `false` | Fetch diner reviews per restaurant — text, author, dates and the per-category ratings (food, service, ambience, value, noise). Billed as a separate reviews-row event. |
| `reviewsPerRestaurant` | integer | `20` | How many reviews to fetch for each restaurant (paged automatically). Only used when reviews are enabled. |
| `reviewsSort` | enum | `newestReview` | Which reviews come first. — one of `newestReview`, `highestRating`, `lowestRating`, `recommended` |
| `detailConcurrency` | integer | `8` | How many restaurants to enrich in parallel (1-20). Raise for speed, lower to be gentler on the API and your proxy. |
| `proxyConfiguration` | object | Apify Proxy | Route requests through Apify Proxy (defaults to RESIDENTIAL/US). Search works from any egress, but <b>restaurant details are geo-gated to US IPs</b> — enable Apify Proxy (RESIDENTIAL, country US) when using the details option. |

#### Example input

```jsonc
{
  "location": "New York, NY",
  "maxItems": 100,
  "includeDetails": false,
  "includeReviews": false,
  "reviewsPerRestaurant": 20,
  "reviewsSort": "newestReview",
  "detailConcurrency": 8,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}
```

***

### 📤 Output

Each result is a JSON object like this:

```jsonc
{
  "id": "108541",
  "name": "Sofi Restaurant",
  "url": "https://www.opentable.com/restaurant/profile/108541",
  "cuisines": [
    "Italian"
  ],
  "priceBand": "$31 to $50",
  "priceBandId": "3",
  "rating": 4.7,
  "reviewCount": 159,
  "neighborhood": "South Loop",
  "neighborhoodId": "82",
  "metroId": "3",
  "address": "616 South Dearborn",
  "city": "Chicago",
  "state": "IL",
  "postalCode": "60605",
  "country": "United States",
  "latitude": 41.8737957,
  "longitude": -87.629455,
  "images": [
    "https://resizer.otstatic.com/v4/photos/67602167-3?width=105&height=105",
    "https://resizer.otstatic.com/v4/photos/67602167-3?width=160&height=160",
    "…+6 more"
  ],
  "photosCount": 0,
  "description": "Born in Milan to a renowned restaurant family, Chef/Owner Maurizio Michi has always had a passion for cooking.\nThis Northern Italian Restaurant features clas…",
  "features": [
    "RESERVATION",
    "ONLINE_WAITLIST",
    "…+2 more"
  ],
  "firstNextAvailableTime": "2026-08-22T16:30",
  "phone": "3127535502",
  "phoneFormatted": "(312) 753-5502",
  "website": "http://www.sofichicago.com/",
  "menuUrl": null,
  "hours": "Mon–Thu, Sun 4:00 pm–10:00 pm\nFri, Sat 4:00 pm–10:30 pm",
  "diningStyle": "Casual Elegant",
  "dressCode": "Business Casual",
  "parkingInfo": "Street Parking",
  "paymentOptions": [
    "AMEX",
    "Discover",
    "…+2 more"
  ],
  "popularDishes": [
    "Gnocchi Genovese",
    "Tagliatelle Bolognese",
    "…+6 more"
  ],
  "maxPartySize": 8,
  "maxAdvanceDays": 90,
  "isTakeoutEnabled": true,
  "hasGenderNeutralRestroom": true,
  "currencyCode": "USD",
  "resosInLastDay": 24,
  "reviewsFetched": 3,
  "reviews": [
    {
      "…": "16 fields"
    },
    {
      "…": "16 fields"
    },
    "…+1 more"
  ]
}
```

***

### 🧾 Data table

| Field | Type | Description |
| --- | --- | --- |
| `id` | text | ID |
| `name` | text | Name |
| `url` | link | Profile URL |
| `cuisines` | array | Cuisines |
| `priceBand` | text | Price band |
| `priceBandId` | text | Price band id |
| `rating` | number | Rating |
| `reviewCount` | number | Review count |
| `neighborhood` | text | Neighbourhood |
| `neighborhoodId` | text | Neighbourhood id |
| `metroId` | text | Metro id |
| `address` | text | Address |
| `city` | text | City |
| `state` | text | State |
| `postalCode` | text | Postal code |
| `country` | text | Country |
| `latitude` | number | Latitude |
| `longitude` | number | Longitude |
| `images` | array | Photo URLs |
| `photosCount` | number | Photos count |
| `description` | text | Description |
| `features` | array | Features |
| `firstNextAvailableTime` | text | First next available |
| `phone` | text | Phone |
| `phoneFormatted` | text | Phone (formatted) |
| `website` | link | Website |
| `menuUrl` | link | Menu |
| `hours` | text | Opening hours |
| `diningStyle` | text | Dining style |
| `dressCode` | text | Dress code |
| `parkingInfo` | text | Parking |
| `paymentOptions` | array | Payment options |
| `popularDishes` | array | Popular dishes |
| `maxPartySize` | number | Max party size |
| `maxAdvanceDays` | number | Booking window (days) |
| `isTakeoutEnabled` | text | Takeout |
| `hasGenderNeutralRestroom` | text | Gender-neutral restroom |
| `currencyCode` | text | Currency |
| `resosInLastDay` | number | Bookings last 24h |
| `reviewsFetched` | number | Reviews fetched |
| `reviews` | array | Reviews |

***

### 💰 Pricing — pay per result

This actor uses **pay-per-event** pricing: you pay only for the rows it delivers.

| You scrape | Price |
| --- | --- |
| **Restaurant record** | **$0.50 per 1,000 rows** |
| **Restaurant details** | **$0.50 per 1,000 rows** |
| **Diner reviews** | **$0.50 per 1,000 rows** |

***

### 🔌 Use the OpenTable Restaurant Search Scraper via API

Run it programmatically with the [Apify API](https://docs.apify.com/api/v2):

```bash
curl -X POST "https://api.apify.com/v2/acts/opentable-api-scraper/runs?token=YOUR_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{
  "location": "New York, NY",
  "maxItems": 100,
  "includeDetails": false,
  "includeReviews": false,
  "reviewsPerRestaurant": 20,
  "reviewsSort": "newestReview",
  "detailConcurrency": 8,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}'
```

Or with the Apify CLI: `apify call opentable-api-scraper -i input.json -o`.

***

### ❓ FAQ

**Do I need an account or API key for OpenTable Restaurant Search?**

No. The actor talks to OpenTable Restaurant Search's public/mobile API for you — you only need an Apify account.

**Do I need a proxy?**

A proxy is recommended for reliable runs; set `proxyConfiguration` (Residential is safest for geo-restricted or bot-protected sites).

**What export formats are supported?**

JSON, CSV, Excel, XML, JSONL, RSS, or live via the Apify API and dataset endpoints.

**Is scraping this legal?**

You are responsible for how you use the data. Scrape only public property listing data and comply with OpenTable Restaurant Search.com's Terms and applicable law (e.g. GDPR/CCPA for personal data).

***

### 🛠️ How it works

The actor impersonates OpenTable Restaurant Search's official app/site and calls its private API the same way the app does, then normalises each response into the flat record above. No browser, no HTML scraping — just clean, structured data.

***

### 📌 Good to know

- Scrape only public property listing data and respect OpenTable Restaurant Search.com's Terms of Service and robots policy.
- For personal data, comply with GDPR/CCPA and applicable law — you are the data controller.
- Fields can be `null` when OpenTable Restaurant Search doesn't expose them for a given record.

***

### ⭐ Found this useful?

Give the **OpenTable Restaurant Search Scraper** a star on Apify and check out my other `apify-*-api-scraper` actors.

# Actor input Schema

## `location` (type: `string`):

City, area, or address to search around (e.g. "New York, NY", "San Francisco"). Resolved to coordinates at run time.

## `q` (type: `string`):

Optional cuisine or keyword (e.g. "sushi", "Indian").

## `lat` (type: `number`):

Optional latitude override; skips the location lookup.

## `lon` (type: `number`):

Optional longitude override; skips the location lookup.

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

Maximum number of restaurants to return (0 = all pages).

## `includeDetails` (type: `boolean`):

Fetch each restaurant's full profile: <b>phone number</b>, website, opening hours, dining style, dress code, parking, payment options, menu link and popular dishes. One extra request per restaurant, billed as a separate detail-row event. <b>Requires a US proxy</b> — this route is geo-gated and simply hangs from non-US IPs.

## `includeReviews` (type: `boolean`):

Fetch diner reviews per restaurant — text, author, dates and the per-category ratings (food, service, ambience, value, noise). Billed as a separate reviews-row event.

## `reviewsPerRestaurant` (type: `integer`):

How many reviews to fetch for each restaurant (paged automatically). Only used when reviews are enabled.

## `reviewsSort` (type: `string`):

Which reviews come first.

## `detailConcurrency` (type: `integer`):

How many restaurants to enrich in parallel (1-20). Raise for speed, lower to be gentler on the API and your proxy.

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

Route requests through Apify Proxy. Defaults to <b>RESIDENTIAL, country US</b>, which is what the restaurant-details option needs: that route is geo-gated and hangs (rather than erroring) from non-US IPs — measured 5/5 phone numbers in 24s on residential US vs 3/5 in 133s on datacenter. Search and reviews are not gated, so for a search-only run you can switch to datacenter to go faster and spare your residential GB quota.

## Actor input object example

```json
{
  "location": "New York, NY",
  "maxItems": 100,
  "includeDetails": false,
  "includeReviews": false,
  "reviewsPerRestaurant": 20,
  "reviewsSort": "newestReview",
  "detailConcurrency": 8,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "US"
  }
}
```

# 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 = {
    "location": "New York, NY",
    "q": "",
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ],
        "apifyProxyCountry": "US"
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("rl1987/opentable-api-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 = {
    "location": "New York, NY",
    "q": "",
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
        "apifyProxyCountry": "US",
    },
}

# Run the Actor and wait for it to finish
run = client.actor("rl1987/opentable-api-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 '{
  "location": "New York, NY",
  "q": "",
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "US"
  }
}' |
apify call rl1987/opentable-api-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,rl1987/opentable-api-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/yZbXSuOUBSLAYKiDI/builds/jM8Z9VMlKG3vuVqki/openapi.json
