Google Maps Menu Scraper avatar

Google Maps Menu Scraper

Pricing

from $3.00 / 1,000 results

Go to Apify Store
Google Maps Menu Scraper

Google Maps Menu Scraper

Extract restaurant menu items, popular dishes, photos, categories, and external menu links from any Google Maps place page.

Pricing

from $3.00 / 1,000 results

Rating

0.0

(0)

Developer

Crawler Bros

Crawler Bros

Maintained by Community

Actor stats

0

Bookmarked

31

Total users

7

Monthly active users

a day ago

Last modified

Share

Extract restaurant menu items, popular-dish indicators, dish photos, menu categories, and external menu links from any Google Maps place page.

What this actor does

Give this actor a Google Maps URL for a restaurant or food business and it returns:

  • Menu items with names and photos (one record per item)
  • isPopular indicator — true when Google highlights a dish as popular
  • Menu categories — the section names (e.g. Appetizers, Ramen, Desserts) that Google exposes for that restaurant
  • External menu URL when the restaurant links to its own menu site, PDF, or ordering portal
  • Place ID / feature ID for cross-referencing with the Google Maps ecosystem — a canonical ChIJ… token is emitted as placeId, and a legacy 0x…:0x… token as featureId
  • Typed menuSummary record whenever Google serves no inline menu cards for the loaded place — a common, expected outcome because the inline dish-card surface is intermittent — preserving the observed place identity, external menu URL, categories, and menuFound: false without inventing an item

Availability: Google serves the inline dish-card surface intermittently. On many runs a loaded restaurant page returns no inline cards at all, and the actor emits a truthful menuSummary row with menuFound: false instead. That is an expected outcome, not a malfunction — the actor never fabricates items to fill the gap, and the same place URL can return cards on one run and none on the next.

The two "no items" outcomes are kept apart. Google also intermittently serves a degraded place page — a "limited view" shell in which the place loads with its real name but the tab strip drops the Menu tab entirely. That render has no menu surface at all, so the actor does not treat it as an empty menu. It retries the page in a fresh anonymous context a bounded number of times (the degraded state is per-context, so only a fresh context clears it), and it accepts an attempt only once the menu content has actually rendered. If the surface still never renders, the row carries menuSurfaceRendered: false and the status message says the surface was never observed, so menuFound: false is never read as "this place has no menu".

Use cases

  • Power menu intelligence for delivery, restaurant tech, or food blog platforms
  • Discover signature / popular dishes per restaurant
  • Aggregate menu category coverage by cuisine, location, or chain
  • Enrich CRM / lead-gen datasets with menu signals
  • Build product catalogues that link back to the operator's own menu page

Input

FieldTypeRequiredDescription
placeUrlStringOne of placeUrl/placeIdGoogle Maps URL of a single restaurant
placeIdStringNoCanonical Google Place ID beginning with ChIJ; use instead of placeUrl
maxItemsIntegerNoMaximum menu items per run (1-100,000). Default 100; records are emitted progressively and Google may expose fewer
languageEnumNoUI language (en, es, fr, de, it, pt, ja, ko, zh, ar, ru). Default en
proxyConfigurationObjectNoUS RESIDENTIAL recovery

Examples

Example: restaurant menu by URL

{
"placeUrl": "https://www.google.com/maps/place/McDonald's/@40.7577297,-73.9853627,17z/",
"maxItems": 50,
"language": "en"
}

Example: restaurant menu by canonical Place ID

{
"placeId": "ChIJabtoT7hlwokRLECK0SW3_8I",
"maxItems": 1000,
"language": "en"
}

Example: Japanese menu extraction

{
"placeUrl": "https://www.google.com/maps/place/Sushi+no+Midori/@35.6704396,139.7596577,17z/",
"maxItems": 100000,
"language": "ja"
}

Tip: Copy the URL straight from the address bar after opening a restaurant on Google Maps. Country-specific domains, short links, wrapping quotes, and tracking parameters are normalized.

Output per record

The actor emits one of two record shapes. Which shape you get for a given run is decided by Google, not by the input: the inline dish-card surface is served intermittently, so both are normal observed output.

Place-level summary row (menuSummary)

When the place page loads but Google serves no inline dish cards, the actor emits one place-level row. This is the shape recent runs returned for McDonald's, The Halal Guys, Sushi no Midori, and The Cheesecake Factory — menuFound: false, itemCount: 0, and no fabricated item. Field values below are illustrative; each row contains only what Google exposed for that place:

