# Google Maps Menu Scraper (`rainminer/google-maps-menu-scraper`) Actor

AI-based structured extraction of restaurant menu data from Google Maps — item names, descriptions, prices, categories, and languages from menu photos at scale. Built for food delivery platforms, price intelligence, competitor menu research, dietary databases, and hospitality data pipelines.

- **URL**: https://apify.com/rainminer/google-maps-menu-scraper.md
- **Developed by:** [rainminer](https://apify.com/rainminer) (community)
- **Categories:** Developer tools, E-commerce, AI
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, NaN bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.90 / 1,000 menu items

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are a software tools running on the Apify platform, for all kinds of web data extraction and automation use cases.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

In JavaScript/TypeScript projects, use official [JavaScript/TypeScript client](https://docs.apify.com/api/client/js.md):

```bash
npm install apify-client
```

In Python projects, use official [Python client library](https://docs.apify.com/api/client/python.md):

```bash
pip install apify-client
```

In shell scripts, use [Apify CLI](https://docs.apify.com/cli/docs.md):

````bash
# MacOS / Linux
curl -fsSL https://apify.com/install-cli.sh | bash
# Windows
irm https://apify.com/install-cli.ps1 | iex
```bash

In AI frameworks, you might use the [Apify MCP server](https://docs.apify.com/platform/integrations/mcp.md).

If your project is in a different language, use the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).


# README

The **Google Maps Menu Scraper** is an Apify Actor that extracts structured menu data from restaurant and café listings on Google Maps. Provide place URLs, Place IDs, or search queries — get back item names, descriptions, prices, and categories in a flat dataset.

---

### Key Features

- **Menu photo extraction**: Reads uploaded menu photos when no structured menu tab is available.
- **Text search**: Find places by keyword (e.g. `"brunch austin"`) — no manual URL collection required.
- **Language detection**: Each item carries an ISO 639-1 language code detected from the item text itself.
- **Source metadata**: Each item records the source menu photo URL and when it was uploaded.
- **Deduplication**: Items appearing across multiple menu photos are merged, keeping the most recent version.
- **Multilingual**: Preserves original item names and descriptions in any language.

---

### Why Scrape Google Maps Menus?

Google Maps is the most comprehensive public source of restaurant information, but menu data is typically locked in uploaded images. This Actor unlocks that data for:

- **Food delivery and aggregator platforms** building comprehensive menu databases.
- **Price intelligence** tracking menu price changes over time across competitors.
- **Restaurant research** comparing offerings across similar establishments in a city.
- **Dietary and allergy databases** that need structured item names and descriptions.
- **Travel and tourism apps** showing visitors what to expect before dining.

---

### Who Is It For?

- **Food-tech startups** that need affordable, scalable menu data.
- **Restaurant chains** auditing competitor menus across locations.
- **Market researchers** studying pricing trends in the hospitality sector.
- **Developers** building culinary discovery apps or dining recommendation systems.

---

### Input Schema

```json
{
  "startUrls": [
    { "url": "https://www.google.com/maps/place/Mercanti+Pizzeria+%26+So./..." }
  ],
  "placeIds": ["ChIJZ2rw6ac5qBQRKgoWMAqIY3E"],
  "textQueries": ["brunch austin", "pizza in New York"],
  "maxMenuImages": 1,
  "maxItems": 100,
  "maxPlacesPerQuery": 5,
  "menuLanguage": "en"
}
````

**At least one of `startUrls`, `placeIds`, or `textQueries` must be provided.**

| Field | Type | Default | Description |
|---|---|---|---|
| `startUrls` | Array | — | Google Maps place page URLs or `maps.app.goo.gl` short links |
| `placeIds` | Array of strings | — | Google Maps Place IDs starting with `ChIJ` |
| `textQueries` | Array of strings | — | Free-text search queries (e.g. `"brunch austin"`) |
| `maxPlacesPerQuery` | Integer | `5` | Max places to scrape per text query (up to 60) |
| `maxMenuImages` | Integer | `1` | Max menu photos to process per place |
| `maxItems` | Integer | `100` | Max unique menu items to output per place after deduplication |
| `menuLanguage` | String | `"en"` | ISO 639-1 language code for menu extraction (e.g. `en`, `es`, `bg`, `fr`, `de`, `it`). |

***

### Output Schema

Each dataset item represents one unique menu item extracted from a place:

```json
{
  "placeId": "ChIJN1t2JNayGGARRwe9bMh9diY",
  "placeName": "Mercanti Pizzeria & So.",
  "placeUrl": "https://www.google.com/maps/place/Mercanti+Pizzeria+%26+So./...",
  "category": "Pizzas",
  "name": "Margherita",
  "description": "Tomato sauce, fior di latte mozzarella, fresh basil",
  "price": 14,
  "currency": "USD",
  "priceText": "$14.00",
  "language": "en",
  "menuImageUrl": "https://lh3.googleusercontent.com/gps-cs-s/...",
  "scrapedAt": "2026-05-31T04:49:29.132Z"
}
```

| Field | Description |
|---|---|
| `placeId` | Google Maps Place ID (`ChIJ…`) |
| `placeName` | Display name of the restaurant |
| `placeUrl` | Google Maps place URL |
| `category` | Menu section header (e.g. `"Pizzas"`, `"Desserts"`) — `null` if not visible |
| `name` | Item name in original language |
| `description` | Item description or ingredients — `null` if absent |
| `price` | Parsed numeric price without currency symbol (e.g. `8.99`) — `null` if absent |
| `currency` | ISO 4217 currency code (e.g. `"EUR"`, `"BGN"`) — `null` if indeterminate |
| `priceText` | Original price text as shown in the photo (e.g. `"€8.99"`) — `null` if absent |
| `language` | ISO 639-1 language code of the item text (e.g. `"en"`, `"bg"`, `"it"`) |
| `menuImageUrl` | High-resolution URL of the source menu photo |
| `scrapedAt` | ISO timestamp of when this item was scraped |

***

### How It Works

1. **Input** — provide `startUrls`, `placeIds`, and/or `textQueries`.
2. **Extract** — the Actor collects menu photos for each place and reads visible items.
3. **Output** — one dataset row per unique menu item, with place details and price fields.

***

### Notes and Limitations

- **AI-powered extraction**: This Actor uses AI to identify menu photos and extract items. Output is **non-deterministic** — the same place may produce slightly different results across runs, and the model can misclassify non-menu photos or misread item names, prices, and descriptions. You may still be charged for photos that are analyzed even when no usable menu items are returned. We are continuously improving accuracy, but the inherent non-determinism of modern AI is not something we can fully eliminate.
- **Photo availability**: Results depend on user-uploaded photos. Places with no menu photos produce no output.
- **Photo quality**: Handwritten or low-quality menu photos may yield incomplete extractions.
- **Menu language**: Set `menuLanguage` to the primary script of the menus you are scraping for best results with non-Latin alphabets (Cyrillic, Arabic, Chinese, etc.).

# Actor input Schema

## `startUrls` (type: `array`):

Google Maps place URLs or maps.app.goo.gl short links.

## `placeIds` (type: `array`):

Google Maps Place IDs (e.g. ChIJZ2rw6ac5qBQRKgoWMAqIY3E). Will be resolved to place pages.

## `maxMenuImages` (type: `integer`):

Maximum number of menu photos to process per place.

## `textQueries` (type: `array`):

Free-text queries to find places (e.g. "brunch austin", "pizza in New York").

## `maxPlacesPerQuery` (type: `integer`):

Maximum number of places to scrape per text query (up to 60).

## `maxItems` (type: `integer`):

Maximum number of unique menu items to output per place after deduplication.

## `menuLanguage` (type: `string`):

ISO 639-1 language code for menu extraction (e.g. en, es, bg). When set, only items in this language are extracted — useful for bilingual menus that would otherwise produce duplicates.

## Actor input object example

```json
{
  "placeIds": [
    "ChIJZ2rw6ac5qBQRKgoWMAqIY3E"
  ],
  "maxMenuImages": 1,
  "maxPlacesPerQuery": 5,
  "maxItems": 100,
  "menuLanguage": "en"
}
```

# Actor output Schema

## `overview` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "placeIds": [
        "ChIJZ2rw6ac5qBQRKgoWMAqIY3E"
    ],
    "maxMenuImages": 1,
    "menuLanguage": "en"
};

// Run the Actor and wait for it to finish
const run = await client.actor("rainminer/google-maps-menu-scraper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "placeIds": ["ChIJZ2rw6ac5qBQRKgoWMAqIY3E"],
    "maxMenuImages": 1,
    "menuLanguage": "en",
}

# Run the Actor and wait for it to finish
run = client.actor("rainminer/google-maps-menu-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "placeIds": [
    "ChIJZ2rw6ac5qBQRKgoWMAqIY3E"
  ],
  "maxMenuImages": 1,
  "menuLanguage": "en"
}' |
apify call rainminer/google-maps-menu-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=rainminer/google-maps-menu-scraper",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

```json
{
    "openapi": "3.0.1",
    "info": {
        "title": "Google Maps Menu Scraper",
        "description": "AI-based structured extraction of restaurant menu data from Google Maps — item names, descriptions, prices, categories, and languages from menu photos at scale. Built for food delivery platforms, price intelligence, competitor menu research, dietary databases, and hospitality data pipelines.",
        "version": "1.0",
        "x-build-id": "4OkNb7Ld9aKgqrZ8G"
    },
    "servers": [
        {
            "url": "https://api.apify.com/v2"
        }
    ],
    "paths": {
        "/acts/rainminer~google-maps-menu-scraper/run-sync-get-dataset-items": {
            "post": {
                "operationId": "run-sync-get-dataset-items-rainminer-google-maps-menu-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/rainminer~google-maps-menu-scraper/runs": {
            "post": {
                "operationId": "runs-sync-rainminer-google-maps-menu-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/rainminer~google-maps-menu-scraper/run-sync": {
            "post": {
                "operationId": "run-sync-rainminer-google-maps-menu-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",
                "properties": {
                    "startUrls": {
                        "title": "Start URLs",
                        "type": "array",
                        "description": "Google Maps place URLs or maps.app.goo.gl short links.",
                        "items": {
                            "type": "object",
                            "required": [
                                "url"
                            ],
                            "properties": {
                                "url": {
                                    "type": "string",
                                    "title": "URL of a web page",
                                    "format": "uri"
                                }
                            }
                        }
                    },
                    "placeIds": {
                        "title": "Place IDs",
                        "type": "array",
                        "description": "Google Maps Place IDs (e.g. ChIJZ2rw6ac5qBQRKgoWMAqIY3E). Will be resolved to place pages.",
                        "items": {
                            "type": "string"
                        }
                    },
                    "maxMenuImages": {
                        "title": "Max menu images per place",
                        "minimum": 1,
                        "maximum": 20,
                        "type": "integer",
                        "description": "Maximum number of menu photos to process per place.",
                        "default": 1
                    },
                    "textQueries": {
                        "title": "Text search queries",
                        "type": "array",
                        "description": "Free-text queries to find places (e.g. \"brunch austin\", \"pizza in New York\").",
                        "items": {
                            "type": "string"
                        }
                    },
                    "maxPlacesPerQuery": {
                        "title": "Max places per text query",
                        "minimum": 1,
                        "maximum": 60,
                        "type": "integer",
                        "description": "Maximum number of places to scrape per text query (up to 60).",
                        "default": 5
                    },
                    "maxItems": {
                        "title": "Max items per place",
                        "minimum": 1,
                        "type": "integer",
                        "description": "Maximum number of unique menu items to output per place after deduplication.",
                        "default": 100
                    },
                    "menuLanguage": {
                        "title": "Menu language",
                        "type": "string",
                        "description": "ISO 639-1 language code for menu extraction (e.g. en, es, bg). When set, only items in this language are extracted — useful for bilingual menus that would otherwise produce duplicates."
                    }
                }
            },
            "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
                                    }
                                }
                            },
                            "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
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
```
