{
  "openapi": "3.0.1",
  "info": {
    "title": "GrabFood Restaurant Scraper: Menus, Ratings & Delivery Data",
    "description": "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.",
    "version": "1.0",
    "x-build-id": "MjNljrdq78lnYS3wK"
  },
  "servers": [
    {
      "url": "https://api.apify.com/v2"
    }
  ],
  "paths": {
    "/acts/abotapi~grabfood-restaurants-scraper/run-sync-get-dataset-items": {
      "post": {
        "operationId": "run-sync-get-dataset-items-abotapi-grabfood-restaurants-scraper",
        "x-openai-isConsequential": false,
        "summary": "Executes an Actor, waits for its completion, and returns Actor's dataset items in response.",
        "tags": [
          "Run Actor"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/inputSchema"
              }
            }
          }
        },
        "parameters": [
          {
            "name": "token",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Enter your Apify token here"
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/acts/abotapi~grabfood-restaurants-scraper/runs": {
      "post": {
        "operationId": "runs-sync-abotapi-grabfood-restaurants-scraper",
        "x-openai-isConsequential": false,
        "summary": "Executes an Actor and returns information about the initiated run in response.",
        "tags": [
          "Run Actor"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/inputSchema"
              }
            }
          }
        },
        "parameters": [
          {
            "name": "token",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Enter your Apify token here"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/runsResponseSchema"
                }
              }
            }
          }
        }
      }
    },
    "/acts/abotapi~grabfood-restaurants-scraper/run-sync": {
      "post": {
        "operationId": "run-sync-abotapi-grabfood-restaurants-scraper",
        "x-openai-isConsequential": false,
        "summary": "Executes an Actor, waits for completion, and returns the OUTPUT from Key-value store in response.",
        "tags": [
          "Run Actor"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/inputSchema"
              }
            }
          }
        },
        "parameters": [
          {
            "name": "token",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Enter your Apify token here"
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "inputSchema": {
        "type": "object",
        "required": [
          "mode"
        ],
        "properties": {
          "mode": {
            "title": "Mode",
            "enum": [
              "search",
              "url"
            ],
            "type": "string",
            "description": "Pick how to find restaurants. Search runs keyword queries in one region. URL mode pastes ready-made GrabFood links (a single restaurant page or a restaurants listing page).",
            "default": "search"
          },
          "region": {
            "title": "Region",
            "enum": [
              "SG",
              "MY",
              "TH",
              "VN",
              "PH",
              "ID",
              "KH",
              "MM"
            ],
            "type": "string",
            "description": "GrabFood country to search. Results and prices are for that country's stores.",
            "default": "SG"
          },
          "language": {
            "title": "Language",
            "enum": [
              "default",
              "en",
              "th",
              "vn",
              "id"
            ],
            "type": "string",
            "description": "Language of restaurant and menu text and of the grabUrl links, where GrabFood offers it for the selected country: Thailand (en, th), Vietnam (en, vn), Indonesia (en, id). Other countries are English only; an unsupported choice falls back to the country default.",
            "default": "default"
          },
          "deliveryLatitude": {
            "title": "Delivery latitude",
            "minimum": -90,
            "maximum": 90,
            "type": "number",
            "description": "Optional delivery point. Supply both latitude and longitude. Used in search, recommended listings and restaurant detail requests. Leave both blank to use the country default point. Does not confirm checkout eligibility."
          },
          "deliveryLongitude": {
            "title": "Delivery longitude",
            "minimum": -180,
            "maximum": 180,
            "type": "number",
            "description": "Optional delivery point. Supply both latitude and longitude. Used in search, recommended listings and restaurant detail requests. Leave both blank to use the country default point. Does not confirm checkout eligibility."
          },
          "searchQueries": {
            "title": "Search keywords",
            "uniqueItems": true,
            "type": "array",
            "description": "Keywords to search within the selected region (e.g. \"ramen\", \"pizza\", \"bubble tea\"). Leave empty to search the default keyword (ramen); to browse a region's recommended feed use URL mode with a listing URL that has no search term. Only applies in Search mode.",
            "default": [
              "ramen"
            ],
            "items": {
              "type": "string"
            }
          },
          "startUrls": {
            "title": "GrabFood URLs",
            "uniqueItems": true,
            "type": "array",
            "description": "Ready-made GrabFood URLs. Supported: a restaurant page (https://food.grab.com/<cc>/<lang>/restaurant/<slug>/<id>, the language segment is optional), a restaurants listing page (https://food.grab.com/<cc>/<lang>/restaurants?search=<keyword>, or without a keyword for the recommended feed) and a country home page (treated as the recommended feed). A page= parameter is not honoured and a sortBy= parameter is ignored (use the Sort by input); chain pages and city cuisine/dish collections are skipped with a warning. Only applies in URL mode.",
            "default": [
              "https://food.grab.com/sg/en/restaurants?search=sushi",
              "https://food.grab.com/sg/en/restaurants",
              "https://food.grab.com/sg/en/restaurant/luckin-coffee-punggol-plaza-delivery/4-C7WFRLKCA2XTEX",
              "https://food.grab.com/sg/restaurants?search=sushi",
              "https://food.grab.com/sg/restaurant/luckin-coffee-punggol-plaza-delivery/4-C7WFRLKCA2XTEX",
              "https://food.grab.com/sg/en/"
            ],
            "items": {
              "type": "string"
            }
          },
          "cuisineFilter": {
            "title": "Cuisine filter",
            "uniqueItems": true,
            "type": "array",
            "description": "Keep only restaurants whose cuisine tags contain any of these terms (e.g. burger, sushi, halal, western, vegetarian). Case-insensitive substring match. Leave empty to include all.",
            "items": {
              "type": "string"
            }
          },
          "minRating": {
            "title": "Min rating",
            "minimum": 0,
            "maximum": 5,
            "type": "integer",
            "description": "Minimum star rating (0-5). Restaurants below this are skipped. Leave empty for no minimum."
          },
          "minReviews": {
            "title": "Min review count",
            "minimum": 0,
            "type": "integer",
            "description": "Minimum number of ratings a restaurant must have to be included. Leave empty or 0 for no minimum.",
            "default": 0
          },
          "sortBy": {
            "title": "Sort results by",
            "enum": [
              "default",
              "rating",
              "reviews",
              "distance",
              "delivery-time"
            ],
            "type": "string",
            "description": "Order of restaurants in the output. Default keeps GrabFood's own ranking; the other options re-order the collected results client-side.",
            "default": "default"
          },
          "fetchMenu": {
            "title": "Fetch full menu",
            "type": "boolean",
            "description": "When ON, each restaurant is enriched with its full menu (categories, items, prices, images) and full address. Turn off for faster listing-only runs.",
            "default": true
          },
          "includeRatings": {
            "title": "Include rating fields",
            "type": "boolean",
            "description": "When ON, the rating score and review count are populated on every record. Turn off if you don't need any rating data.",
            "default": true
          },
          "maxItems": {
            "title": "Max restaurants",
            "minimum": 0,
            "maximum": 100000,
            "type": "integer",
            "description": "Hard cap on total restaurants for the whole run. This is the main limit. Leave empty or 0 for unlimited (the run then stops at Max pages).",
            "default": 20
          },
          "maxPages": {
            "title": "Max pages",
            "minimum": 0,
            "type": "integer",
            "description": "No page limit by default — the run stops at Max restaurants. Set a number only to also cap how many listing pages are walked per search. Whatever you set, a single run never walks beyond what the 1,000-restaurant hard stop needs.",
            "default": 0
          },
          "proxyConfiguration": {
            "title": "Proxy",
            "type": "object",
            "description": "A residential connection is required for reliable listing loads. The exit country is aligned to the selected region automatically.",
            "default": {
              "useApifyProxy": true,
              "apifyProxyGroups": [
                "RESIDENTIAL"
              ]
            }
          },
          "resumeFromRunId": {
            "title": "Resume from a previous run",
            "type": "string",
            "description": "Paste a previous run ID or dataset ID to continue a large pull of restaurants without returning or charging for restaurants already collected there. Use this after an interrupted run, or when continuing a large crawl in another run. For recurring daily monitoring of the same search, use Incremental mode below instead."
          },
          "incrementalMode": {
            "title": "Incremental changes for scheduled runs",
            "type": "boolean",
            "description": "Turn this on for daily or recurring monitoring. The first run returns all matching restaurants as NEW. Later runs normally return only NEW, UPDATED, and REAPPEARED restaurants. A restaurant is UPDATED when any output field changes (rating, votes, promo labels, open status, delivery time and fee, menu categories/items with prices and availability, opening hours, announcements, photos) except scrapedAt and any fields you list in Ignore fields below; changedFields names what moved. EXPIRED is only emitted after a complete scan. Turn on \"Emit unchanged\" or \"Emit expired\" only when you also want those restaurants returned (and billed). State is kept separately for each mode/region/search/URL and detail/filter setup; use State key when you want to name or deliberately share a monitoring campaign. To continue one specific interrupted run instead, use Resume from a previous run above.",
            "default": false
          },
          "stateKey": {
            "title": "State key (optional, incremental mode only)",
            "type": "string",
            "description": "Optional. Name this monitoring campaign to keep its state stable, or to deliberately share state across differently-configured runs. Leave empty to let the actor derive a key automatically from the mode/region/search/URL and detail/filter settings — different searches then never mix state with each other."
          },
          "emitUnchanged": {
            "title": "Emit unchanged restaurants (incremental mode only)",
            "type": "boolean",
            "description": "Off by default. Turn on to also return restaurants that have not changed since the last run, marked UNCHANGED. This returns — and bills — extra rows you already have, so leave it off unless you specifically want the full snapshot every run.",
            "default": false
          },
          "emitExpired": {
            "title": "Emit expired restaurants (incremental mode only)",
            "type": "boolean",
            "description": "Off by default. Turn on to also return restaurants that were present in a previous run but are no longer found, marked EXPIRED. Only produced once a run has fully scanned the tracked search — not when Max restaurants capped it or when Resume was used. This returns — and bills — extra synthetic rows, so leave it off unless you need expiry tracking.",
            "default": false
          },
          "ignoreFieldsForChanges": {
            "title": "Ignore fields when detecting changes (incremental mode only)",
            "type": "array",
            "description": "Optional. Field names that must NOT make a restaurant count as UPDATED, e.g. announcements, photoHref, openHours. Use a dotted path for nested fields (menu.items.price drops price from every menu item; arrays are transparent) or *.name to ignore a name at any depth (*.photoHref). Ignored fields still appear in the output; they are only left out of the comparison and changedFields. Already excluded automatically: scrapedAt.",
            "default": [],
            "items": {
              "type": "string"
            }
          },
          "mcpConnectors": {
            "title": "Pipe results into your apps (optional)",
            "type": "array",
            "description": "Optionally send the scraped results into the apps you already use, via Model Context Protocol (MCP) connectors. Authorize a connector once under Apify → Settings → Integrations, then select it here. The connector receives a condensed, human-readable summary per item (title + key fields), not the full JSON — the complete record stays in the dataset. Leave empty to skip. Supported: Notion (https://mcp.notion.com/mcp), Linear (https://mcp.linear.app/sse), Airtable (https://mcp.airtable.com/mcp), Apify (https://mcp.apify.com)."
          },
          "notionParentPageUrl": {
            "title": "Notion parent page (Notion connector only)",
            "type": "string",
            "description": "URL (or id) of the Notion page under which item pages are created. Required to enable the Notion export; ignored by other connectors."
          },
          "maxNotifyListings": {
            "title": "Max items to export per connector",
            "minimum": 1,
            "maximum": 1000,
            "type": "integer",
            "description": "Cap on items written to each connector per run. Does not affect the dataset.",
            "default": 50
          }
        }
      },
      "runsResponseSchema": {
        "type": "object",
        "properties": {
          "data": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string"
              },
              "actId": {
                "type": "string"
              },
              "userId": {
                "type": "string"
              },
              "startedAt": {
                "type": "string",
                "format": "date-time",
                "example": "2025-01-08T00:00:00.000Z"
              },
              "finishedAt": {
                "type": "string",
                "format": "date-time",
                "example": "2025-01-08T00:00:00.000Z"
              },
              "status": {
                "type": "string",
                "example": "READY"
              },
              "meta": {
                "type": "object",
                "properties": {
                  "origin": {
                    "type": "string",
                    "example": "API"
                  },
                  "userAgent": {
                    "type": "string"
                  }
                }
              },
              "stats": {
                "type": "object",
                "properties": {
                  "inputBodyLen": {
                    "type": "integer",
                    "example": 2000
                  },
                  "rebootCount": {
                    "type": "integer",
                    "example": 0
                  },
                  "restartCount": {
                    "type": "integer",
                    "example": 0
                  },
                  "resurrectCount": {
                    "type": "integer",
                    "example": 0
                  },
                  "computeUnits": {
                    "type": "integer",
                    "example": 0
                  }
                }
              },
              "options": {
                "type": "object",
                "properties": {
                  "build": {
                    "type": "string",
                    "example": "latest"
                  },
                  "timeoutSecs": {
                    "type": "integer",
                    "example": 300
                  },
                  "memoryMbytes": {
                    "type": "integer",
                    "example": 1024
                  },
                  "diskMbytes": {
                    "type": "integer",
                    "example": 2048
                  }
                }
              },
              "buildId": {
                "type": "string"
              },
              "defaultKeyValueStoreId": {
                "type": "string"
              },
              "defaultDatasetId": {
                "type": "string"
              },
              "defaultRequestQueueId": {
                "type": "string"
              },
              "buildNumber": {
                "type": "string",
                "example": "1.0.0"
              },
              "containerUrl": {
                "type": "string"
              },
              "usage": {
                "type": "object",
                "properties": {
                  "ACTOR_COMPUTE_UNITS": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATASET_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATASET_WRITES": {
                    "type": "integer",
                    "example": 0
                  },
                  "KEY_VALUE_STORE_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "KEY_VALUE_STORE_WRITES": {
                    "type": "integer",
                    "example": 1
                  },
                  "KEY_VALUE_STORE_LISTS": {
                    "type": "integer",
                    "example": 0
                  },
                  "REQUEST_QUEUE_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "REQUEST_QUEUE_WRITES": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATA_TRANSFER_INTERNAL_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATA_TRANSFER_EXTERNAL_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_SERPS": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_UNBLOCKER_UNITS": {
                    "type": "integer",
                    "example": 0
                  }
                }
              },
              "usageTotalUsd": {
                "type": "number",
                "example": 0.00005
              },
              "usageUsd": {
                "type": "object",
                "properties": {
                  "ACTOR_COMPUTE_UNITS": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATASET_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATASET_WRITES": {
                    "type": "integer",
                    "example": 0
                  },
                  "KEY_VALUE_STORE_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "KEY_VALUE_STORE_WRITES": {
                    "type": "number",
                    "example": 0.00005
                  },
                  "KEY_VALUE_STORE_LISTS": {
                    "type": "integer",
                    "example": 0
                  },
                  "REQUEST_QUEUE_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "REQUEST_QUEUE_WRITES": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATA_TRANSFER_INTERNAL_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATA_TRANSFER_EXTERNAL_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_SERPS": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_UNBLOCKER_UNITS": {
                    "type": "integer",
                    "example": 0
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}