GrabFood Restaurant Scraper: Menus, Ratings & Delivery Data avatar

GrabFood Restaurant Scraper: Menus, Ratings & Delivery Data

Pricing

from $1.00 / 1,000 restaurant results

Go to Apify Store
GrabFood Restaurant Scraper: Menus, Ratings & Delivery Data

GrabFood Restaurant Scraper: Menus, Ratings & Delivery Data

Scrape GrabFood restaurants across Southeast Asia, including SG, MY, TH, VN, PH, ID, KH, and MM. Search by keyword or URL and extract 45+ fields: restaurant name, address, GPS, cuisine, ratings, reviews, promos, delivery time, fees, opening hours, and full menus with item prices and images.

Pricing

from $1.00 / 1,000 restaurant results

Rating

0.0

(0)

Developer

Abot API

Abot API

Maintained by Community

Actor stats

1

Bookmarked

54

Total users

14

Monthly active users

a day ago

Last modified

Share

GrabFood Restaurants Scraper: Menus, Ratings, Delivery Fees and Promos

GrabFood Restaurants Scraper turns GrabFood, the food delivery arm of Grab, into structured restaurant and menu data across 8 Southeast Asian countries: Singapore, Malaysia, Thailand, Vietnam, Philippines, Indonesia, Cambodia and Myanmar. Get restaurant names, addresses and GPS coordinates, cuisine tags, star ratings and review counts, delivery time and fees, opening hours, promotions and price tiers, plus the complete menu (categories, items, descriptions, prices and images) when you turn it on. Search by keyword within a region, or paste GrabFood links, then export to JSON, CSV or Excel, or pull results straight into your app through the API.

Why This Scraper?

  • Two ways to find restaurants. Search by keyword inside a region, or paste ready made GrabFood restaurant pages and listing pages directly.
  • Full menu on demand. Turn on menu extraction and get every category and item, with description, price, discounted price, takeaway price, image and modifier group count.
  • 8 regions, one actor. Singapore, Malaysia, Thailand, Vietnam, Philippines, Indonesia, Cambodia and Myanmar, each with prices in its own local currency.
  • Delivery detail that matters for ordering. Delivery time, delivery fee (with any discount applied), delivery options such as delivery, takeaway or dine in, and distance from the search point.
  • Structured promotions. Promo labels come through as readable text, for example "S$5.00 off", not just a yes or no flag.
  • Filters and sorting built in. Keep only restaurants matching a cuisine tag, a minimum rating or a minimum review count, then sort by rating, review count, distance or delivery time.
  • Built for schedules. Turn on incremental mode to get only new, updated and reappeared restaurants on every recurring run, and a run that cannot read GrabFood fails loudly instead of quietly returning an empty dataset.

Use Cases

  • Delivery aggregation and comparison apps: pull live menus, prices and delivery fees to compare against other platforms.
  • Market and competitor research: track promotions, price tiers and ratings across cuisines, chains and cities.
  • Menu and pricing analytics: build a historical menu and price dataset for a chain across countries.
  • Local lead generation: find restaurants by cuisine, rating or review count to build outreach or partnership lists.
  • Daily monitoring: watch a search or a list of restaurants for new promotions, rating changes or menu updates.

Data You Get

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

FieldExample
id"SGDD00000"
name"Sample Burger Place"
chainName"Sample Burger"
cuisine["Burger", "Fast Food", "Halal"]
fullAddress"1 Sample Street, #01-01"
city / countryCode"Singapore" / "SG"
latitude / longitude1.2880 / 103.8520
rating4.3
voteCount1200
priceTag2
isOpentrue
estimatedDeliveryTime35
distanceInKm1.34
hasPromotrue
promoLabels["S$5.00 off"]
deliveryOptions"DELIVERY_TAKEAWAY_DINEIN"
deliveryFee{ price, priceDisplay, discountedPrice, hasDiscountedPrice }
openHoursper day hours plus a displayedHours summary string
menuarray of { name, itemCount, items: [{ name, price, discountedPrice, imageUrl, modifierGroupCount }] }
menuCategoryCount / menuItemCount12 / 86
grabUrl"https://food.grab.com/sg/en/restaurant/sample-burger-place/SGDD00000"
region"SG"
scrapedAtISO timestamp

Prices are in the region's currency, in minor units (for example cents), with a formatted display string where GrabFood provides one. Rating and review count come from GrabFood's own aggregate score and rating count on its public web pages; the public site does not expose individual written reviews, so this actor does not invent any. Incremental mode adds changeType (NEW, UPDATED, UNCHANGED, REAPPEARED or EXPIRED), changedFields, firstSeenAt and lastSeenAt to every record; these are absent from a normal run's output.

How to Use

  1. Pick a mode: search (keyword within a region) or url (paste GrabFood links).
  2. Fill in the fields for that mode: region and search keywords for search mode, or a list of GrabFood URLs for URL mode. Add cuisine, rating or review filters and a sort order if you want them.
  3. Turn Fetch full menu on or off, and set Max restaurants to control run size and cost.
  4. Click Start, then download the dataset as JSON, CSV or Excel, or read it through the API.

