Zomato Scraper - Restaurants, Ratings & Reviews
Pricing
from $1.00 / 1,000 restaurant scrapeds
Zomato Scraper - Restaurants, Ratings & Reviews
Scrape restaurants and reviews from zomato.com. Get names, cuisines, ratings and votes, cost for two, address and coordinates, phone, per-vertical ratings, delivery info, plus per-review rating, author, date, text, photos and tags. Search a city feed or paste URLs.
Pricing
from $1.00 / 1,000 restaurant scrapeds
Rating
0.0
(0)
Developer
Abot API
Maintained by CommunityActor stats
1
Bookmarked
14
Total users
3
Monthly active users
8 days ago
Last modified
Categories
Share
Zomato Scraper: Restaurant Profiles, Ratings & Reviews
Zomato Scraper turns Zomato into a clean restaurant data API. Search a city's Delivery or Dining Out feed, or paste restaurant, feed, or keyword-search links, and get structured JSON for every restaurant: cuisines, ratings, cost, address, coordinates, phone numbers, delivery status, and full review text. Add incremental mode for recurring monitoring, or export straight into Notion, Linear, or Airtable.
Why This Scraper?
- Two ways to find restaurants. Search one or more city slugs on the Delivery or Dining Out feed, with an optional keyword, or paste restaurant, feed, and keyword-search links directly.
- Full restaurant profiles. Cuisines, cost for two, per-vertical (Dining and Delivery) ratings, address, coordinates, phone numbers, chain name, and open/closed status, all on by default.
- Reviews included. Attach up to 2000 reviews per restaurant with rating, author, date, like count, tags, and full text, sorted the way you want.
- Offers filter. Narrow the Dining Out feed and keyword search to restaurants currently running a dining offer.
- Built for schedules. Incremental mode returns only new and changed restaurants on recurring runs, with a resume option for one interrupted run.
- Automatic mid-run checkpointing. A run that gets migrated or restarted by the platform picks up where it stopped instead of re-collecting and re-charging restaurants already pushed.
- Cost control. Cap the number of restaurants and the number of feed pages walked per run, and turn detail or review fetching off entirely if you only need the feed listing.
Use Cases
- Food delivery market research: compare cuisines, cost for two, and delivery times across a city or neighborhood.
- Reputation and review analysis: pull full review text and ratings to track sentiment for specific restaurants or chains.
- Restaurant discovery apps: feed structured listings into your own search, map, or recommendation tool.
- Competitive monitoring: schedule the actor and use incremental mode to catch new listings, rating changes, or closures over time.
- Local business directories: collect address, phone, and status data for restaurants in a city or region.
Data You Get
Sample shape: values are illustrative placeholders, not from a live record.
| Field | Example |
|---|---|
id | "18685577" |
name | "Olive Bar & Kitchen" |
url | "https://www.zomato.com/ncr/olive-bar-kitchen-mehrauli-new-delhi" |
cuisines / cuisineString | ["Italian", "Mediterranean"] / "Italian, Mediterranean" |
locality | "Mehrauli, New Delhi" |
costForTwoText | "₹4,000 for two" |
rating | { "aggregate": 4.6, "text": "4.6", "subtitle": "Very Good", "votes": 3049 } |
ratingsByVertical | { "DINING": { "rating": 4.6, "reviewCount": 3049 }, "DELIVERY": { "rating": 4.1, "reviewCount": 1200 } } |
timing | { "text": "12noon to 1am", "isOpenNow": true } |
thumbnail | image URL |
deliveryTime | "30-35 mins" |
hasOnlineOrdering / isServiceable | true / true |
hasPromo / promoOffer | true / "20% OFF" |
address | "One Style Mile, Kalka Das Marg, Mehrauli, New Delhi" (with Fetch full details) |
latitude / longitude | 28.5245 / 77.1855 (with Fetch full details) |
city / country / zipcode | "New Delhi" / "India" / "110030" (with Fetch full details) |
phones | ["+911234567890"] (with Fetch full details) |
chainName | "Olive Group" (with Fetch full details) |
statusText / isDeliveryOnly / isPermanentlyClosed | "Opens at 12noon" / false / false (with Fetch full details) |
establishments | ["Fine Dining", "Bar"] (with Fetch full details) |
reviewsCount | 1 (number of reviews captured in this record, not Zomato's total; with Fetch reviews) |
source / detailScraped | "search" / true |
changeType | "NEW" (incremental mode only) |
With Fetch reviews on (the default), each restaurant also carries a reviews array. Each entry has its own id, numeric rating and ratingText, full text, author, authorProfileUrl, authorReviewsCount, authorFollowersCount, timestamp, likeCount, commentCount, dining or delivery experience, photos, tags, and reviewUrl. This is one array field on the restaurant record, not a separate record type: reviews never appear as their own top-level dataset rows.
How to Use
- Pick a mode: Search a city (city slugs and a feed) or Paste URLs (restaurant, feed, or keyword-search links).
- For search mode, add one or more city slugs, choose Delivery or Dining Out, and optionally a keyword.
- Leave Fetch full details and Fetch reviews on for full profiles, or turn either off to save on cost.
- Set Max restaurants to control run size and cost, then click Start.
Search a city's delivery feed:
{"mode": "search","cities": ["ncr"],"context": "delivery","maxItems": 20}
Search a keyword on the Dining Out feed, offers only:
{"mode": "search","cities": ["mumbai"],"context": "dine-out","query": "pizza","offersOnly": true,"maxItems": 30}
Paste restaurant and feed URLs:
{"mode": "url","urls": ["https://www.zomato.com/ncr/olive-bar-kitchen-mehrauli-new-delhi","https://www.zomato.com/mumbai/dine-out"],"fetchReviews": true,"maxReviews": 50}
Listing only, no detail or review fetch (lowest cost per restaurant):
{"mode": "search","cities": ["dubai", "london"],"context": "delivery","fetchDetails": false,"fetchReviews": false,"maxItems": 100}
Run it from your code
Python:
from apify_client import ApifyClientclient = ApifyClient("<YOUR_APIFY_TOKEN>")run = client.actor("abotapi/zomato-scraper").call(run_input={"mode": "search", "cities": ["ncr"], "context": "delivery"})for restaurant in client.dataset(run["defaultDatasetId"]).iterate_items():print(restaurant["name"], restaurant["rating"])
JavaScript:
import { ApifyClient } from 'apify-client';const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });const run = await client.actor('abotapi/zomato-scraper').call({ mode: 'search', cities: ['ncr'], context: 'delivery' });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 this run returns only restaurants not already collected there. A separate, automatic mid-run checkpoint also protects against platform migrations without any input needed, so an interrupted run never re-pushes (or re-charges) a restaurant it already collected. - Incremental mode (
incrementalMode) is for scheduled runs over the same city, feed, or URL set. The first run returns everything asNEW. Later runs classify each restaurant asNEW,UPDATED(withchangedFields),UNCHANGED(suppressed and not billed unlessemitUnchangedis on), orEXPIRED(only after a run that scanned the full tracked scope without a cap or a resume, and only withemitExpiredon).REAPPEAREDis produced only for a restaurant that an earlier run marked as expired (which requiresemitExpiredon and a complete scan in that earlier run) and that then shows up again in any later run; it does not appear on an ordinary recurring run that never turnedemitExpiredon.stateKeynames or shares the stored state; leave it empty and the actor derives one automatically from the city/feed/URL and detail/review settings. With incremental mode off, output is exactly as before.
Send results into your apps (MCP connectors)
Optionally pipe the scraped restaurants into the apps you already use, via Model Context Protocol (MCP) connectors. This is an extra delivery step after the scrape: the Apify dataset is never changed.
What gets written to the connector: a condensed, human-readable summary of each restaurant, not the full JSON. Each item becomes one entry with a title and its key fields flattened to plain text. The complete record always stays in the Apify dataset.
- Authorize a connector once under Apify → Settings → Integrations (Notion, Linear, Airtable, or Apify).
- Select it in the "Pipe results into your apps" input field. (If the picker is empty, you haven't authorized a connector yet.)
- For Notion, also set
notionParentPageUrlto the page where restaurants should be created.
The connection is mediated by Apify's MCP proxy, so this actor never sees your third-party credentials. Leave the field empty to skip.
Input Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
mode | string | search | search (city and feed) or url (paste links). |
cities | array | none (prefill: ["ncr"]) | Zomato city slugs. If left empty in search mode, the run falls back to ncr. |
context | string | delivery | delivery or dine-out feed, for search mode. |
query | string | none | Optional cuisine, dish, or restaurant keyword to narrow the feed. |
offersOnly | boolean | false | Apply Zomato's own Offers filter. Only affects the Dining Out feed and keyword search; Delivery has no such filter. |
urls | array | none (prefill: ["https://www.zomato.com/ncr/delivery"]) | Restaurant, city-feed, or keyword-search URLs, for url mode. |
fetchDetails | boolean | true | Enrich each restaurant with address, coordinates, phone, per-vertical ratings, and taxonomy from its page. |
fetchReviews | boolean | true | Attach reviews to each restaurant. |
maxReviews | integer | 20 | Cap on reviews captured per restaurant (0 to 2000). 0 disables reviews. |
reviewsSort | string | popular | popular, newest, oldest, rating-high, or rating-low. |
maxItems | integer | 20 | Total restaurants to return across all cities or URLs. 0 = unlimited. |
maxPages | integer | none (empty/0 = unlimited) | Safety bound on feed pages walked per city or URL; the run still stops at Max restaurants or the feed's own end. |
resumeFromRunId | string | none | ID of a previous run or dataset. Restaurants already there are skipped this run (a delta). |
incrementalMode | boolean | false | Turn on for daily or recurring monitoring; returns only new and changed restaurants after the first run. |
stateKey | string | none | Optional name for an incremental-mode monitoring campaign, to keep its state stable or deliberately share it. |
emitUnchanged | boolean | false | Also return (and bill) restaurants unchanged since the last run, marked UNCHANGED. |
emitExpired | boolean | false | Also return (and bill) restaurants no longer found, marked EXPIRED, once a run has fully scanned the tracked search. |
proxy | object | { "useApifyProxy": true } | Connection settings. |
mcpConnectors | array | none | Optional: send a summary of each restaurant to apps you authorized under Integrations. |
notionParentPageUrl | string | none | Notion connector only: page under which restaurant pages are created. |
maxNotifyListings | integer | 50 | Cap on items written to each connector per run (1 to 1000). Does not affect the dataset. |
Output Example
Sample shape: values are illustrative placeholders, not from a live record.
{"id": "18685577","name": "Olive Bar & Kitchen","url": "https://www.zomato.com/ncr/olive-bar-kitchen-mehrauli-new-delhi","cuisines": ["Italian", "Mediterranean", "Continental"],"cuisineString": "Italian, Mediterranean, Continental","locality": "Mehrauli, New Delhi","costForTwoText": "₹4,000 for two","rating": { "aggregate": 4.6, "text": "4.6", "subtitle": "Very Good", "votes": 3049, "color": "3F7E00" },"ratingsByVertical": {"DINING": { "rating": 4.6, "reviewCount": 3049, "subtitle": "Very Good" },"DELIVERY": { "rating": 4.1, "reviewCount": 1200, "subtitle": "Very Good" }},"address": "One Style Mile, Kalka Das Marg, Mehrauli, New Delhi","city": "New Delhi","country": "India","latitude": 28.5245,"longitude": 77.1855,"phones": ["+911234567890"],"chainName": "Olive Group","hasPromo": true,"promoOffer": "20% OFF","detailScraped": true,"reviews": [{"id": "r123","rating": 5,"ratingText": "Excellent","author": "Jane D.","timestamp": "5 days ago","text": "Fantastic ambience and food.","likeCount": 3,"commentCount": 0,"tags": ["Great Ambience"]}],"reviewsCount": 1,"source": "search"}
Plan Requirement
The default connection setting works out of the box, and the actor rotates connections automatically when one is refused. For large or frequent runs, or for regional feeds that need a local exit, select the Residential proxy group under Connection and set a matching country. The country setting only applies together with the Residential group.
FAQ
How much does it cost?
You pay per restaurant returned, plus optional detail and review surcharges billed only when you fetch that extra data. The Pricing tab shows the current rates. Use Max restaurants and Max pages per feed to cap the cost of any run.
Is it legal to scrape Zomato?
This actor collects only publicly visible restaurant and review data; login-gated fields are not collected. You are responsible for how you use it: follow Zomato's terms and the laws that apply to you, and get legal advice if you plan commercial redistribution. Review text and author names are user-generated content and may carry their own rights.
Can I get only new or changed restaurants on a schedule?
Yes. Schedule the actor from the Schedules tab and turn on Incremental mode. Each run then returns only new and updated restaurants, and unchanged ones are not billed. Turn on Emit expired if you also want restaurants that disappeared marked EXPIRED; that only fires once a run has scanned the entire tracked search without a cap or a resume.
Why did my run return fewer restaurants than expected, or none at all?
Most causes return an empty or partial dataset rather than failing the run: a city slug or keyword with no results, a feed page that couldn't be read after retries, or Max restaurants capping the run early. Check the run's status message for the reason. The run fails outright only in two cases: an invalid resumeFromRunId (a run or dataset ID that doesn't resolve), or combining resumeFromRunId with incremental mode on a search that already has saved incremental state.
Can I use it with AI agents or MCP?
Yes. Call it from any Apify integration or MCP client, and use the connector field to push results into Notion, Linear, or Airtable.
🔗 Want more e-commerce data?
Pair this actor with these related scrapers from the same team:
| 🛒 GrabFood Restaurant Scraper Scrape GrabFood restaurants across Southeast Asia, including SG, MY, TH, VN, PH, ID, KH... | 🧩 OpenTable Reviews Scraper Scrape full OpenTable.com restaurant reviews by keyword, area, or restaurant profile... |
| 🛒 DoorDash Scraper Extract structured doordash.com data at scale. Search by keyword or paste store URLs to... | 🍷 Wolt.com From $0.8/1K. Scrape Wolt restaurants and full menus at the city scale. Extract 60+... |
| 🛒 Cdiscount Scraper Scrape product data from Cdiscount.com by keyword, category, or URL. Apply filters and... | 🛒 Ozon.ru Scraper Extract structured product data from Ozon.ru, Russia’s largest marketplace. Search by... |
💬 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.