{
"recordType": "menuSummary",
"businessName": "The Halal Guys",
"featureId": "0x89c258ff097dbc2d:0x86df3a18a06daf7c",
"placeUrl": "https://www.google.com/maps/place/...",
"menuUrl": "https://example.com/menu",
"menuCategories": ["Platters", "Sandwiches", "Sides"],
"menuFound": false,
"menuSurfaceRendered": true,
"itemCount": 0,
"sourceUrl": "https://www.google.com/maps/place/...",
"scrapedAt": "2026-06-16T12:00:00Z"
}

This row is a place-level observation, not a menu item: it preserves any observed external menuUrl, categories, place identity, and menuFound: false; item-only fields (itemName, isPopular, itemImage, rank) remain absent. menuFound: false means "Google served no inline dish cards on this run" — it does not mean the restaurant has no menu. menuSurfaceRendered states whether the menu surface was actually seen: on an item row it is always true (the card came from the rendered surface), and on a summary row true means the Menu panel rendered and held no inline cards (an observed-empty menu), while false means the surface was never observed — Google's degraded limited view, or a place page that exposes no Menu tab — in which case the status message says so explicitly and no empty-menu claim is made. Because the flag now rides on item rows too, it is present on every row of a run, including a populated one. If the place itself cannot be reached after the bounded navigation/fallback attempts, no summary row is emitted and the run status explains the unavailable source (an upstream load failure, not a place that publishes no menu).

Inline item row (menuItem)

When Google does serve the dish cards, the actor emits one record per item. The values below illustrate the emitted field set — they are not a claim about current availability: an earlier capture of the same McDonald's URL returned 20 such rows, while a later capture of the same input returned none.

{
"recordType": "menuItem",
"businessName": "The Halal Guys",
"featureId": "0x89c258ff097dbc2d:0x86df3a18a06daf7c",
"placeUrl": "https://www.google.com/maps/place/...",
"menuUrl": "https://example.com/menu",
"menuCategories": ["Platters", "Sandwiches", "Sides", "Beverages", "Desserts"],
"menuFound": true,
"menuSurfaceRendered": true,
"itemName": "Chicken and Beef Gyro Platter",
"isPopular": true,
"itemImage": "https://lh3.googleusercontent.com/...=s0",
"rank": 1,
"sourceUrl": "https://www.google.com/maps/place/...",
"scrapedAt": "2026-06-16T12:00:00Z"
}

When Google exposes a canonical ChIJ… Place ID it is emitted as placeId; the legacy 0x…:0x… form is always emitted as featureId and is never placed under placeId. Only the observed form is emitted — if just the legacy token is seen, placeId is omitted.

Empty / null fields are stripped from output to keep records compact. A record contains only menu and place fields that Google exposed for that item.

Field Reference

