Skip to main content
Thirdwatchthirdwatch
Food delivery

Scrape Zomato Restaurants for India Food Research (2026)

Extract Zomato restaurant data across 20 Indian cities. Ratings, cuisines, cost for two, delivery times, and addresses. Python and API recipes included.

May 26, 2026 · 6 min read · 1,272 words
See the scraper →

Thirdwatch's Zomato Scraper extracts restaurant listings from Zomato across 20 Indian cities. Each result includes name, cuisines, delivery and dining ratings, cost for two, delivery time, full address, and listing URL. Search by dish, cuisine, or browse all delivery restaurants in a city. Built for food-tech analysts, cloud kitchen operators, market researchers, and aggregator builders who need structured India restaurant data programmatically.

Why scrape Zomato for India food research

Zomato is India's largest restaurant discovery and food delivery platform. According to Zomato's FY2025 annual report, the platform lists over 300,000 active restaurant partners across 800+ Indian cities, processing 2.5 million+ daily food delivery orders. For anyone researching India's restaurant industry, Zomato is the single richest public data surface available.

The problem: Zomato retired its public API for third-party developers years ago. The Partner API is gated to enterprise restaurant partners only. That leaves manual browsing or structured scraping as the only paths to systematic data collection. A food-tech startup benchmarking competitor pricing across Bangalore needs cost-for-two distributions by cuisine and locality. A cloud kitchen operator identifying underserved cuisines in Hyderabad needs delivery rating distributions by area. An investor building a restaurant supply-side model for an India F&B fund needs structured listings at city scale. All of these reduce to: query Zomato, return structured rows, analyze in a dataframe. The actor is the data layer.

How does this compare to the alternatives?

Three paths to getting Zomato restaurant data into a pipeline:

Approach Reliability Setup time Maintenance Pricing model
DIY Python scraper Brittle without anti-bot handling 2-4 weeks You own proxy rotation and site changes Free compute, your dev time
tugkan/zomato-scraper (Apify) Established 5 minutes Vendor maintains Flat per-run fee (~$10/run)
Thirdwatch Zomato Scraper Production-tested across 20 cities 5 minutes Thirdwatch tracks Zomato changes Pay per restaurant returned

The DIY route is deceptively expensive. Zomato serves different responses based on request fingerprinting and geo-location, and the site structure changes frequently. Zomato has also aggressively shut down third-party data access since retiring its public API — scrapers that worked six months ago often fail on current page structures. The Thirdwatch actor handles anti-bot work, proxy rotation, and ongoing maintenance against Zomato's evolving defenses so you pay only for the data you receive, not for failed requests or infrastructure overhead. For researchers who previously used the Zomato API before it was retired, this actor provides the closest equivalent to programmatic restaurant data access.

How to scrape Zomato restaurants in 4 steps

Step 1: How do I set up Apify authentication?

Sign in at apify.com (free tier, no credit card required), go to Settings, then Integrations, and copy your personal API token. Every example below assumes the token is stored in APIFY_TOKEN:

export APIFY_TOKEN="apify_api_xxxxxxxxxxxxxxxx"

Step 2: How do I search restaurants by dish or cuisine in a specific city?

Pass dish or cuisine names in queries, set city to one of the 20 supported Indian cities, and control volume with maxResults. Leave queries empty to browse all delivery restaurants in that city.

import os, requests, pandas as pd

ACTOR = "thirdwatch~zomato-scraper"
TOKEN = os.environ["APIFY_TOKEN"]

resp = requests.post(
    f"https://api.apify.com/v2/acts/{ACTOR}/run-sync-get-dataset-items",
    params={"token": TOKEN},
    json={
        "queries": ["biryani", "pizza"],
        "city": "bangalore",
        "maxResults": 50,
        "deliveryOnly": True,
    },
    timeout=3600,
)
df = pd.DataFrame(resp.json())
print(f"{len(df)} restaurants across {df.cuisine.apply(len).mean():.1f} avg cuisines")

Two queries across Bangalore with 50 results each yields up to 100 restaurant records. The deliveryOnly flag filters to restaurants currently accepting online orders.

Step 3: How do I compare delivery vs dining ratings across restaurants?

Zomato uniquely provides separate delivery and dining ratings. Use both to identify restaurants where delivery experience diverges from dine-in quality.

df["delivery_score"] = pd.to_numeric(df["delivery_rating"], errors="coerce")
df["dining_score"] = pd.to_numeric(df["dining_rating"], errors="coerce")
df["rating_gap"] = df["dining_score"] - df["delivery_score"]

# Restaurants where dining is much better than delivery — packaging/logistics issues
problem_delivery = df[df["rating_gap"] > 0.5].sort_values("rating_gap", ascending=False)
print(problem_delivery[["name", "delivery_score", "dining_score", "rating_gap",
                         "cost_for_two", "location"]].head(10))

A rating gap above 0.5 between dining and delivery scores often signals packaging or logistics problems — actionable intelligence for cloud kitchen operators or delivery optimization consultants.

