🔧 Behind the Scenes🤖 For AI Agents

Google Maps Menu Scraper API — Agent Reference

Machine reference for the Google Maps Menu Scraper Apify actor (managed and BYOK variants): place URL, place ID, or place name in — structured dish records with EU-14 allergens, prices, ingredients, dietary type and nutrition out. Input/output schemas, pricing, diagnostics.

Travel Eat TeamJuly 13, 20266 min read
apifygoogle-mapsmenu-datascraperallergensapi-referenceai
🔧

🤖 This article is written for AI agents. Raw markdown

It's structured for machine consumption — dense, factual, and not optimized for human reading. If you're a human, you may prefer our articles for humans.

Audience: AI agents and automated tools. This is a machine-oriented reference. Human-readable articles: /blog.

TL;DR

One Apify actor, two variants, same code and same output: give it a Google Maps place (URL, ChIJ… place ID, or plain name) and it returns the restaurant's menu as one JSON record per dish — translated name, price, atomic ingredients with EU-14 allergen flags and confidence, dietary type, nutrition estimates.

VariantActor IDAPI keysPrice (2026-07-13)
Managednomad-agent/google-maps-menu-scraper-managednone$0.02/run + $0.015/dish (drops to $0.012 on higher Apify plans)
BYOKnomad-agent/google-maps-menu-scraperGemini (free tier works), Outscraper optional$0.01/run + $0.004/dish (drops to $0.0025 on higher Apify plans)

Choose managed for zero setup — AI and photo-fetch costs are included in the per-dish price. Choose BYOK when you already hold a Gemini key and want the lower Apify markup; you then pay Google directly. Cost breakdown: /blog/how-much-does-ai-menu-parsing-cost. The store page is authoritative on price; values here are a snapshot.

First run waives per-dish charges on up to 10 dishes. Per-dish fees apply only to dishes actually extracted; empty runs emit a free diagnostic row instead.

How it resolves a menu

  1. Input place is resolved to its Google Maps listing (name → Maps search, if needed).
  2. The place's Menu photo gallery is fetched (menuSource: "maps").
  3. If Maps has no menu photos, the actor falls back to the menu published on the place's own website, PDFs included (menuSource: "website").
  4. Photos are deduplicated, then AI-parsed together as one menu at temperature 0 — the same menu bills the same way twice.
  5. Nothing found → one non-billed diagnostic row explaining why.

Signed-out Google serves a "limited view" that hides the Maps photo gallery from plain browsers. The managed variant handles this on our keys. On BYOK, set outscraperApiKey (free tier) for reliable gallery access; without it the actor falls back to browser scraping plus the website menu. Background: /blog/google-maps-locked-photo-gallery-2026.

Input

FieldTypeRequiredDefaultNotes
placeUrlsstring[]yesMaps URLs, bare place IDs (ChIJ…), or plain place names
languageenumnoenen es fr de it pt ru zh ja ko ar hi uk ka eu
maxMenuPhotosintegerno8Clamped to 1–20
geminiModelenumnogemini-3.5-flashOr gemini-3.1-flash-lite
geminiApiKeystringBYOK onlyaistudio.google.com/apikey
outscraperApiKeystringBYOK, recommendedReliable Maps photo access
proxyConfigurationobjectBYOK onlyStandard Apify proxy input

gemini-3.1-flash-lite is cheaper but finds roughly a third of the dishes on a dense menu. Do not select it when completeness matters.

Invocation

Synchronous run returning dataset items directly (managed):

curl -X POST \
  "https://api.apify.com/v2/acts/nomad-agent~google-maps-menu-scraper-managed/run-sync-get-dataset-items?token=$APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "placeUrls": ["Katz'\''s Delicatessen New York"],
    "language": "en",
    "maxMenuPhotos": 8
  }'

BYOK adds the key:

{
  "placeUrls": ["ChIJKZuMg9dZwokRA7zRe1Yh3Ok"],
  "geminiApiKey": "AIza…",
  "outscraperApiKey": "…",
  "language": "en"
}

Any Apify invocation method works: REST, apify-client (JS/Python), apify call, MCP server, scheduled runs.

Output

One record per dish in the run's default dataset.

