Dataspryva

Google Maps Restaurant Menu & Popular Dish Extractor

Extract a restaurant's menu items from its Google Maps place page - dish name, photo, Popular badge, menu section, and an external menu link when the business has one.

Input schema

Input fields, types, required flags, descriptions, defaults, and prefills come from the published definition. Prefill examples are distinct from defaults.

FieldTypeRequiredDescription
modestringoptionalHow to choose restaurants: paste place URLs/IDs or run a Google Maps search query.
placeUrlsarrayoptionalOne restaurant per line (mode=byPlace). Accepts Google Maps place URLs, short maps.app.goo.gl links, canonical ChIJ Place IDs, and 0x…:0x… feature IDs.
searchQueriesarrayoptionalCuisine + city queries, e.g. `italian restaurant, New York` or `sushi in Austin` (mode=bySearch). Each query returns up to Max places per query.
Show all input fields
FieldTypeRequiredDescription
modestringoptionalHow to choose restaurants: paste place URLs/IDs or run a Google Maps search query.
placeUrlsarrayoptionalOne restaurant per line (mode=byPlace). Accepts Google Maps place URLs, short maps.app.goo.gl links, canonical ChIJ Place IDs, and 0x…:0x… feature IDs.
searchQueriesarrayoptionalCuisine + city queries, e.g. `italian restaurant, New York` or `sushi in Austin` (mode=bySearch). Each query returns up to Max places per query.
maxPlacesPerQueryintegeroptionalMaximum restaurants to process from each search query (mode=bySearch).
maxItemsPerPlaceintegeroptionalMaximum menu rows to return for each restaurant.
languagestringoptionalLanguage used for Google Maps place lookups and search results.
includePlacesWithoutMenubooleanoptionalReturn one summary row for a restaurant whose menu cannot be read, so every input place is accounted for.
includePopularDishesbooleanoptionalAlso check Google's own inline menu when it is available and mark dishes Google labels Popular. This uses a browser and is slower.

Download the published input schema

View complete JSON
{
  "description": "Choose restaurant menus by Google Maps place URL/ID or search by cuisine and city, then collect dishes, sections, descriptions, prices and place context.",
  "properties": {
    "includePlacesWithoutMenu": {
      "default": true,
      "description": "Return one summary row for a restaurant whose menu cannot be read, so every input place is accounted for.",
      "prefill": true,
      "title": "Include places without a menu",
      "type": "boolean"
    },
    "includePopularDishes": {
      "default": false,
      "description": "Also check Google's own inline menu when it is available and mark dishes Google labels Popular. This uses a browser and is slower.",
      "prefill": false,
      "title": "Include popular dishes",
      "type": "boolean"
    },
    "language": {
      "default": "en",
      "description": "Language used for Google Maps place lookups and search results.",
      "editor": "select",
      "enum": [
        "en",
        "es",
        "fr",
        "de",
        "it",
        "pt",
        "ja",
        "ko",
        "zh",
        "ar",
        "ru",
        "nl",
        "pl",
        "tr",
        "sv",
        "da",
        "no",
        "fi",
        "cs",
        "el",
        "he",
        "hi",
        "th",
        "vi",
        "id",
        "uk"
      ],
      "enumTitles": [
        "English",
        "Spanish",
        "French",
        "German",
        "Italian",
        "Portuguese",
        "Japanese",
        "Korean",
        "Chinese",
        "Arabic",
        "Russian",
        "Dutch",
        "Polish",
        "Turkish",
        "Swedish",
        "Danish",
        "Norwegian",
        "Finnish",
        "Czech",
        "Greek",
        "Hebrew",
        "Hindi",
        "Thai",
        "Vietnamese",
        "Indonesian",
        "Ukrainian"
      ],
      "prefill": "en",
      "title": "Language",
      "type": "string"
    },
    "maxItemsPerPlace": {
      "default": 100,
      "description": "Maximum menu rows to return for each restaurant.",
      "maximum": 1000,
      "minimum": 1,
      "prefill": 3,
      "title": "Max items per place",
      "type": "integer"
    },
    "maxPlacesPerQuery": {
      "default": 20,
      "description": "Maximum restaurants to process from each search query (mode=bySearch).",
      "maximum": 120,
      "minimum": 1,
      "prefill": 1,
      "title": "Max places per query",
      "type": "integer"
    },
    "mode": {
      "default": "byPlace",
      "description": "How to choose restaurants: paste place URLs/IDs or run a Google Maps search query.",
      "editor": "select",
      "enum": [
        "byPlace",
        "bySearch"
      ],
      "enumTitles": [
        "Restaurants by URL or Place ID",
        "Restaurants by search query"
      ],
      "prefill": "byPlace",
      "title": "Mode",
      "type": "string"
    },
    "placeUrls": {
      "default": [],
      "description": "One restaurant per line (mode=byPlace). Accepts Google Maps place URLs, short maps.app.goo.gl links, canonical ChIJ Place IDs, and 0x…:0x… feature IDs.",
      "editor": "stringList",
      "items": {
        "type": "string"
      },
      "prefill": [
        "https://www.google.com/maps/place/Sicily+Osteria/@40.7601601,-73.9891863,17z/data=!3m1!4b1!4m6!3m5!1s0x89c25932322b79d3:0x7c085aeae6751161!8m2!3d40.7601601!4d-73.9891863"
      ],
      "title": "Place URLs or Place IDs",
      "type": "array"
    },
    "searchQueries": {
      "default": [],
      "description": "Cuisine + city queries, e.g. `italian restaurant, New York` or `sushi in Austin` (mode=bySearch). Each query returns up to Max places per query.",
      "editor": "stringList",
      "items": {
        "type": "string"
      },
      "title": "Search queries",
      "type": "array"
    }
  },
  "required": [],
  "schemaVersion": 1,
  "title": "Google Maps Restaurant Menu Scraper Input",
  "type": "object"
}