Search for a keyword in one region:

{
"mode": "search",
"region": "SG",
"searchQueries": ["ramen"],
"maxItems": 30
}

Browse a region's recommended feed:

{
"mode": "url",
"startUrls": ["https://food.grab.com/th/en/restaurants"],
"maxItems": 50
}

Paste a GrabFood listing URL:

{
"mode": "url",
"startUrls": ["https://food.grab.com/sg/en/restaurants?search=sushi"]
}

Filtered and sorted search:

{
"mode": "search",
"region": "MY",
"searchQueries": ["burger"],
"cuisineFilter": ["halal"],
"minRating": 4,
"sortBy": "rating",
"maxItems": 25
}

Run it from your code

Python:

from apify_client import ApifyClient
client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("abotapi/grabfood-restaurants-scraper").call(run_input={"mode": "search", "region": "SG", "searchQueries": ["ramen"], "maxItems": 30})
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/grabfood-restaurants-scraper').call({ mode: 'search', region: 'SG', searchQueries: ['ramen'], maxItems: 30 });
const { items } = await client.dataset(run.defaultDatasetId).listItems();

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

How Max restaurants and Max pages work together

Max restaurants (maxItems) is the main cap on a run, 20 by default; set it to 0 to remove your cap (a single run still stops at 1,000 restaurants). Max pages (maxPages) additionally caps how many listing pages get walked per keyword search (the recommended feed is not affected); it is 0 by default, meaning no page limit, so the run stops at Max restaurants instead. Set Max pages only when you want to bound page walking regardless of Max restaurants.

Resume and recurring updates

  • Resume (resumeFromRunId) continues one specific interrupted or previous large pull: paste its run or dataset ID and this run skips restaurants already collected there, so you don't pay twice.
  • Incremental mode (incrementalMode) is for a schedule, for example daily, over the same search or URLs. The actor remembers the previous run of the same search by itself, no run ID needed. The first run returns everything as NEW. Later runs return only NEW, UPDATED and REAPPEARED restaurants by default; unchanged restaurants are suppressed and not billed unless emitUnchanged is on. EXPIRED rows are only produced once a run has fully scanned the tracked search, not when Max restaurants capped it or Resume was used. stateKey names or shares a monitoring campaign.

Send results into your apps (MCP connectors)

Optionally pipe the scraped results 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.

  1. Authorize a connector once under Apify → Settings → Integrations (Notion, Linear, Airtable, or Apify).
  2. Select it in the "Pipe results into your apps" input field. If the picker is empty, you haven't authorized a connector yet.
  3. For Notion, also set notionParentPageUrl to the page where items 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

