Discover/Grab API
live

Grab APIfood.grab.com

Search GrabFood restaurants by location and retrieve full menus with prices, categories, and modifier options via two structured endpoints.

This API takes change requests — .
Endpoints
2
Updated
1mo ago

What is the Grab API?

The GrabFood API gives developers access to two endpoints — search_restaurants and get_merchant_menu — covering restaurant listings and full menu data across GrabFood regions including Indonesia, Singapore, and Thailand. search_restaurants returns up to paginated batches of restaurants with ratings, delivery fees, estimated delivery times, and distance, while get_merchant_menu returns every menu item across all categories for a given merchant, including prices in both display and minor-unit formats.

This call costs1 credit / call— charged only on success
Try it
Latitude,longitude for location-based search (comma-separated, e.g. '-6.1767352,106.826504').
Pagination offset (increment by page_size for next page).
Search keyword to filter restaurants by name or cuisine.
Number of results per page.
Guest authentication JWT from browser sessionStorage('guest_token') on food.grab.com. Valid for 30 days.
ISO country code for the GrabFood region (e.g. 'ID' for Indonesia, 'SG' for Singapore, 'TH' for Thailand).
api.parse.bot/scraper/8c488b01-45f9-416b-a0f2-11dd465bf552/<endpoint>
Ready to send
Fill in the parameters and hit sign in to send to see live response data here.
Call it over HTTPgrab a free API key at signup
curl -X GET 'https://api.parse.bot/scraper/8c488b01-45f9-416b-a0f2-11dd465bf552/search_restaurants?KEYWORD=burger&keyword=burger&PAGE_SIZE=3&page_size=5&GUEST_TOKEN=REDACTED_TOKEN&guest_token=REDACTED_TOKEN' \
  -H 'X-API-Key: $PARSE_API_KEY'
Python SDK · recommended

Typed, relational, agent-ready

A generated client with real types, enums, and the links between objects — the structure a flat JSON response can't carry. Autocompletes in your editor and reads cleanly to coding agents.

  • Fully typed · autocompletes
  • Objects link to objects
  • Typed errors & pagination

Typed Python client. Set up the SDK in your uv project, then pull this API’s typed client:

uv add parse-sdk
uv run parse init
uv run parse add --marketplace food-grab-com-api

uv run parse add --marketplace pulls a pinned snapshot of this canonical API — it won’t change underneath you. To customize it, subscribe and swap to your own copy.

"""
GrabFood Indonesia API Client
Search restaurants and retrieve full menu listings from GrabFood Indonesia.
Get your API key from: https://parse.bot/settings
"""

import os
import requests
from typing import Optional, Any, Dict, List


class ParseClient:
    """Client for interacting with the GrabFood Indonesia API via Parse.bot."""

    def __init__(self, api_key: Optional[str] = None):
        """Initialize the Parse API client.

        Args:
            api_key: API key for authentication. If not provided, reads from PARSE_API_KEY env var.
        """
        self.base_url = "https://api.parse.bot"
        self.scraper_id = "8c488b01-45f9-416b-a0f2-11dd465bf552"
        self.api_key = api_key or os.getenv("PARSE_API_KEY")
        if not self.api_key:
            raise ValueError("API key must be provided or set in PARSE_API_KEY environment variable")

    def _call(self, endpoint: str, method: str = "POST", **params) -> Dict[str, Any]:
        """Make an API call to the Parse.bot scraper.

        Args:
            endpoint: The endpoint name (e.g., 'search_restaurants')
            method: HTTP method ('GET' or 'POST')
            **params: Parameters to pass to the endpoint

        Returns:
            The JSON response from the API

        Raises:
            requests.exceptions.RequestException: If the API call fails
        """
        url = f"{self.base_url}/scraper/{self.scraper_id}/{endpoint}"
        headers = {
            "X-API-Key": self.api_key,
            "Content-Type": "application/json"
        }

        if method.upper() == "GET":
            response = requests.get(url, headers=headers, params=params)
        else:
            response = requests.post(url, headers=headers, json=params)

        response.raise_for_status()
        return response.json()

    def search_restaurants(
        self,
        guest_token: str,
        latlng: str = "-6.1767352,106.826504",
        keyword: str = "",
        offset: int = 0,
        page_size: int = 32,
        country_code: str = "ID"
    ) -> Dict[str, Any]:
        """Search for GrabFood restaurants by location and optional keyword.

        Args:
            guest_token: Guest authentication token from browser sessionStorage
            latlng: Latitude,longitude for location-based search
            keyword: Search keyword to filter restaurants
            offset: Pagination offset
            page_size: Number of results per page
            country_code: Country code (default: ID for Indonesia)

        Returns:
            Dictionary containing total_count, offset, page_size, and restaurants list
        """
        return self._call(
            "search_restaurants",
            method="GET",
            guest_token=guest_token,
            latlng=latlng,
            keyword=keyword,
            offset=offset,
            page_size=page_size,
            country_code=country_code
        )

    def get_merchant_menu(
        self,
        guest_token: str,
        merchant_id: str,
        latlng: str = "-6.1767352,106.826504"
    ) -> Dict[str, Any]:
        """Get the full menu for a specific GrabFood merchant/restaurant.

        Args:
            guest_token: Guest authentication token from browser sessionStorage
            merchant_id: Merchant ID from search results
            latlng: Latitude,longitude for location context

        Returns:
            Dictionary containing merchant info and menu_items list
        """
        return self._call(
            "get_merchant_menu",
            method="GET",
            guest_token=guest_token,
            merchant_id=merchant_id,
            latlng=latlng
        )