Output schema

Published result links and storage locations. This schema is not an exhaustive contract for every field in a dataset record.

FieldTypeRequiredDescription
menuItemsstringoptionalDataset containing menu item rows and one summary row per place without a readable menu.

Download the published output schema

View complete JSON
{
  "actorOutputSchemaVersion": 1,
  "description": "Restaurant menu rows with dish name, section, description, price, currency, photo, menu provenance and full Google Maps place context.",
  "properties": {
    "menuItems": {
      "description": "Dataset containing menu item rows and one summary row per place without a readable menu.",
      "template": "{{links.apiDefaultDatasetUrl}}/items",
      "title": "Menu Items",
      "type": "string"
    }
  },
  "title": "Google Maps Restaurant Menu & Price Scraper Output"
}

Dataset schema

Published dataset views and any declared field definitions. A view can select only a subset of the returned record fields.

Download the published dataset schema

View complete JSON
{
  "actorSpecification": 1,
  "views": {
    "overview": {
      "display": {
        "component": "table",
        "properties": {
          "businessName": {
            "format": "text",
            "label": "Business"
          },
          "currency": {
            "format": "text",
            "label": "Currency"
          },
          "featureId": {
            "format": "text",
            "label": "Feature ID"
          },
          "isPopular": {
            "format": "boolean",
            "label": "Popular"
          },
          "itemCount": {
            "format": "number",
            "label": "Item Count"
          },
          "itemDescription": {
            "format": "text",
            "label": "Description"
          },
          "itemImage": {
            "format": "image",
            "label": "Photo"
          },
          "itemName": {
            "format": "text",
            "label": "Item"
          },
          "latitude": {
            "format": "number",
            "label": "Latitude"
          },
          "longitude": {
            "format": "number",
            "label": "Longitude"
          },
          "menuCategories": {
            "format": "array",
            "label": "Menu Sections"
          },
          "menuFound": {
            "format": "boolean",
            "label": "Menu Found"
          },
          "menuProvider": {
            "format": "text",
            "label": "Menu Provider"
          },
          "menuSection": {
            "format": "text",
            "label": "Menu Section"
          },
          "menuSource": {
            "format": "text",
            "label": "Menu Source"
          },
          "menuSurfaceRendered": {
            "format": "boolean",
            "label": "Google Menu Surface Rendered"
          },
          "menuUrl": {
            "format": "link",
            "label": "Menu URL"
          },
          "placeAddress": {
            "format": "text",
            "label": "Address"
          },
          "placeCategories": {
            "format": "array",
            "label": "Categories"
          },
          "placeId": {
            "format": "text",
            "label": "Place ID"
          },
          "placePriceLevel": {
            "format": "text",
            "label": "Price Level"
          },
          "placeRating": {
            "format": "number",
            "label": "Rating"
          },
          "placeReviewCount": {
            "format": "number",
            "label": "Reviews"
          },
          "placeUrl": {
            "format": "link",
            "label": "Google Maps URL"
          },
          "placeWebsite": {
            "format": "link",
            "label": "Website"
          },
          "popularDishes": {
            "format": "array",
            "label": "Popular Dishes"
          },
          "price": {
            "format": "number",
            "label": "Price Value"
          },
          "priceText": {
            "format": "text",
            "label": "Price"
          },
          "rank": {
            "format": "number",
            "label": "Rank"
          },
          "recordType": {
            "format": "text",
            "label": "Record Type"
          },
          "scrapedAt": {
            "format": "datetime",
            "label": "Scraped At"
          },
          "sourceUrl": {
            "format": "link",
            "label": "Source URL"
          },
          "totalItems": {
            "format": "number",
            "label": "Total Items"
          }
        }
      },
      "title": "Menu Items",
      "transformation": {
        "fields": [
          "recordType",
          "businessName",
          "placeId",
          "featureId",
          "placeUrl",
          "menuFound",
          "menuSource",
          "menuProvider",
          "menuSurfaceRendered",
          "itemCount",
          "totalItems",
          "itemName",
          "menuSection",
          "itemDescription",
          "priceText",
          "price",
          "currency",
          "isPopular",
          "itemImage",
          "rank",
          "menuUrl",
          "menuCategories",
          "placeAddress",
          "placeRating",
          "placeReviewCount",
          "placePriceLevel",
          "placeCategories",
          "placeWebsite",
          "latitude",
          "longitude",
          "popularDishes",
          "sourceUrl",
          "scrapedAt"
        ]
      }
    }
  }
}

