OpenTable Restaurant Scraper – Reviews, Ratings & Phone Numbers avatar

OpenTable Restaurant Scraper – Reviews, Ratings & Phone Numbers

Pricing

from $0.50 / 1,000 restaurant records

Go to Apify Store
OpenTable Restaurant Scraper – Reviews, Ratings & Phone Numbers

OpenTable Restaurant Scraper – Reviews, Ratings & Phone Numbers

Scrape OpenTable restaurants in any city, by area, address or lat-lng, with an optional cuisine keyword. Each row: name, profile URL, cuisines, price band, rating, review count, neighbourhood, address, coordinates. Opt in for phone, website, hours, menu, popular dishes and diner reviews.

Pricing

from $0.50 / 1,000 restaurant records

Rating

0.0

(0)

Developer

R.L.

R.L.

Maintained by Community

Actor stats

1

Bookmarked

13

Total users

8

Monthly active users

3 days ago

Last modified

Share

OpenTable Restaurant Scraper — Reviews, Ratings & Phone Numbers

Scrape restaurants from OpenTable (opentable.com) in any city OpenTable covers. Search by city, area or address — or by exact coordinates — with an optional cuisine keyword, and export clean records: name, profile URL, cuisines, price band, star rating, review count, neighbourhood, full address and coordinates. Switch on the optional deep scrape for phone numbers, website, opening hours, full menus (every section, item, description and price), popular dishes, reservation availability (open tables by date and party size) and full diner reviews. Or skip the search: give it the restaurants you care about by URL, ID or name and pick a single-purpose mode, so a scheduled run tracking menu changes fetches nothing else. It reads OpenTable's own mobile API directly, so it's fast and complete — no flaky HTML parsing, no headless browser. Export to JSON, CSV, Excel, or an API.


Why use this OpenTable scraper?

  • No code required — set your input, click Start, download your data.
  • Fast & accurate — reads OpenTable's own mobile API and returns structured JSON, not scraped HTML.
  • Rich, structured data — 25 fields from search alone, 41 with the deep scrape on.
  • Full pagination — 30 restaurants per page, collected up to your maxItems cap, with cross-page duplicates removed.
  • 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 data?

  • Restaurant market & competitor research — compare cuisines, price bands and ratings across a city or a neighbourhood.
  • Demand & popularity tracking — resosInLastDay (bookings in the last 24 hours) and firstNextAvailableTime show how booked a restaurant is, sampled over time.
  • Review mining — per-category diner scores for food, service, ambience, value and noise, with the review text.
  • Lead generation — restaurant phone numbers, websites and addresses for a whole metro.
  • Data science — clean, flat datasets for pricing, rating and cuisine analysis.

What data does the OpenTable scraper extract?

Each row is one restaurant. From search alone you get: id, name, url, cuisines, priceBand, priceBandId, rating, reviewCount, neighborhood, neighborhoodId, metroId, address, city, state, postalCode, country, latitude, longitude, images, photosCount, description, features, firstNextAvailableTime, reviews, reviewsFetched. The deep-scrape options add 16 more. See the full Data table below.


How do I scrape OpenTable restaurants? (3 steps)

  1. Set your search area — a location string (city, area or address), or lat + lon. Add q for a cuisine or keyword.
  2. Pick options — maxItems to cap the run, and the two deep-scrape toggles if you need phones or reviews.
  3. Run & export — click Start, then download the dataset as JSON/CSV/Excel or fetch it via the API.

Can I get phone numbers, menus and diner reviews?

Yes — all four deep-scrape options are off by default and each is billed as its own pay-per-event surcharge, so a plain search run stays cheap. Current prices are on the Pricing tab.

OptionAdds
Include restaurant detailsPhone number (raw digits + formatted), website, opening hours, dining style, dress code, parking, payment options, menu link, popular dishes, max party size, booking window, currency, takeout flag, gender-neutral restroom flag, bookings in the last 24h
Include full menuEvery OpenTable-hosted menu: sections, dishes, prices, descriptions and dish photos, with the menu provider's attribution preserved
Include diner reviewsReview text, author and their home metro, posted/reservation dates, VIP and loyalty tier, and per-category ratings — overall, food, service, ambience, value, noise
Include availabilityBookable time slots for a date and party size, or OpenTable's own reason there are none

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:

ProxyPhones returnedRun time
Apify Proxy (default datacenter)3 / 5133 s
Apify Proxy — RESIDENTIAL, country US5 / 524 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).

