OpenTable Reviews Scraper – Restaurants, Ratings & Diner Data avatar

OpenTable Reviews Scraper – Restaurants, Ratings & Diner Data

Pricing

from $1.00 / 1,000 review scrapeds

Go to Apify Store
OpenTable Reviews Scraper – Restaurants, Ratings & Diner Data

OpenTable Reviews Scraper – Restaurants, Ratings & Diner Data

Scrape full OpenTable.com restaurant reviews by keyword, area, or restaurant profile URLs. Collect every available review with text, ratings, diner profile data, tags, photos, restaurant metadata, cuisines, neighborhood, and profile links in structured rows.

Pricing

from $1.00 / 1,000 review scrapeds

Rating

0.0

(0)

Developer

Abot API

Abot API

Maintained by Community

Actor stats

1

Bookmarked

38

Total users

7

Monthly active users

13 days ago

Last modified

Share

OpenTable Reviews Scraper: Review Text, Ratings & Diner Data

OpenTable Reviews Scraper turns OpenTable restaurant pages into structured review data. Search by keyword and location, or paste restaurant profile links, and collect every review OpenTable has for each restaurant: full review text, the overall rating plus separate food, service, ambience, value and noise ratings, the dined date, diner details, restaurant replies and review photos, with the parent restaurant's own name, cuisine, neighborhood and rating carried on every row. Export straight to JSON, CSV or Excel, or read the dataset through the API.

Why This Scraper?

  • Two ways to find restaurants. Search by keyword and location, or paste OpenTable restaurant links directly. No need to look up restaurant IDs by hand.
  • Full review depth on every row. Review text plus five per-category ratings, food, service, ambience, value and noise, not just the single overall star score.
  • Deep filtering. Minimum rating per category, cuisine, neighborhood, VIP reviewers only, photos only, and a dined date window, so you only pay for reviews that match.
  • Restaurant context on every row. Cuisine, neighborhood, restaurant level rating and total review count are carried on each review, so no extra join is needed.
  • Built to run at scale. Resume an interrupted pull without paying twice, and cap reviews per restaurant independently from the run wide total.
  • Built for schedules. Incremental mode returns only new, updated and reappeared reviews on repeat runs, classified per review.
  • Fails loudly, not quietly. A run that could not reach OpenTable stops with a clear message instead of returning a misleading empty dataset.

Use Cases

  • Reputation management: track new reviews and rating trends for your own restaurants as they come in.
  • Competitive research: compare food, service, ambience and value ratings, and cuisine mix, across nearby restaurants.
  • Market research: build a dataset of diner sentiment and review volume across neighborhoods or cities.
  • Lead generation: find highly rated restaurants and their OpenTable profile links for outreach or listing sites.
  • AI and NLP: feed review text into sentiment analysis, review summarization, or an AI agent answering "where should I eat" questions.

Data You Get

Sample shape: values are illustrative placeholders, not from a live record.

FieldExample
reviewId"OT-1000001-2100000001-100000000001"
reviewText"Great spot for a birthday dinner, the staff were attentive all night."
ratingOverall / ratingFood / ratingService5 / 5 / 4
ratingAmbience / ratingValue5 / 4
ratingNoise"MODERATE"
dinedDate / submittedDateTime"2026-06-09" / "2026-06-10T16:02:39Z"
reviewerName / reviewerLocation"Jordan K." / "New York City"
reviewerIsVipfalse
reviewerApprovedReviewCount3
reviewHelpfulUp / reviewHelpfulDown / reviewHelpfulScore2 / 0 / 2
restaurantReplyTextnull
reviewPhotoUrls / reviewPhotoCountarray of photo URLs / 1
restaurantName"Sample Sushi House"
restaurantUrlOpenTable link to the restaurant profile
restaurantPrimaryCuisine / restaurantCuisines"Japanese" / ["Japanese", "Sushi"]
restaurantNeighborhood"Tribeca"
restaurantRating / restaurantTotalReviewCount4.7 / 5397
sourceMode / sourceQuery"search" / "sushi"
scrapedAttimestamp of when this row was scraped