Key-value schema

Published key-value store definitions, when provided by this build.

This definition is not present in the default published build. See the published README for available documentation.

Published README

Read the full published documentation

Google Maps Restaurant Menu & Price Scraper

Scrape restaurant menus from Google Maps and the restaurant's own public menu page. Paste one or many place URLs, or search by cuisine and city, and get one row per dish with its section, description, price, currency, photo, menu provenance, and the full place context — address, rating, review count, categories, website and coordinates. No login, API key or proxy setup is required.

What this actor does

  • Two modes: byPlace (Maps URL, short link, Place ID or feature ID) and bySearch (cuisine + city query)
  • Batch-ready: process many restaurants in one run, with a separate item cap per place
  • Dish-level data: menu section, item name, description, price text, numeric price and currency
  • Menu provenance: menuSource, menuProvider, the menu URL and the sections found for each restaurant
  • Place context on every row: address, rating, review count, price level, categories, website, latitude and longitude
  • Honest no-menu rows: when a place has no readable menu, one summary row records the place instead of inventing items
  • Empty or unavailable fields are omitted from each row rather than sent as null

Output per menu item (recordType: "menuItem")

  • businessName, placeId, featureId, placeUrl
  • placeAddress, placeRating, placeReviewCount, placePriceLevel
  • placeCategories[], placeWebsite, latitude, longitude
  • menuUrl, menuCategories[] — the menu page and the sections detected on it
  • menuSource — menuPage when the restaurant's own menu supplied the items, google when Google's inline menu did
  • menuProvider — the menu site or provider label, when identifiable
  • menuSection — the dish's section, e.g. Antipasti or Desserts
  • itemName, itemDescription
  • priceText — the price exactly as published, e.g. $24.95, €12,50, 12.00 USD
  • price — numeric value when the price is unambiguous
  • currency — ISO code when the page states it or the symbol is unambiguous
  • itemImage — dish photo when the menu publishes one
  • isPopular — true only when Google's inline menu shows a Popular badge
  • rank — 1-based position in the collected menu
  • totalItems — rows returned for this restaurant
  • menuFound: true, recordType: "menuItem", sourceUrl, scrapedAt

Output per place without a readable menu (recordType: "menuSummary")

  • The same place context fields and menuUrl when one was found
  • menuFound: false, menuSource: "none"
  • itemCount: 0, totalItems: 0
  • recordType: "menuSummary", sourceUrl, scrapedAt

When includePopularDishes is enabled and Google exposes Popular badges, an additional summary row carries popularDishes[] beside the item rows.

Input