FieldTypeNotes
recordTypeStringmenuItem for an inline menu item, menuSummary for the place-level row emitted when Google exposes no inline items
businessNameStringRestaurant name from the place page heading
placeIdStringCanonical Google Place ID beginning with ChIJ, when observed
featureIdStringLegacy 0x…:0x… feature token, when observed. Never emitted under placeId
placeUrlStringThe original Google Maps URL
menuUrlStringExternal menu link (restaurant site, PDF, ordering portal) when present
menuCategoriesArray<String>Menu section names exposed by Google for this restaurant
menuFoundBooleantrue when at least one inline menu item was extracted; false on a truthful menuSummary row
menuSurfaceRenderedBooleanStated on every row. On a menuItem row it is always true — an emitted dish card is itself proof the Menu surface rendered — so a populated run is verifiable without a summary row. On a menuSummary row it is true when the Menu panel rendered but held no inline cards (an observed-empty menu), or false when the surface was never observed (Google's degraded limited view, or no Menu tab). Absent only when it was never observed
itemCountIntegerNumber of inline menu items, present only on a menuSummary row (always 0)
itemNameStringMenu item name
isPopularBooleantrue when Google flags the item as Popular
itemImageStringMax-resolution photo URL of the dish (when available)
rankIntegerPosition of the item within the menu (1-based)
sourceUrlStringSource Google Maps URL for the row
scrapedAtStringISO 8601 UTC timestamp

FAQ

Does every restaurant on Google Maps have a menu? No — inline items only appear for restaurants where Google has indexed menu cards and serves that surface on the run. Google serves the inline dish-card surface intermittently, so a place that returned cards yesterday can return none today (and vice versa). When inline items aren't served, the actor emits a truthful place-level menuSummary row instead; any observed external menu link is available in that row. menuFound: false is an expected outcome, not evidence the actor is broken. When the page came back in Google's degraded "limited view" shell — no Menu surface at all — the actor retries in a fresh context and, if the surface still never renders, reports menuSurfaceRendered: false with a status message that says the surface was never observed rather than claiming an empty menu.

Why no prices on inline items? Google Maps does not currently render per-item prices on the place page Menu tab — it shows the dish name, image, and a "Popular" badge but the price (when published) lives behind the external menu link. The actor surfaces every signal Google exposes; if you need item-level prices, follow the menuUrl to the restaurant's own menu site.

Do I need a proxy or cookies? No login or cookies are required. The actor attempts direct public access, retries the menu surface in fresh anonymous contexts, and then uses its hardcoded Apify US RESIDENTIAL recovery path when no menu signal is observed. {"useApifyProxy": false} is available for direct-only diagnostics.

Which languages are supported? Eleven languages are exposed via the language input: English, Spanish, French, German, Italian, Portuguese, Japanese, Korean, Chinese, Arabic, Russian. This controls the Google Maps UI language only — menu item text is returned exactly as published. Google's menu content is crowd-sourced/aggregated, so a single place's menu can legitimately carry an item whose name is in another language (an English-language US McDonald's page was observed serving French Fries alongside a Turkish Patates Buyuk Boy and a Spanish Papas Fritas, all within the same Menu region of that place). Such entries are returned verbatim rather than filtered: they are Google's own data for that place, not a mix-up.

How many items per run? The default cap is 100 and you can request up to 100,000 items per run with maxItems. Records are emitted progressively and the actor stops when the public menu cards are exhausted. The ceiling is a limit, not a promise: because Google serves the inline dish-card surface intermittently, a place may expose dozens of cards, a handful, or none at all on a given run. Observed captures of the same popular-chain URL have returned anywhere from 0 to about 20 cards, so treat maxItems as an upper bound rather than an expected count.

Can I batch multiple restaurants? This actor accepts one placeUrl per run. To process many restaurants, schedule the actor with different inputs or chain it inside a workflow that iterates over URLs.

Why are some fields missing from my records? Empty / null fields are stripped to keep your dataset compact. If itemImage or menuUrl is absent it simply means Google did not publish that detail for that restaurant. Each emitted item includes its record type, place URL, and scrape timestamp.

Notes & Limitations

  • Google's inline Menu data is shallow. Google Maps shows dish name + image + a "Popular" badge on the place page, but does not render per-item prices or descriptions inline. Where the operator publishes the full menu (prices, sizes, modifiers, etc.) it lives behind the external menuUrl we surface.
  • Coverage varies, and the inline surface is intermittent. Google serves the inline dish-card surface for some places and some runs, not for others, and the same place URL can return cards on one run and none on the next. menuFound: false with a truthful menuSummary row is therefore a normal, expected result and not proof the actor is broken or that the place publishes no menu. When cards are served you typically get a handful to a few dozen dish rows (observed captures of the same chain URL returned 0 to about 20); the actor never pads the dataset up to the requested maxItems and never emits a fake dish row.
  • Place resolution is search-first. The actor resolves the input URL via Google Maps search; very ambiguous names without coordinates may resolve to a different branch — include @lat,lng in the URL when targeting a specific location.

You are responsible for using the exported menu data lawfully and in line with Google's terms.

Complete Google Maps Scraper Suite

This actor is part of a comprehensive Google Maps data extraction toolkit by crawlerbros. All actors run cookielessly on the free Apify plan with bounded direct, retry, and residential recovery paths, and return clean, structured data.

ActorWhat it does
🏢 Google Maps Business ScraperExtract business data — name, address, phone, website, rating, reviews, hours, amenities
⭐ Google Maps Reviews ScraperScrape reviews with reviewer Local Guide level, photos, mentioned items, owner replies
📸 Google Maps Photos ScraperExtract all photos from any place — max-resolution URLs, contributor info, categories
🕐 Google Maps Business Hours ScraperFull 7-day hours, timezone, current local time, next open/close, holiday hours
📊 Google Maps Popular Times ScraperBusy hours histogram for all 7 days + current busyness + typical visit time
📧 Google Maps Email ExtractorFind business emails + social media links by crawling websites
🗺️ Google Maps Area ScannerGeographic grid scanning — bypass the 120-place limit with bounding box / circle / polygon
💼 Google Maps Leads ScraperB2B lead generation with email + phone enrichment, US states + global countries
🤖 Google Maps MCP ServerUnified MCP server combining search + reviews for AI assistants
🧭 Google Maps Directions ScraperA→B routing — distance, duration, traffic, route alternatives for driving/walking/transit
📍 Google Maps Geocoding ScraperBidirectional geocoding — address ↔ coordinates, with address components
🔗 Google Maps Similar Places Scraper"People also search for" / related place discovery
📌 Google Maps Nearby ScraperFind places near a coordinate point — lightweight POI search by category
📋 Google Maps Place List ScraperExtract Google's curated "Top X in Y" lists
🌍 Google Maps Timezone ScraperIANA timezone + current local time from coordinates