ParameterTypeDefaultDescription
modestringsearchsearch (keyword) or url (paste GrabFood links).
regionstringSGGrabFood country: SG, MY, TH, VN, PH, ID, KH or MM. Results and prices are for that country.
searchQueriesarray(none, prefilled with ramen)Keywords to search within the region. Search mode only. To browse a region's recommended feed, use URL mode with a listing URL that has no search term.
startUrlsarray(none, prefilled with a sample URL)GrabFood restaurant pages or restaurants listing pages. URL mode only.
cuisineFilterarray(empty)Keep only restaurants whose cuisine tags contain any of these terms, case insensitive. Leave empty to include all.
minRatinginteger(none)Minimum star rating, 0 to 5. Leave empty for no minimum.
minReviewsinteger0Minimum number of ratings a restaurant must have.
sortBystringdefaultdefault (GrabFood's own order), rating, reviews, distance or delivery-time.
fetchMenubooleantrueEnrich each restaurant with its full menu and full address. Turn off for faster, listing only runs.
includeRatingsbooleantruePopulate the rating score and review count on every record.
maxItemsinteger20Hard cap on total restaurants for the run. 0 removes the cap; a single run still stops at 1,000 restaurants.
maxPagesinteger0Extra cap on how many listing pages are walked per search. 0 means no page limit; the run stops at Max restaurants instead.
proxyConfigurationobjectApify Proxy, ResidentialConnection settings. A residential connection is recommended for reliable listing loads.
mcpConnectorsarray(none)Optional: send a summary of each record to apps you authorized under Integrations.
notionParentPageUrlstring(none)Notion connector only: the page under which item pages are created.
maxNotifyListingsinteger50Cap on items written to each connector per run. Does not affect the dataset.
resumeFromRunIdstring(none)Continue one interrupted or previous large pull without re-collecting or re-charging restaurants already saved there.
incrementalModebooleanfalseTurn on for scheduled runs: return only new, updated and reappeared restaurants after the first run.
stateKeystring(none)Name or share an incremental mode monitoring campaign. Leave empty to derive one automatically from the mode, region, search or URL, and filter setup.
emitUnchangedbooleanfalseAlso return, and bill, restaurants that have not changed since the last run. Incremental mode only.
emitExpiredbooleanfalseAlso return, and bill, restaurants no longer found after a complete scan. Incremental mode only.

Output Example

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

{
"id": "SGDD00000",
"name": "Sample Burger Place",
"chainName": "Sample Burger",
"branchName": "Downtown",
"cuisine": ["Burger", "Fast Food", "Halal"],
"address": "Sample Burger Place - Downtown",
"fullAddress": "1 Sample Street, #01-01",
"street": "Sample Street",
"suburb": "Downtown",
"postcode": "000000",
"city": "Singapore",
"countryCode": "SG",
"latitude": 1.288,
"longitude": 103.852,
"rating": 4.3,
"voteCount": 1200,
"priceTag": 2,
"isOpen": true,
"estimatedDeliveryTime": 35,
"estimatedDeliveryTimeRange": "From 35 mins",
"distanceInKm": 1.34,
"hasPromo": true,
"promoLabels": ["S$5.00 off"],
"deliveryOptions": "DELIVERY_TAKEAWAY_DINEIN",
"deliveryFee": {
"currencyCode": "SGD",
"currencySymbol": "S$",
"price": 200,
"priceDisplay": "S$2.00",
"discountedPrice": 0,
"hasDiscountedPrice": true
},
"currency": { "code": "SGD", "symbol": "S$", "exponent": 2 },
"openHours": { "mon": "10:00-22:00", "tue": "10:00-22:00", "displayedHours": "Daily 10:00-22:00" },
"photoHref": "https://food.grab.com/sample/photo.jpg",
"grabUrl": "https://food.grab.com/sg/en/restaurant/sample-burger-place/SGDD00000",
"menuCategoryCount": 12,
"menuItemCount": 86,
"menu": [
{
"name": "Burgers",
"available": true,
"itemCount": 1,
"items": [
{
"id": "ITEM0001",
"name": "Classic Cheeseburger",
"description": "Beef patty, cheddar, lettuce, house sauce",
"available": true,
"price": 890,
"discountedPrice": 690,
"takeawayPrice": 850,
"imageUrl": "https://food.grab.com/sample/burger.jpg",
"modifierGroupCount": 2,
"isOnSpecial": true,
"savingsAmount": 200,
"discountPercent": 22
}
]
}
],
"orderValueLimit": 1500,
"announcements": ["Public holiday hours apply"],
"region": "SG",
"scrapedAt": "2026-01-01T00:00:00.000Z"
}

Plan Requirement

This actor uses Apify Proxy with the Residential group by default, already set as the input's default connection. A residential connection is recommended for reliable listing loads; datacenter connections may be rejected by the site. The proxy's exit country is aligned automatically to the selected region.

FAQ

How much does it cost?

This actor charges per event: once when a run starts, once for each restaurant returned, and a small additional charge for each restaurant enriched from its own restaurant page (always in URL mode, and in search mode when Fetch full menu is on). The Pricing tab shows the current rates. Use Max restaurants to cap the cost of any run.

This actor collects only publicly available restaurant and menu data from GrabFood's own web pages. You are responsible for how you use it: follow GrabFood's terms and the laws that apply to you, and get legal advice if you plan commercial redistribution.

Can I get only new or changed restaurants on a schedule?

Yes. Schedule the actor from the Schedules tab with the same search or URLs and turn on Incremental mode. Each run then returns only NEW, UPDATED and REAPPEARED restaurants; unchanged ones are skipped and not billed unless Emit unchanged is on.

What's the difference between Resume and Incremental mode?

Resume (resumeFromRunId) continues one specific interrupted or capped run so you don't re-collect, or re-pay for, restaurants it already saved. Incremental mode is for running the same search again and again on a schedule and getting only what changed; it remembers the previous run by itself, no run ID needed.

Why did my run fail instead of returning an empty dataset?

The run fails outright when it cannot confirm anything from GrabFood: no restaurants were found and a search or listing request failed, the guest session could not be set up or gets rejected twice in a row, a given resumeFromRunId does not match a run or dataset this account can read, or Incremental mode is combined with resumeFromRunId on a search that already has saved state. A genuinely empty result, such as a keyword with no matches or filters that exclude everything, still succeeds with zero rows and a warning in the run log.

Does this actor return individual written reviews?

No. GrabFood's public listings expose only the aggregate rating and the total review count for a restaurant, not individual written reviews, so this actor returns that rating data and does not fabricate review text the site does not publish.

Can I use it with AI agents or MCP?

Yes. Call it from any Apify integration or MCP client, and use the mcpConnectors field to push a summary of each restaurant into Notion, Linear or Airtable.

🔗 Want more e-commerce data?

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

🍷 Wolt.com
From $0.8/1K. Scrape Wolt restaurants and full menus at the city scale. Extract 60+...
🛒 Zomato Restaurants & Reviews Scraper
Scrape restaurants and reviews from zomato.com. Get names, cuisines, ratings and votes...
🛒 DoorDash Scraper
Extract structured doordash.com data at scale. Search by keyword or paste store URLs to...
✈️ Naver Map Scraper
From $1/1K. Scrape Naver Map places by keyword or URL: names, categories, ratings...
🛒 Lazada
Scrape Lazada products and reviews across SEA markets, including Malaysia, Singapore...
🧩 Resy Scraper
Scrape Resy restaurants and live reservation availability by city or URL. Get bookable...

👉 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.