Search and reviews are not geo-gated, so a datacenter group works for search-only jobs if you prefer one; keep residential whenever details, menus or availability are on, because those routes are US-gated and hang rather than erroring from elsewhere. Either way the proxy transfer is not billed to you on top of the event charges.


Modes

One run does one job. mode defaults to search, so anything that worked before still works unchanged.

modeneedsreturns
search (default)location or lat+lonrestaurants by city, area or coordinates — plus whichever include* extras you switch on
restaurantsrestaurantUrls / restaurantNamesphone, hours, website, popular dishes
menusrestaurantUrls / restaurantNamesevery menu, section, item and price
reviewsrestaurantUrls / restaurantNamesdiner reviews with category ratings
availabilityrestaurantUrls / restaurantNamesopen tables by date and party size

An enrichment mode fetches only its own data, so a monitoring run that wants menus does not pay for reviews — it sets its own include* flag and clears the other three. It ignores the search inputs (location, q, sort, the filters) and logs which ones it dropped, rather than leaving you to wonder why q did nothing.

In search mode the include* flags are additive and behave exactly as they always have.

{ "mode": "menus",
"restaurantUrls": ["1230112", "https://www.opentable.com/restaurant/profile/42265"] }
{ "mode": "availability",
"restaurantNames": ["Brooklyn Chop House Times Square, New York"],
"daysAhead": 7, "time": "19:00", "partySize": 2 }

What can I filter on?

FieldTypeDefaultDescription
modestringsearchWhat the run does: search, or restaurants / menus / reviews / availability to fetch one kind of data for restaurants you list. See Modes.
restaurantUrlsarray–Restaurants to fetch, for every mode except search: a numeric ID (42265), a profile URL, or any opentable.com restaurant URL — slug URLs are resolved automatically.
restaurantNamesarray–Restaurants by name, one per line as Name, City — e.g. Brooklyn Chop House Times Square, New York. An ambiguous name is reported with the near-misses rather than guessed.
locationstringNew York, NYCity, area, or address to search around (e.g. "New York, NY", "San Francisco"). Resolved to coordinates through OpenTable's own autocomplete at run time.
qstring–Optional cuisine or keyword (e.g. "sushi", "Indian").
latnumber–Optional latitude override; skips the location lookup.
lonnumber–Optional longitude override; skips the location lookup.
maxItemsinteger100Maximum number of restaurants to return (0 = all pages).
includeDetailsbooleanfalseFetch each restaurant's full profile: phone number, website, opening hours, dining style, dress code, parking, payment options, menu link and popular dishes. One extra request per restaurant, billed as its own event. Requires a US proxy — this route is geo-gated and simply hangs from non-US IPs.
includeReviewsbooleanfalseFetch diner reviews per restaurant — text, author, dates and the per-category ratings (food, service, ambience, value, noise). Billed per review returned.
includeMenubooleanfalseFetch the complete OpenTable-hosted menu per restaurant: every menu, section, item, description, price and size variation, plus the menu's currency and data provider. One extra request per restaurant. Separate from the menuUrl field, which links to the menu on the restaurant's own website — that one comes from the profile, so it needs includeDetails. A restaurant can have either source, both or neither; enable both options to capture both. Not currently billed as a separate event.
includeAvailabilitybooleanfalseFetch bookable reservation slots per restaurant: time, seating type, dining area, loyalty points, and whether a card is required (hold vs deposit). One extra request per restaurant. Needs a US proxy — like the profile route, this hangs from non-US IPs rather than erroring.
datestringtomorrowReservation date, YYYY-MM-DD.
daysAheadinteger–Check this many days from the run date (0 today, 7 a week out). Use instead of date for scheduled monitoring — a relative target stays correct every run. Setting both is rejected.
timestring19:00Reservation time, HH:MM 24-hour.
partySizeinteger2Number of diners.
cuisinesarray–Filter by cuisine, applied upstream by OpenTable. Display names (Italian, Steakhouse) are resolved to OpenTable's ids automatically; raw UUIDs also work. An unknown name is reported and ignored.
neighborhoodsarray–Filter by neighbourhood (Chelsea, Astoria). Names resolved to ids (n_hood:195, c_hood:292), which you may pass directly.
priceBandsarray–OpenTable price bands: 2 = $30 and under, 3 = $31 to $50, 4 = $50 and over. (1 returns nothing.)
awardTagsarray–Award holders, e.g. MICHELIN, MICHELIN;1 Star, 2025.
contextualPromptsarray–Occasion prompts, e.g. ROMANTIC, BRUNCH, FUN.
wheelchairAccessiblebooleanfalseOnly restaurants flagged wheelchair accessible.
searchRadiusMilesinteger–Radius around the location. Widens as well as narrows — a New York search is 637 at the default, 985 at 2 miles, 63 at half a mile. Ignored when q is set, because a term search runs nationwide.
sortstring–best / rating / distance / newestArrivals.
reviewsPerRestaurantinteger20How many reviews to fetch for each restaurant (paged automatically). Only used when reviews are enabled.
reviewsSortenumnewestReviewWhich reviews come first — one of newestReview, highestRating, lowestRating, recommended.
detailConcurrencyinteger8How many restaurants to enrich in parallel (1-20). Raise for speed, lower to be gentler on the API and your proxy.
proxyConfigurationobjectApify Proxy, RESIDENTIAL, USRoute requests through Apify Proxy. Search works from any egress, but restaurant details are geo-gated to US IPs, so the default is RESIDENTIAL with country US.