{
  "place": "Katz's Delicatessen New York",
  "name": "Pastrami on Rye",
  "originalName": "Pastrami on Rye",
  "category": "Sandwiches",
  "price": "$26.95",
  "composition": "Hand-carved pastrami piled on rye bread with mustard",
  "description": "Towering sandwich served with a pickle spear",
  "ingredients": [
    {
      "name": "rye bread",
      "display_name": "Rye bread",
      "explanation": null,
      "allergens": [{ "name": "gluten", "confidence": "high" }]
    },
    {
      "name": "cured beef brisket",
      "display_name": "Cured beef brisket",
      "explanation": "Brined, spiced and smoked beef",
      "allergens": [{ "name": "sulfites", "confidence": "low" }]
    },
    {
      "name": "mustard",
      "display_name": "Mustard",
      "explanation": null,
      "allergens": [{ "name": "mustard", "confidence": "high" }]
    }
  ],
  "dietaryType": "Other",
  "dietaryTypeExplanation": "Contains beef",
  "allergens": "Gluten, Mustard, Sulfites",
  "type": "dish",
  "isAlcoholic": null,
  "story": "Katz's has cured its pastrami on Houston Street since 1888…",
  "nutritionInfo": {
    "serving_size_grams": "400g",
    "calories": "800-1000 kcal",
    "protein": "50-60g",
    "fat": "40-50g",
    "carbs": "50-60g"
  },
  "menuPhotoUrls": ["https://lh3.googleusercontent.com/p/AF1…"],
  "menuPhotosFound": 12,
  "menuSource": "maps"
}
FieldTypeNotes
placestringThe place this dish came from — echoes your input, use it to join multi-place runs
namestringDish name translated to language
originalNamestringName exactly as printed on the menu
categorystringMenu section, translated
pricestring | nullAs printed, with currency symbol when shown
compositionstring | nullIngredient summary, translated
descriptionstring | nullAppearance/presentation, translated
ingredients[]arrayAtomic base ingredients — composites (pesto, hummus) are decomposed
ingredients[].namestringAlways English, for matching and joins
ingredients[].display_namestringTranslated
ingredients[].explanationstring | nullOnly for uncommon ingredients
ingredients[].allergens[]array | absentAbsent when the ingredient carries no EU-14 allergen
ingredients[].allergens[].nameenumgluten crustaceans eggs fish peanuts soy milk nuts celery mustard sesame sulfites lupin molluscs
ingredients[].allergens[].confidenceenumhigh | medium | low
dietaryTypeenumVegan | Vegetarian | Pescetarian | Other
dietaryTypeExplanationstringWhy that classification — human-auditable
allergensstringHuman-readable summary, translated, or "None identified"
typeenumdish | beverage | other
isAlcoholicboolean | nullSet for beverages
storystringOrigin/cultural note; may be plausible-but-invented
nutritionInfoobjectEstimated ranges per portion
menuPhotoUrlsstring[]Photos actually parsed
menuPhotosFoundintegerPhotos found before the maxMenuPhotos cut
menuSourceenummaps | website

Allergen confidence semantics and EU 1169/2011 context: /blog/eu-14-allergen-menu-data.

Diagnostic rows

A failed or empty run pushes exactly one row with diagnostic: true and a message explaining why (no menu photos, photos undownloadable, model returned no dishes, missing key). These rows are never billed per-dish. Filter them out before treating the dataset as menu records:

dishes = [r for r in items if not r.get("diagnostic")]

Limitations

  1. Allergen and nutrition values are inferred from the menu, not measured. The venue remains the authority under EU 1169/2011.
  2. Unreadable menu text is skipped, not guessed — dish counts can be lower than the printed menu.
  3. Menu photos on Maps may be outdated; prices reflect the photographed menu, not today's.
  4. story may be plausible reconstruction rather than verified history.

Chaining

  • Already hold menu photo URLs? Skip this actor and use AI Menu Parser.
  • Want a real guest photo attached to each dish? Feed these records plus the same place into Dish Photo Matcher and join on the dish name.

Integration help: support@traveleat.app. Machine-readable site index: /llms.txt.

T

Travel Eat Team

Contributing writer at Travel Eat. Passionate about food, travel, and helping people eat well wherever they go.

Ready to Eat Confidently Anywhere?

Download Travel Eat and discover amazing food wherever you travel.

Download for iOS