def format_price(price_str: str) -> float:
    """Convert price display string to numeric value for comparison."""
    return float(price_str.replace("Rp", "").replace(".", "").strip())


def analyze_restaurant_menu(client: ParseClient, guest_token: str, restaurant: Dict[str, Any]) -> None:
    """Analyze and display menu analysis for a single restaurant."""
    print(f"\n{'='*80}")
    print(f"📊 MENU ANALYSIS: {restaurant['name']}")
    print(f"{'='*80}")

    menu_data = client.get_merchant_menu(
        guest_token=guest_token,
        merchant_id=restaurant['id']
    )

    # Basic info
    print(f"📍 Location: {menu_data['address']}")
    print(f"🍽️  Cuisine: {menu_data['cuisine']}")
    print(f"📋 Total Menu Items: {menu_data['total_menu_items']}")

    # Availability stats
    available_count = sum(1 for item in menu_data['menu_items'] if item['available'])
    top_sellers = [item for item in menu_data['menu_items'] if item['top_seller']]

    print(f"\n📊 Statistics:")
    print(f"   • Available items: {available_count}/{len(menu_data['menu_items'])}")
    print(f"   • Top sellers: {len(top_sellers)}")

    # Price analysis
    if menu_data['menu_items']:
        prices = []
        discounted_items = []

        for item in menu_data['menu_items']:
            if item['price_minor_unit']:
                prices.append(item['price_minor_unit'])
            if item['discounted_price_display']:
                discounted_items.append(item)

        if prices:
            min_price = min(prices) / 100_000
            max_price = max(prices) / 100_000
            avg_price = sum(prices) / len(prices) / 100_000
            print(f"   • Price range: Rp {min_price:.0f}K - Rp {max_price:.0f}K")
            print(f"   • Average price: Rp {avg_price:.0f}K")
            print(f"   • Items on sale: {len(discounted_items)}")

    # Display top sellers
    if top_sellers:
        print(f"\n🔥 Top Sellers (showing first 3):")
        for item in top_sellers[:3]:
            price = item['price_display']
            if item['discounted_price_display']:
                price = f"{item['discounted_price_display']} (was {item['price_display']})"
            print(f"   • {item['name']}: {price}")

    # Show categories and sample items
    print(f"\n📂 Menu Categories and Samples:")
    items_by_category = {}
    for item in menu_data['menu_items']:
        category = item['category']
        if category not in items_by_category:
            items_by_category[category] = []
        items_by_category[category].append(item)

    for category in menu_data['menu_categories'][:5]:  # Show first 5 categories
        if category in items_by_category:
            print(f"\n   {category} ({len(items_by_category[category])} items)")
            for item in items_by_category[category][:2]:  # Show first 2 items per category
                available_mark = "✓" if item['available'] else "✗"
                print(f"      {available_mark} {item['name'][:50]}")