Every row also carries the restaurant's internal id, metro area and broader region, price band and currency symbol, and its profile photo; the review's own type flag and exact dined timestamp; the reviewer's initials, profile color and photo when OpenTable provides them; and the source URL when the row came from URL mode. Incremental mode (below) adds changeType, changedFields, firstSeenAt and lastSeenAt to every row.

How to Use

  1. Pick a mode: Keyword search (restaurants around a location) or Restaurant URLs (paste OpenTable profile links).
  2. For keyword search, add search terms and a location, or set exact latitude and longitude; for URL mode, paste one or more restaurant profile links.
  3. Optionally narrow results with the rating, cuisine, neighborhood, VIP, photo, and dined date filters, and set Max items to control run size and cost.
  4. Click Start, then download the dataset as JSON, CSV or Excel, or read it through the API.

Keyword search:

{
"mode": "search",
"keywords": ["sushi", "steakhouse"],
"location": "New York",
"maxItems": 20
}

Keyword search, filtered to highly rated reviews with photos:

{
"mode": "search",
"keywords": ["rooftop"],
"location": "Miami",
"minOverallRating": 4,
"requireReviewPhotos": true,
"maxItems": 50
}

Restaurant URLs:

{
"mode": "url",
"startUrls": [
{ "url": "https://www.opentable.com/r/yakiniku-shodai-san-francisco" }
],
"maxReviewsPerRestaurant": 200
}

Recurring monitoring of the same search:

{
"mode": "search",
"keywords": ["steakhouse"],
"location": "Chicago",
"incrementalMode": true,
"stateKey": "chicago-steakhouses"
}

Run it from your code

Python:

from apify_client import ApifyClient
client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("abotapi/opentable-reviews-scraper").call(run_input={"mode": "search", "keywords": ["sushi"], "location": "New York"})
for review in client.dataset(run["defaultDatasetId"]).iterate_items():
print(review["restaurantName"], review["ratingOverall"], review["reviewText"])

JavaScript:

import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });
const run = await client.actor('abotapi/opentable-reviews-scraper').call({ mode: 'search', keywords: ['sushi'], location: 'New York' });
const { items } = await client.dataset(run.defaultDatasetId).listItems();

Or connect it to Make, Zapier, n8n, Google Sheets or webhooks from the Integrations tab.

Resume and recurring updates

  • Resume (resumeFromRunId) continues one interrupted run: paste its run or dataset ID and the actor skips every review it already collected there, so you don't pay twice.
  • Incremental mode (incrementalMode) is for recurring monitoring of the same search or URLs. Each review is classified NEW, UPDATED (with changedFields), UNCHANGED (suppressed and not billed unless emitUnchanged is on), REAPPEARED, or EXPIRED (only after a run that scanned every tracked restaurant in full, and only with emitExpired). stateKey names or shares the stored state; leave it empty to derive one automatically from the search or URLs and the active filters. Combining Resume with Incremental mode is only allowed before that state key has saved data; once it has, remove Resume or pick a different State key.

Keyword search first collects up to Max restaurants per keyword restaurants around your coordinates, then pulls reviews from each one until Max items is reached. A keyword that returns exactly the restaurant cap may have more restaurants available; raise Max restaurants per keyword to check. Max items is the hard stop on total review rows across every keyword and restaurant in the run.

Input Parameters