Field Type Default Description
mode string byPlace byPlace or bySearch
placeUrls array – Maps URLs, maps.app.goo.gl links, ChIJ… Place IDs or 0x…:0x… feature IDs (mode=byPlace)
searchQueries array – Cuisine + city queries, e.g. italian restaurant, New York (mode=bySearch)
maxPlacesPerQuery integer 20 Restaurants to process per search query (1–120, mode=bySearch)
maxItemsPerPlace integer 100 Maximum menu rows per restaurant (1–1000)
language string en Language for Maps lookups and search results
includePlacesWithoutMenu boolean true Return a summary row for places with no readable menu
includePopularDishes boolean false Also check Google's inline menu for Popular badges (slower)

Example: several restaurants by URL

{
  "mode": "byPlace",
  "placeUrls": [
    "https://www.google.com/maps/place/Sicily+Osteria/@40.7601601,-73.9891863,17z/data=!3m1!4b1!4m6!3m5!1s0x89c25932322b79d3:0x7c085aeae6751161!8m2!3d40.7601601!4d-73.9891863",
    "https://www.google.com/maps/place/?q=place_id:ChIJxZXYx7cEdkgRdgAOZ6OHOJw"
  ],
  "maxItemsPerPlace": 50
}

Example: search by cuisine and city

{
  "mode": "bySearch",
  "searchQueries": ["italian restaurant, New York", "sushi in Austin"],
  "maxPlacesPerQuery": 10,
  "maxItemsPerPlace": 25,
  "includePlacesWithoutMenu": true
}

Example: place IDs only

{
  "mode": "byPlace",
  "placeUrls": ["ChIJ03krMjJZwokRYRF15upaCHw"],
  "maxItemsPerPlace": 100,
  "language": "en"
}

Example: non-English menu lookup

{
  "mode": "byPlace",
  "placeUrls": ["https://www.google.com/maps/place/?q=place_id:ChIJxZXYx7cEdkgRdgAOZ6OHOJw"],
  "language": "de",
  "maxItemsPerPlace": 30
}

Use cases

  • Menu intelligence — track dish names, sections and prices across a city or chain
  • Delivery and food-tech catalogues — seed dish data with descriptions and price text
  • Restaurant lead enrichment — attach menu coverage, website and rating to a place list
  • Market research — compare cuisine pricing and menu depth across neighbourhoods
  • Hospitality monitoring — detect menu changes, new sections and removed dishes over time

FAQ

Where does the menu data come from? From Google Maps place data plus the restaurant's own public menu page when it publishes one. The actor reads the menu items the restaurant publishes, not an AI guess.

Why is there no menu for some restaurants? Not every restaurant publishes a structured menu. Coverage in tested cities is roughly 60–80%; places that publish only a PDF, an image, or a login-gated ordering page may not expose readable item rows. When includePlacesWithoutMenu is on, those places still produce one summary row so nothing is silently dropped.

Why do some items have no price or no description? Many menus publish dish names without prices, or prices without descriptions. The actor reports what the page actually contains. priceText preserves the published price even when it cannot be interpreted as a single number, and price/currency are omitted when the value is ambiguous (for example a price range).

What is the difference between menuSource and menuProvider? menuSource tells you whether the rows came from menuPage or Google's inline menu (google). menuProvider names the menu site or platform when it can be identified, which helps when you compare the same chain across locations.

What happens when maxItemsPerPlace is smaller than the published menu? Rows are returned in menu order up to the cap, and totalItems states how many rows this run returned for that place. Increase the cap to collect more.

Can I scrape many restaurants in one run? Yes. Use several placeUrls entries in byPlace, or one or more searchQueries in bySearch. Places are deduplicated by Place ID or feature ID before menus are fetched.

Do I need a proxy or a Google API key? No. The actor works without a key, login or proxy setup. It automatically chooses a working connection and only escalates when a source blocks the direct request.

How fresh is the data? Each run reads the current public menu and place data; nothing is cached between runs. Menu changes appear on the next execution.

Is this affiliated with Google? No. This is an independent third-party actor that reads public Google Maps place data and public restaurant menu pages.

Python quickstart

Use a schema-derived input and the Python standard library to start a run. An Apify account and token are required for execution.

Open the step-by-step Python guide →

Source build afN7oVipaRHfge5Pm. Documentation is regenerated from the default published build; private drafts are not included.