if __name__ == "__main__":
    # Initialize the client
    client = ParseClient()

    # Replace with your actual guest token from GrabFood browser session
    # You can extract this from browser DevTools: sessionStorage.getItem('guest_token')
    GUEST_TOKEN = "your_guest_token_here"

    print("🚀 GrabFood Indonesia Restaurant Analyzer")
    print("=" * 80)

    try:
        # Step 1: Search for restaurants with a keyword
        print("\n🔍 Step 1: Searching for fried chicken restaurants in Jakarta...")
        search_results = client.search_restaurants(
            guest_token=GUEST_TOKEN,
            keyword="ayam goreng",
            page_size=5
        )

        total_found = search_results['total_count']
        restaurants = search_results['restaurants']

        print(f"✓ Found {total_found} total restaurants (showing {len(restaurants)} results)")

        # Step 2: Display search results summary
        print("\n" + "=" * 80)
        print("📋 SEARCH RESULTS SUMMARY")
        print("=" * 80)

        for i, restaurant in enumerate(restaurants, 1):
            print(f"\n{i}. {restaurant['name']}")
            print(f"   ID: {restaurant['id']}")
            print(f"   ⭐ Rating: {restaurant['rating']}/5 ({restaurant['vote_count']:,} votes)")
            print(f"   📏 Distance: {restaurant['distance_km']} km")
            print(f"   ⏱️  Delivery: {restaurant['estimated_delivery_time']} min")
            print(f"   💵 Delivery Fee: {restaurant['delivery_fee_display']}")
            print(f"   🍗 Cuisine: {', '.join(restaurant['cuisine'])}")
            if restaurant.get('promo'):
                print(f"   🎉 Promo: {restaurant['promo']}")
            print(f"   🕌 Halal: {'Yes' if restaurant['halal'] else 'No'}")

        # Step 3: Analyze menus for top restaurants
        print("\n\n" + "=" * 80)
        print("📊 DETAILED MENU ANALYSIS")
        print("=" * 80)

        # Analyze top 2 restaurants
        for restaurant in restaurants[:2]:
            try:
                analyze_restaurant_menu(client, GUEST_TOKEN, restaurant)
            except Exception as e:
                print(f"\n⚠️  Could not retrieve menu for {restaurant['name']}: {str(e)}")

        # Step 4: Comparison summary
        print("\n\n" + "=" * 80)
        print("📊 COMPARISON SUMMARY")
        print("=" * 80)

        print("\nRanking by Rating:")
        sorted_by_rating = sorted(restaurants, key=lambda x: x['rating'], reverse=True)
        for i, restaurant in enumerate(sorted_by_rating[:3], 1):
            print(f"   {i}. {restaurant['name']}: ⭐ {restaurant['rating']} ({restaurant['vote_count']} votes)")

        print("\nRanking by Delivery Time:")
        sorted_by_time = sorted(restaurants, key=lambda x: x['estimated_delivery_time'])
        for i, restaurant in enumerate(sorted_by_time[:3], 1):
            print(f"   {i}. {restaurant['name']}: ⏱️  {restaurant['estimated_delivery_time']} min")

        print("\nRanking by Delivery Fee:")
        def extract_fee(fee_str):
            return float(fee_str.replace("Rp", "").replace(".", "").strip())

        sorted_by_fee = sorted(restaurants, key=lambda x: extract_fee(x['delivery_fee_display']))
        for i, restaurant in enumerate(sorted_by_fee[:3], 1):
            print(f"   {i}. {restaurant['name']}: {restaurant['delivery_fee_display']}")

        print("\n✅ Analysis completed successfully!")

    except ValueError as e:
        print(f"❌ Configuration Error: {e}")
        print("   Please set your API key: export PARSE_API_KEY='your_api_key_here'")
    except requests.exceptions.RequestException as e:
        print(f"❌ API Error: {e}")
        print("   Please verify your guest token and try again.")
    except Exception as e:
        print(f"❌ Unexpected error: {e}")
All endpoints · 2 totalmissing one? ·

Search and list GrabFood restaurants by location with optional keyword filter. Returns paginated results with restaurant details including name, cuisine, rating, delivery fee, and location.