ParameterTypeDefaultDescription
modestringsearchsearch (keyword and location discovery) or url (paste restaurant profile links).
keywordsarray(none)Restaurant, cuisine, or dining terms to search (search mode).
locationstringNew YorkCity, neighborhood, or address to search around (search mode); resolved to coordinates automatically.
latitudenumber(none)Optional. Exact latitude; together with Longitude it overrides Location.
longitudenumber(none)Optional. Exact longitude; together with Latitude it overrides Location.
startUrlsarray(none)OpenTable restaurant profile URLs to scrape (url mode).
maxItemsinteger20Maximum review rows saved across the whole run. The run can return fewer when OpenTable has fewer matching reviews.
maxReviewsPerRestaurantinteger(none)Optional cap on reviews collected per restaurant. Leave empty to collect every available review for each restaurant, bounded only by Max items.
maxRestaurantsinteger50Keyword search only. How many restaurants around the coordinates to pull reviews from, per keyword.
reviewSortstringhighestRatedOrder reviews are returned in, per restaurant: highestRated, newest, or lowestRated.
minOverallRatingnumber(none)Only keep reviews with at least this overall rating.
minFoodRatingnumber(none)Only keep reviews with at least this food rating.
minServiceRatingnumber(none)Only keep reviews with at least this service rating.
minAmbienceRatingnumber(none)Only keep reviews with at least this ambience rating.
minValueRatingnumber(none)Only keep reviews with at least this value rating.
cuisinesarray(none)Only keep restaurants whose primary or secondary cuisine matches at least one value.
neighborhoodsarray(none)Only keep restaurants whose neighborhood matches at least one value.
fromDinedDatestring(none)Only keep reviews with a dined date on or after this YYYY-MM-DD date.
toDinedDatestring(none)Only keep reviews with a dined date on or before this YYYY-MM-DD date.
vipOnlybooleanfalseOnly keep reviews where OpenTable marks the reviewer as VIP.
requireReviewPhotosbooleanfalseOnly keep reviews that include diner photos.
resumeFromRunIdstring(none)Continue one interrupted run: paste its run or dataset ID and already collected reviews are skipped and not re-charged.
incrementalModebooleanfalseTurn on for recurring monitoring of the same search or URLs. Classifies each review as NEW, UPDATED, UNCHANGED, REAPPEARED, or EXPIRED against the previous run for this state.
emitUnchangedbooleanfalseAlso return, and bill, reviews unchanged since the last run (incremental mode only).
emitExpiredbooleanfalseAlso return, and bill, reviews that were tracked before but are no longer found (incremental mode only). Only produced when a run fully scans every tracked restaurant or search.
stateKeystring(none)Name or share an incremental mode monitoring state. Leave empty to derive one automatically from the search or URLs and the active filters.
proxyobjectApify ProxyConnection routing for the run.
mcpConnectorsarray(none)Connector selection for exporting results to connected apps. Not active in this build; selecting a connector currently has no effect on the run or the dataset.
notionParentPageUrlstring(none)Notion page under which exported rows would be created. Not active in this build (see mcpConnectors).
maxNotifyListingsinteger50Cap on rows sent to each connector per run. Not active in this build (see mcpConnectors); does not affect the dataset.

Output Example

Sample shape: values are illustrative placeholders, not from a live record.

{
"reviewId": "OT-1000001-2100000001-100000000001",
"reviewText": "Great spot for a birthday dinner, the staff were attentive all night and the sushi was excellent.",
"ratingOverall": 5,
"ratingFood": 5,
"ratingService": 4,
"ratingAmbience": 5,
"ratingValue": 4,
"ratingNoise": "MODERATE",
"reviewType": "OPENTABLEVERIFIED",
"submittedDateTime": "2026-06-10T16:02:39Z",
"dinedDateTime": "2026-06-09T18:45:00-04:00",
"dinedDate": "2026-06-09",
"reviewHelpfulUp": 2,
"reviewHelpfulDown": 0,
"reviewHelpfulScore": 2,
"restaurantReplyText": null,
"reviewerName": "Jordan K.",
"reviewerInitials": "JK",
"reviewerIsVip": false,
"reviewerLocation": "New York City",
"reviewerApprovedReviewCount": 3,
"reviewerProfileColor": "#3E7C59",
"reviewerPhotoUrl": null,
"reviewPhotoUrls": ["https://www.opentable.com/example-assets/photos/sample-1.jpg"],
"reviewPhotoCount": 1,
"restaurantId": 1000001,
"restaurantName": "Sample Sushi House",
"restaurantUrl": "https://www.opentable.com/r/sample-sushi-house-new-york",
"restaurantMetro": "New York City",
"restaurantMacro": "Northeast",
"restaurantNeighborhood": "Tribeca",
"restaurantPrimaryCuisine": "Japanese",
"restaurantCuisines": ["Japanese", "Sushi"],
"restaurantPriceBand": 3,
"restaurantCurrencySymbol": "$",
"restaurantRating": 4.7,
"restaurantPhotoUrl": "https://www.opentable.com/example-assets/restaurants/sample-profile.jpg",
"restaurantTotalReviewCount": 5397,
"sourceMode": "search",
"sourceQuery": "sushi",
"sourceUrl": null,
"scrapedAt": "2026-06-15T09:12:00.000000+00:00"
}