Step 4: How do I schedule recurring Zomato data pulls?

Set up an Apify schedule to refresh your dataset at a regular cadence. The webhook fires on completion, pushing fresh data to your pipeline.

curl -X POST "https://api.apify.com/v2/schedules?token=$APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "zomato-bangalore-biryani-daily",
    "cronExpression": "0 8 * * *",
    "timezone": "Asia/Kolkata",
    "isEnabled": true,
    "actions": [{
      "type": "RUN_ACTOR",
      "actorId": "thirdwatch~zomato-scraper",
      "runInput": {
        "queries": ["biryani", "pizza", "north-indian"],
        "city": "bangalore",
        "maxResults": 100,
        "deliveryOnly": true
      }
    }]
  }'

Add an ACTOR.RUN.SUCCEEDED webhook pointing at your data warehouse ingestion endpoint and the pipeline runs itself — every morning at 8 AM IST, fresh Bangalore restaurant data lands in your system.

Sample output

A single record from the dataset for a Bangalore biryani restaurant looks like this:

{
  "name": "Meghana Foods",
  "cuisine": ["Biryani", "Andhra", "North Indian", "Chinese"],
  "rating": 4.2,
  "rating_count": "11.7K",
  "delivery_rating": 4.2,
  "delivery_rating_count": "10.5K",
  "dining_rating": 4.2,
  "dining_rating_count": "1,197",
  "cost_for_two": "₹1,000 for two",
  "cost_for_one": "₹400 for one",
  "delivery_time": "29 min",
  "is_serviceable": true,
  "has_online_ordering": true,
  "location": "St. Marks Road, Bangalore",
  "address": "18-22, Vasavi Complex, St. Marks Road, Bangalore",
  "city": "bangalore",
  "image_url": "https://b.zmtcdn.com/data/pictures/...",
  "url": "https://www.zomato.com/bangalore/meghana-foods-residency-road/order",
  "restaurant_id": "19282473",
  "is_promoted": false,
  "query": "biryani"
}

cuisine is a clean array of strings, ready for multi-label analysis without parsing. delivery_rating and dining_rating are separate numeric scores with independent vote counts — Zomato is one of the few platforms that surfaces both dimensions. is_promoted flags paid placements so you can filter them out of organic ranking analysis. cost_for_two is the formatted Indian Rupee string as Zomato displays it.

Common pitfalls

Three things go wrong in production Zomato data pipelines. City slug mismatch — the city input expects lowercase slugs like bangalore, not Bengaluru or BLR. The 20 supported slugs are listed in the actor's input schema; passing an unsupported city returns zero results silently. Rating type confusion — Zomato's top-level rating is an aggregate, while delivery_rating and dining_rating are independent axes. Averaging all three double-counts the aggregate. Pick one axis per analysis. Promoted listing contamination — Zomato injects paid placements into search results. The is_promoted flag lets you filter these out, but forgetting to do so skews ranking and pricing analysis toward restaurants with advertising budgets.

Thirdwatch's actor handles the anti-bot work and proxy rotation so you can focus on the data. The actor pre-configures India-based proxy routing and manages request pacing automatically.

A fourth issue for quantitative research: cost_for_two normalization. Zomato displays prices in formatted strings ("₹1,000 for two"), not as clean integers. The scraper preserves the original format. For numerical analysis, parse the string to extract the numeric value (strip the currency symbol and commas) and note that Zomato's cost-for-two is an estimate, not a menu-derived calculation. It represents the platform's assessment of what a typical two-person meal costs including taxes but excluding delivery fees. For cross-city comparisons, this metric is directionally reliable but should not be treated as a precise price point. A restaurant showing "₹400 for two" in Bangalore may serve an equivalent meal for "₹250 for two" in Indore — the difference reflects local cost-of-living, not portion size or cuisine quality.

Related use cases

Frequently asked questions

Does the Zomato Scraper work outside India?

The actor currently covers 20 major Indian cities only. Zomato's international markets are not supported. For Middle East food delivery data, see the Talabat and Deliveroo scrapers.

Can I get full restaurant menus from Zomato?

Yes. Set includeMenu to true in the input. The actor fetches menu items per restaurant with an extra request each, so runs with menus enabled are slower but return dish-level data.

How fresh is the Zomato data?

Data is pulled live at run time from Zomato's public pages. Ratings, delivery times, and pricing reflect whatever Zomato shows at the moment of the scrape. Schedule runs daily or hourly for near-real-time monitoring.

Does Zomato have a public API I can use instead?

Zomato retired its public API for third-party developers. The only official data access is through their Partner API, restricted to enterprise restaurant partners. This scraper gives any buyer structured discovery data without needing partner status.

What is the difference between delivery_rating and dining_rating?

Zomato maintains two separate rating systems per restaurant. delivery_rating reflects online-order customer satisfaction, while dining_rating reflects dine-in experience. Both are returned with their respective vote counts for independent analysis.

Related

Try it yourself

100 free credits, no credit card.

About 30 real searches. Add the MCP to Claude or Cursor in two minutes.