Input
ParamTypeDescription
latlngstringLatitude,longitude for location-based search (comma-separated, e.g. '-6.1767352,106.826504').
offsetintegerPagination offset (increment by page_size for next page).
keywordstringSearch keyword to filter restaurants by name or cuisine.
page_sizeintegerNumber of results per page.
guest_tokenrequiredstringGuest authentication JWT from browser sessionStorage('guest_token') on food.grab.com. Valid for 30 days.
country_codestringISO country code for the GrabFood region (e.g. 'ID' for Indonesia, 'SG' for Singapore, 'TH' for Thailand).
Response
{
  "type": "object",
  "fields": {
    "offset": "integer current pagination offset",
    "page_size": "integer page size used",
    "restaurants": "array of restaurant objects with id, name, cuisine, rating, vote_count, distance_km, estimated_delivery_time, delivery_fee_display, photo_url, halal, promo, latitude, longitude",
    "total_count": "integer total number of matching restaurants"
  },
  "sample": {
    "data": {
      "offset": 0,
      "page_size": 5,
      "restaurants": [
        {
          "id": "6-C7ACCXAKGAVXNA",
          "name": "Wallace - Tangki",
          "halal": true,
          "promo": "Diskon Rp38.120",
          "rating": 4.6,
          "cuisine": [
            "Ayam Goreng",
            "Nasi Ayam"
          ],
          "latitude": -6.147417791414867,
          "longitude": 106.82382539472837,
          "photo_url": "https://huawei-food-cms.grab.com/compressed_webp/merchants/6-C7ACCXAKGAVXNA/hero/bbad5422-247f-47fb-b58f-82ab1fd45aeb__store_cover__2025__05__28__05__55__02.webp",
          "vote_count": 867,
          "distance_km": 4.99,
          "delivery_fee_display": "Rp11.000",
          "estimated_delivery_time": 50
        }
      ],
      "total_count": 767
    },
    "status": "success"
  }
}

About the Grab API

Restaurant Search

The search_restaurants endpoint accepts a latlng coordinate pair (e.g. -6.1767352,106.826504), an optional keyword to filter by restaurant name or cuisine, and a country_code for regional targeting. Results are paginated via offset and page_size. Each restaurant object in the restaurants array includes id, name, cuisine, rating, vote_count, distance_km, estimated_delivery_time, and delivery_fee_display. The total_count field tells you how many matches exist so you can calculate how many pages to walk.

Merchant Menus

The get_merchant_menu endpoint takes a merchant_id sourced from search_restaurants results (e.g. IDGFSTI00001mce) and returns a complete menu. Response fields include merchant_name, address, cuisine, menu_categories (array of category name strings), and menu_items. Each item carries id, name, description, category, available, price_display, price_minor_unit, and discounted_price_disp where applicable. total_menu_items gives a count of all items returned.

Authentication

Both endpoints require a guest_token — a JWT available from sessionStorage on food.grab.com — which remains valid for 30 days. No account login is required to obtain it; a regular browser visit to the GrabFood site is sufficient.

Reliability & maintenance

The Grab API is a managed, monitored endpoint for food.grab.com — not a raw scraper you maintain. Every endpoint is automatically health-checked on a schedule, and when food.grab.com changes and a check fails, the API is automatically queued for repair and re-verified. It is built to keep working as the site underneath it changes.

This isn't an official food.grab.com API — it's an independent, maintained REST wrapper over public data. Where the source has no official API (or only a limited one), Parse gives you a stable contract over a source that never promised one, and keeps it current. Need a new endpoint or field? You can revise it yourself in plain English and the agent rebuilds it against the live site in minutes — contributing the change back to the shared API is free.

Will this API break when the source site changes?+
It's built not to. Every endpoint is health-checked on a schedule with automated test probes. When the source site changes and a check fails, the API is automatically queued for repair and re-verified — that's the self-healing layer. Each API page shows when its endpoints were last verified. And because marketplace APIs are shared, any fix reaches everyone using it.
Is this an official API from the source site?+
No — Parse APIs are independent, managed REST wrappers over publicly available data. That is the point: where a site has no official API (or only a limited one), Parse gives you a maintained, monitored endpoint for that data and keeps it working as the site changes — so you get a stable contract over a source that never promised one.
Can I fix or extend this API myself if I need a new endpoint or field?+
Yes — and you don't have to wait on us. This API was generated by the Parse agent, which stays attached. Describe the change in plain English ("add an endpoint that returns reviews", "fix the price field") in the revise box on the API page or via the revise_api MCP tool, and the agent rebuilds it against the live site in minutes. Contributing the change back to the public API is free.
What happens if I call an endpoint that has an issue?+
Errors are machine-readable: a bad call returns a clean status with the list of available endpoints and a repair hint, so an agent (or you) can recover or trigger a fix instead of failing silently. Confirmed failures feed the automatic repair queue.
Common use cases
  • Build a delivery fee comparison tool across nearby restaurants using delivery_fee_display and distance_km.
  • Aggregate GrabFood menu prices by cuisine type for market research or competitive pricing analysis.
  • Power a restaurant discovery app filtered by rating and estimated delivery time for a given coordinate.
  • Monitor menu item availability and price changes for a set of tracked merchant IDs over time.
  • Extract discounted_price_disp values to identify current promotions across a city's restaurant listings.
  • Seed a food database with structured menu categories, item descriptions, and prices for multiple regions.
  • Build a keyword-based search interface over GrabFood inventory using the keyword parameter in search_restaurants.