Any place OpenTable's own autocomplete recognises — US metros and international markets alike. Verified live against this Actor, 12-30 restaurants each:

Location inputReturns restaurants incountry
New York, NYManhattan and the outer boroughsUnited States
Chicago, ILChicagoUnited States
Los Angeles, CALos AngelesUnited States
Miami, FLMiamiUnited States
London, United KingdomLondonUnited Kingdom
Paris, FranceParisFrance
Berlin, GermanyBerlinGermany / Deutschland
Tokyo, JapanChuo-ku, Minato-ku, Shibuya-kuJapan / 日本
Sydney, AustraliaSydney, Surry Hills, Barangaroo, DrummoyneAustralia
Toronto, CanadaToronto, North York, Don MillsCanada
Mexico City, MexicoCiudad de MéxicoMexico / México
Dubai, United Arab EmiratesDubai, Business BayUnited Arab Emirates

Both "Boise, Idaho" and "Boise, ID" forms resolve, as do plain city names and street addresses. Note that country (and sometimes city) can come back in the local language — Tokyo rows carry either Japan or 日本 — so normalise it if you group by country.

Example input

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

What does one row look like?

A row from a run with both deep-scrape options on (the detail and review fields are absent when they are off):

{
"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=1280&height=1280",
"…+6 more sizes of the same photo"
],
"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": [
{
"name": "Gnocchi Genovese",
"description": "Potato dumplings, basil pesto, green beans",
"photoUrl": "https://resizer.otstatic.com/v4/photos/…",
"reviewMentions": 11,
"photoMentions": 3
},
"…+7 more"
],
"popularDishNames": ["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"
]
}

How much of the city did this run cover?

A search run reports its own coverage, so a short dataset is never ambiguous. The run's status message says it outright — "Returned 50 of 440 matching restaurants (11%). Raise maxItems to collect the rest." — and a SEARCH_COVERAGE record lands in the key-value store:

{ "returned": 50, "totalMatching": 440, "totalAvailable": 440,
"complete": false, "truncatedBy": "maxItems" }

truncatedBy distinguishes a maxItems cut from a spend-cap cut, so you can tell "that is every Italian restaurant in Brooklyn" from "that is the first 50 of 440".

Data table