Plan Requirement

The default proxy setting works out of the box on every Apify plan, and the actor rotates connections automatically when one is refused. For larger or more frequent pulls, the residential proxy group gives more headroom; pick it under Connection in the input.

FAQ

How much does it cost?

You pay per review row returned; runs that find no matching reviews are not charged for that event. The Pricing tab shows the current rates. Use Max items to cap the cost of any run.

This actor collects only publicly available review data. You are responsible for how you use it: follow OpenTable's terms and the laws that apply to you, and get legal advice if you plan commercial redistribution. Review text and ratings belong to the diners and restaurants that posted them.

Can I get only new reviews on a schedule?

Yes. Schedule the actor from the Schedules tab and turn on Incremental mode. Each run then classifies every review as new, updated, unchanged, reappeared, or expired against the previous run for that search, and unchanged reviews are not billed unless you turn that on.

Why do I get one row with "error": true instead of an empty dataset?

When a run finds zero matching reviews, whether from an unmatched search, an unresolved restaurant URL, or filters that exclude everything, the actor pushes a single row shaped like {"error": true, "code": "ZERO_RESULTS", "message": "..."} instead of leaving the dataset empty, so "nothing matched" is never confused with a run that silently produced nothing. Try broader keywords, nearby coordinates, fewer filters, or different restaurant URLs.

Why did my run fail instead of returning results?

A run fails, rather than returning an empty or ZERO_RESULTS dataset, when the actor could not establish a working connection to OpenTable at all, when it lost that connection partway through, could not recover, and had not saved any reviews yet (if some reviews were already saved, the run finishes with those partial results), when resumeFromRunId points at a run or dataset ID it cannot read, or when Incremental mode already has saved state for a search and resumeFromRunId is set at the same time. Each of these stops the run with a clear status message describing what to check or retry.

Can I use it with AI agents or MCP?

Yes. Call it from any Apify integration, or from an AI agent through Apify's MCP server.

🔗 More scrapers you might like

Pair this actor with these related scrapers from the same team:

🛒 Zomato Restaurants & Reviews Scraper
Scrape restaurants and reviews from zomato.com. Get names, cuisines, ratings and votes...
🧩 Resy Scraper
Scrape Resy restaurants and live reservation availability by city or URL. Get bookable...
🛒 GrabFood Restaurant Scraper
Scrape GrabFood restaurants across Southeast Asia, including SG, MY, TH, VN, PH, ID, KH...
🏨 Caterer.com Scraper
Scrape UK hospitality jobs from Caterer.com, including chef, hotel, restaurant, bar, and...
🍷 Wolt.com
From $0.8/1K. Scrape Wolt restaurants and full menus at the city scale. Extract 60+...
✈️ Naver Map Scraper
From $1/1K. Scrape Naver Map places by keyword or URL: names, categories, ratings...

👉 Browse all abotapi scrapers

💬 Support & custom scrapers

  • 🐞 Found a bug or a missing field? Open a ticket on the Issues tab. We usually reply within hours.
  • 🛠️ Need another site, extra fields or a private build? Email abotapi@proton.me or message Telegram @abotapi.
  • ⭐ Enjoying it? A quick review on the actor page helps other users find it.