Pricing & limitsSee full pricing →
TierPriceCredits/monthRate limit
Free$0/mo2005 req/min
Hobby$30/mo1,00020 req/min
Developer$100/mo5,000100 req/min
Team$300/mo20,000300 req/min
Company$1,000/mo100,000500 req/min

Each endpoint has a fixed posted price per successful call — most fall between 1 and 10 credits — shown on this API's page before you run it. Exceeding the rate limit returns a 429 response. Authenticate with the X-API-Key header.

Frequently asked questions
Does GrabFood have an official public developer API?+
Grab offers a developer platform at developers.grab.com covering payments, identity, and logistics. A public API for browsing GrabFood restaurant and menu data equivalent to this API is not part of that developer program.
What does `get_merchant_menu` return beyond item names and prices?+
get_merchant_menu returns structured data for every item: name, description, category, price_display, price_minor_unit, discounted_price_disp, and an available boolean. Items are organized under menu_categories, and the total_menu_items integer tells you the full item count without needing to count the array manually.
Which GrabFood regions does the API cover?+
The country_code parameter supports GrabFood markets including Indonesia (ID), Singapore (SG), and Thailand (TH). Coverage is tied to what the latlng coordinate resolves to in a given region, so results depend on both the coordinate and the country code supplied.
Can I retrieve order history or user reviews for a restaurant?+
Not currently. The API covers restaurant listings (with aggregate rating and vote_count) and menu data. Individual user reviews, review text, and order history are not exposed. You can fork this API on Parse and revise it to add those endpoints if that data is needed.
Does the menu endpoint return item images?+
The get_merchant_menu endpoint description references image data in the menu item objects. Fields confirmed in the response schema are id, name, description, category, available, price_display, price_minor_unit, and discounted_price_disp. If image URLs are a strict requirement and are not present in current responses, you can fork the API on Parse and extend the response mapping to surface them.
Page content last updated . Spec covers 2 endpoints from food.grab.com.
Related APIs in Food DiningSee all →
ubereats.com API
Search for restaurants by cuisine or location and browse their menus, prices, ratings, and delivery times. Get detailed information about specific restaurants and menu items to find exactly what you want to order.
deliveroo.co.uk API
Search for restaurants and retrieve menus from Deliveroo UK. Look up restaurants by keyword and postcode, or fetch full menu details for any Deliveroo restaurant by URL.
menupages.com API
Access restaurant menu data from MenuPages including all categories, items with descriptions and prices, plus customization options and modifiers. Search for specific menus or individual menu items to build restaurant catalogs, comparison tools, or delivery app integrations.
postmates.com API
Browse and search Postmates restaurants to discover menus, items, and detailed restaurant information all in one place. Get category suggestions, view complete menus, and access specific item details to find exactly what you're looking for.
toasttab.com API
Search for restaurants on ToastTab.com by location and keyword. Retrieve restaurant profiles, contact details, hours, and full menus — including item names, prices, descriptions, and customization options.
chownow.com API
Search for nearby restaurants, view their operating hours and delivery zones, and browse complete menus with all items, modifiers, and prices. Access detailed restaurant information and locations to find exactly what you're looking for on the ChowNow marketplace.
doordash.com API
Search for restaurants on DoorDash and view their menus, hours, and current promotions to find exactly what you're looking for. Get detailed information about any restaurant including pricing, availability, and active discounts across the US.
openrice.com API
Search for restaurants across Hong Kong and discover detailed information including reviews, cuisines, districts, and award-winning establishments. Browse new restaurant openings and filter by location or cuisine type to find exactly what you're looking for.