FieldTypeFromDescription
idtextsearchOpenTable restaurant ID
nametextsearchName
urllinksearchProfile URL
cuisinesarraysearchCuisine labels
priceBandtextsearchPrice band label, localised (e.g. $31 to $50, £26 to £40)
priceBandIdtextsearchPrice band number — the market-independent one
ratingnumbersearchOverall star rating, 1-5; null with no reviews
reviewCountnumbersearchHow many diner reviews OpenTable holds
neighborhoodtextsearchNeighbourhood
neighborhoodIdtextsearchOpenTable neighbourhood ID
metroIdtextsearchOpenTable metro (market) ID
addresstextsearchStreet address
citytextsearchCity
statetextsearchState / region
postalCodetextsearchPostal code
countrytextsearchCountry, sometimes in the local language
latitudenumbersearchLatitude
longitudenumbersearchLongitude
imagesarraysearchProfile photo URLs — one photo in 8 render sizes, 105px to 1280px
photosCountnumbersearchRaw API counter; in practice 0 — count images instead
descriptiontextsearchBlurb (longer version when details are on)
featuresarraysearchBooking features, e.g. RESERVATION, ONLINE_WAITLIST, DIRECT_MESSAGING, DINER_AI, TAKEOUT_TIPS_ENABLED
firstNextAvailableTimetextsearchNext bookable slot, local time
phonetextdetailsPhone, digits only
phoneFormattedtextdetailsPhone, formatted
websitelinkdetailsWebsite
menuUrllinkdetailsExternal menu link
hourstextdetailsOpening hours
diningStyletextdetailse.g. Casual Dining, Casual Elegant
dressCodetextdetailse.g. Casual Dress, Business Casual, Resort Casual
parkingInfotextdetailse.g. Valet, Street Parking, None
paymentOptionsarraydetailse.g. AMEX, Visa, Mastercard, Discover, Diners Club
popularDishesarraydetailsOne object per dish: name, description, photoUrl, reviewMentions, photoMentions
popularDishNamesarraydetailsThe same dish names as a flat list, for simpler exports
menuPhotosCountnumberdetailsDiner-uploaded menu photos on the profile
maxPartySizenumberdetailsLargest party bookable online
maxAdvanceDaysnumberdetailsHow far ahead bookings open
isTakeoutEnabledbooleandetailsTakeout available
hasGenderNeutralRestroombooleandetailsGender-neutral restroom
currencyCodetextdetailse.g. USD
resosInLastDaynumberdetailsReservations made in the last 24 hours
menusarraymenuThe full OpenTable-hosted menu: menu → sections → items → variations, with prices, currency and the data provider
menuCountnumbermenuHow many menus this restaurant has
menuItemCountnumbermenuTotal items across all its menus
availabilitySlotsobjectavailabilityBookable slots plus minPartySize, maxPartySize, maxDaysInAdvance and noTimesReasons
reviewsFetchednumberreviewsHow many reviews this row carries
reviewsarrayreviewsThe reviews themselves — 16 fields each, below

What is in each review?

reviews[] carries these 16 fields: id, author, authorMetro (the reviewer's home market), postedDate, reservationDate, text, overallRating, food, service, ambience, value, noise, positiveFeedback, negativeFeedback, dinerIsVIP and loyaltyTier.

Which cuisines does OpenTable use?

cuisines carries OpenTable's own labels, and a restaurant can have several. An eight-city sample (New York, Chicago, Los Angeles, Miami, London, Paris, Tokyo, Sydney and Toronto) returned 68 distinct labels. The most common:

Italian · American · Japanese · French · Steakhouse · Mediterranean · Contemporary American · Korean · Steak · Chinese · Cocktail Bar · Seafood · Sushi · Mexican · Greek · Modern Australian · Spanish · Bar / Lounge / Bottle Service · Café · European · Tapas / Small Plates · Australian · Contemporary Italian · Thai · Argentinean · Bistro · Brazilian · British · Comfort Food · Fusion / Eclectic · Lebanese · Middle Eastern · Pizzeria · Pizza Bar · Pub · Gastro Pub · Southeast Asian · Vietnamese · Teppanyaki · Yakiniku · Izakaya · Sichuan · Persian · Latin American · Caribbean · Cuban · Ethiopian · German · Irish · Southern · Soul food · Vegetarian / Vegan

Set q to any of these, or to a dish (ramen) or a restaurant name (Nobu), to narrow a search. Restaurants OpenTable has not classified come back as Unspecified. Note that a brand-name q can pull in matches outside your location — searching Nobu around New York also returns Nobu Miami and Nobu Las Vegas — so filter on city or metroId if you need the results strictly local.

What do these OpenTable terms mean?

  • Price band (priceBand / priceBandId) — OpenTable's per-person spend bracket. The label is localised to the market: band 3 is $31 to $50 in the US, £26 to £40 in London, €31 to €50 in Paris, ¥5,001 to ¥10,000 in Tokyo and CAN$31 to CAN$50 in Toronto. Compare across countries on priceBandId, never on the label.
  • Metro (metroId) — OpenTable's market ID, the grouping the app calls a "metro". Every restaurant in one search area shares it, so it is a stable key for "this city": 3 is Chicago, 6 Los Angeles, 8 New York, 17 Miami, 72 London, 74 Toronto, 201 Tokyo, 255 Sydney, 3408 Paris.
  • firstNextAvailableTime — the earliest table OpenTable can seat, as local time at the restaurant. It is a snapshot at the moment of the run, not a schedule.
  • resosInLastDay — reservations booked through OpenTable in the previous 24 hours. The closest thing the API gives to a popularity signal.
  • Noise (noise on a review) — diners rate how loud the room was on the same 1-5 scale as food, service, ambience and value; a low number means quiet.
  • features — which booking mechanics the restaurant has switched on, not amenities: RESERVATION (bookable), ONLINE_WAITLIST, DIRECT_MESSAGING, DINER_AI, TAKEOUT_TIPS_ENABLED.
  • Dining style vs dress code — diningStyle is the format (e.g. Casual Dining, Casual Elegant); dressCode is what to wear (e.g. Casual Dress, Business Casual, Resort Casual).
  • maxAdvanceDays — the booking window: how many days ahead the restaurant opens tables. 14, 30, 42 and 90 are all common.

Pricing — pay per result

This actor uses pay-per-event pricing: you pay for the data it actually returns, and nothing for a slice that came back empty.

See the Pricing tab for the current per-event prices. They vary by your Apify plan, so the tab shows what you would pay — which a table here could not.

What gets billed:

  • the restaurant record — one charge per restaurant returned by a search
  • the profile — one charge per restaurant whose own profile page was read
  • the menus — one charge per restaurant that actually has an OpenTable-hosted menu
  • the reviews — charged per review returned, so you pay for the number you asked for
  • the availability check — one charge per completed check for a restaurant, date and party size

Until 2026-10-26, menus and availability are returned at no charge and reviews are billed once per restaurant rather than per review. The Pricing tab shows both the current prices and the scheduled change.

A plain search bills only the first of those. Each deep-scrape option is off by default and costs one extra request per restaurant, so you opt in to what you need.


Use the OpenTable scraper via API

Run it programmatically with the Apify API:

curl -X POST "https://api.apify.com/v2/acts/rl1987~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"
],
"apifyProxyCountry": "US"
}
}'

