Discover/Grab API
live

Grab APIfood.grab.com

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

Endpoints
2
Updated
2mo ago

What is the Grab API?

The GrabFood API provides 2 endpoints to query restaurants and menus across GrabFood-supported markets in Southeast Asia. Use search_restaurants to find and page through nearby restaurants with ratings, delivery fees, and estimated delivery times, then call get_merchant_menu with a merchant ID to retrieve every menu item—including prices, categories, availability flags, and modifier options—for that restaurant.

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 parameter (latitude/longitude, comma-separated) and an optional keyword to filter by restaurant name or cuisine type. 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 records match so you can calculate how many pages to fetch. A country_code parameter (e.g. SG, TH, ID) scopes results to the correct regional GrabFood instance.

Merchant Menu Retrieval

Once you have a merchant id from search results, pass it as merchant_id to get_merchant_menu. The response returns menu_items—an array covering every item across all categories—with fields like name, description, category, price_display, price_minor_unit, discounted_price_disp, and available. The menu_categories array lists category names in order, and total_menu_items gives the total item count. The address and cuisine fields on the response describe the merchant itself.

Authentication and Coverage

Both endpoints require a guest_token—a JWT retrieved from sessionStorage on food.grab.com in any modern browser. Tokens are valid for 30 days. The API covers GrabFood markets where the service operates, including Indonesia, Singapore, Thailand, and other Southeast Asian countries. The latlng parameter drives location context for both search and menu requests, so accurate coordinates are important for correct delivery fee and availability data.

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
  • Aggregate restaurant listings and delivery fees across multiple GrabFood cities for a price-comparison dashboard.
  • Build a menu database by iterating search_restaurants results and calling get_merchant_menu for each merchant ID.
  • Track rating and vote_count changes for specific restaurants over time to monitor reputation trends.
  • Identify which cuisines and restaurants are available near a given latitude/longitude for a delivery coverage map.
  • Extract discounted item prices (discounted_price_disp vs price_display) to surface deals across restaurants.
  • Populate a food-ordering assistant with structured category and modifier data from menu_items for a given merchant.
  • Analyze estimated delivery times and delivery_fee_display across a city grid to study logistics patterns.
Pricing & limitsSee full pricing →
TierPriceCredits/monthRate limit
Free$0/mo1005 req/min
Hobby$30/mo1,00020 req/min
Developer$100/mo5,000100 req/min

One credit = one API call regardless of which marketplace API you call. 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?+
GrabFood does not offer a public developer API for restaurant search or menu data. Grab does publish a GrabExpress delivery API for business partners at developers.grab.com, but that covers parcel delivery logistics, not food restaurant or menu data.
What does `search_restaurants` return beyond a restaurant name and rating?+
Each result object includes the merchant id (needed for menu lookups), cuisine type, vote_count, distance_km from the supplied coordinates, estimated_delivery_time, and delivery_fee_display. The total_count field on the response lets you paginate through all matching results using offset and page_size.
Does `get_merchant_menu` return modifier groups or add-on options for menu items?+
The menu_items array includes modifier options at the item level. Full nested modifier-group structures (e.g. size choices, toppings with sub-options and min/max selection rules) are included where the merchant has configured them.
Is order placement or cart management supported?+
No. The API covers restaurant discovery via search_restaurants and menu retrieval via get_merchant_menu. Order creation, cart management, and checkout are not exposed. You can fork this API on Parse and revise it to add those endpoints if your use case requires transactional capabilities.
How current is the menu and pricing data?+
Data reflects what GrabFood returns at the time of the request for the supplied latlng and merchant_id. Menu availability (available field per item) and pricing can vary by time of day or merchant configuration, so results should be treated as point-in-time. There is no built-in caching layer between your call and the source.
Page content last updated . Spec covers 2 endpoints from food.grab.com.
Related APIs in Food DiningSee all →
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.
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.
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.
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.
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.
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.
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.
resy.com, opentable.com API
Search and compare restaurants across Resy and OpenTable by cuisine, location, and price range, then sort results by price or ratings to find the best dining option. Retrieve comprehensive restaurant details including addresses, contact information, descriptions, and customer ratings all in one place.