Or with the Apify CLI: apify call rl1987/opentable-api-scraper --input-file=input.json -o.


FAQ

Do I need an OpenTable account or API key? No. The Actor talks to OpenTable's own mobile API as an anonymous client — there is nothing to register. An Apify account is all you need.

Can I get restaurant phone numbers? Yes, with Include restaurant details on: phone (digits) and phoneFormatted. That route is geo-gated, which is why the proxy defaults to Apify Residential with country US — leave it as it is and phones work out of the box.

Does it cover restaurants outside the US? Yes. London, Paris, Berlin, Tokyo, Sydney, Toronto, Mexico City and Dubai were all verified live. Price-band labels come back in the local currency, so compare markets on priceBandId.

How many restaurants can one run return, and how do I cap the cost? The API pages 30 at a time and the Actor follows pagination until OpenTable's own total is reached. maxItems caps it (default 100, 0 = everything), and because billing is per row, that cap is also your budget cap.

Can I schedule it to track ratings or availability over time? Yes — use Apify Schedules. rating, reviewCount, resosInLastDay and firstNextAvailableTime are all snapshots at run time, so a daily run gives you the series.

What happens if my search returns nothing, or OpenTable blocks the run? Neither is treated as a crash. An empty search, an unusable input and a search OpenTable itself refuses each finish with a status message explaining what to change. If OpenTable blocks every exit IP after retries, the run says so plainly in its status rather than handing you a silent empty dataset, and if a later page fails the Actor keeps the records it already collected.

Are the images the full photo gallery? No. images is the restaurant's profile photo in eight render sizes (105px up to 1280px), not eight different photos — and photosCount from the API is 0 in practice, so ignore it.

Is scraping OpenTable legal? You are responsible for how you use the data. Collect only public restaurant data and comply with OpenTable's Terms and applicable law (e.g. GDPR/CCPA where reviews and reviewer names count as personal data).


How it works

The Actor impersonates OpenTable's official app and calls its mobile API the same way the app does: a token request, the app's autocomplete to turn your location into the coordinates OpenTable itself would search, then the paged personalizer search — plus the profile and reviews endpoints when you ask for them. Each response is normalised into the flat record above. No browser, no HTML scraping.


Good to know

  • Collect only public restaurant data and respect OpenTable's Terms of Service and robots policy.
  • Reviews carry reviewer names and home metros. For personal data, comply with GDPR/CCPA and applicable law — you are the data controller.
  • Fields can be null when OpenTable doesn't expose them for a given restaurant — rating is null before the first review, and menuUrl is often empty.

Found this useful?

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