# Google Maps Shared List Scraper (`memo23/google-maps-list-scraper`) Actor

Export places from public Google Maps shared lists — name, creator note, address, coordinates, categories, phone, website, rating, hours, and photos, plus optional newest reviews and website emails. Works with short or full list links and direct place URLs. JSON or CSV out.

- **URL**: https://apify.com/memo23/google-maps-list-scraper.md
- **Developed by:** [Muhamed Didovic](https://apify.com/memo23) (community)
- **Categories:** Lead generation, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.00 / 1,000 place exporteds

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/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — 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

## Google Maps Shared List Scraper

**Turn any public Google Maps shared list into a structured dataset.** Paste a `maps.app.goo.gl` link and get one row per saved place — name, creator note, full address, coordinates, categories, phone, website, rating, review count, opening hours, and photos — with optional Google reviews and website contact emails per place. JSON or CSV out, priced per place, not per compute unit.

### Why Use This Scraper?

- ✅ Works with short `maps.app.goo.gl` links, full list links, and direct place links — all auto-classified
- ✅ One row per saved place, with the creator's note and list order preserved
- ✅ Rich place details: phone, website, rating, review count, hours, plus code, categories, photos
- ✅ Optional newest Google reviews attached to each place (translated to English when needed)
- ✅ Optional website email harvest — turn a saved list into a lead list
- ✅ No Google API key, no browser, no login — pure HTTP, a 63-place list exports in seconds
- ✅ Deduplicates places across lists automatically

### Overview

The Google Maps Shared List Scraper is built for travelers, researchers, and sales teams who need the places saved in a public Google Maps list as structured data.

The output is place-shaped rows. Whether you start from a shared-list link or a direct place link, the dataset is a stream of place rows — one per unique place. List context (list ID, title, the creator's note, position in the list, saved-by name, saved-at date) rides along on each row when the place came from a list.

Private lists (ones that require a Google login to open) are not supported — only lists shared with a public link.

### Supported Inputs

#### URL types

| URL type | Pattern | Example |
|---|---|---|
| Short shared-list link | `maps.app.goo.gl/{code}` | `https://maps.app.goo.gl/MMjvFNWpUTjiupHc9` |
| Full shared-list link | `google.com/maps/placelists/list/{id}` | `https://www.google.com/maps/placelists/list/PiSwyqmbpwpP...` |
| Direct place link | `google.com/maps/place/...` | `https://www.google.com/maps/place/SAVOY/@35.65,139.73,17z/data=...` |
| Place by CID | `maps.google.com/?cid={number}` | `https://maps.google.com/?cid=214453715609291850` |
| Bare CID | decimal number | `214453715609291850` |

#### Copy-pasteable input

```json
{
  "listUrls": [
    "https://maps.app.goo.gl/MMjvFNWpUTjiupHc9",
    "https://maps.google.com/?cid=214453715609291850"
  ]
}
```

#### Unsupported inputs

- ❌ Private or login-protected lists ("You need access" lists)
- ❌ Google Maps search-results URLs (`/maps/search/...`) — this actor is list- and place-focused
- ❌ `goo.gl` links that don't point at Google Maps

### Use Cases

| Audience | Use case |
|---|---|
| **Travelers & creators** | Export a trip shortlist to a spreadsheet, keep notes and list order |
| **Sales & lead-gen teams** | Turn a curated list of businesses into phones, websites, and emails |
| **Researchers** | Bulk-collect place metadata (categories, ratings, hours) for local-market analysis |
| **Food & travel media** | Compare saved venues by rating and review count, pull recent review quotes |
| **Agencies** | Deliver client-ready location datasets without writing scrapers |

### How It Works

![How It Works](https://raw.githubusercontent.com/muhamed-didovic/muhamed-didovic.github.io/main/assets/how-it-works-gmaps-list.png)

1. **Input** — paste one or more public shared-list links and/or direct place links
2. **Resolve** — short links are followed to the underlying list; every input is auto-classified
3. **Collect** — one JSON call per list returns every saved place (up to Google's 500-place list cap)
4. **Enrich** — place details are fetched in parallel; reviews and website emails are added when enabled
5. **Output** — one row per unique place, exported as JSON, CSV, or via the dataset API

### Input Configuration

#### Input fields

| Field | Type | Required | Notes |
|---|---|---|---|
| `listUrls` | `array<string>` | yes | Shared-list links and/or direct place links, mixed freely |
| `maxPlacesPerList` | `integer` | optional | Cap per list. Default `100`, max `500` |
| `maxResults` | `integer` | optional | Cap across the whole run. Default `1000` |
| `includeReviews` | `boolean` | optional | Attach newest Google reviews per place. Default `false` |
| `maxReviewsPerPlace` | `integer` | optional | Default `10`, max `200`. Fetched 10 per request |
| `includeWebsiteEmails` | `boolean` | optional | Visit each place's website and harvest public emails. Default `false` |
| `maxEmailsPerPlace` | `integer` | optional | Default `3`, max `10` |
| `maxConcurrency` | `integer` | optional | Parallel place-detail requests. Default `10` |
| `proxy` | `object` | optional | Apify Residential recommended for larger runs |

#### Common scenarios

**1. Export a whole list**

```json
{
  "listUrls": ["https://maps.app.goo.gl/MMjvFNWpUTjiupHc9"]
}
```

**2. Lead list with reviews and emails**

```json
{
  "listUrls": ["https://maps.app.goo.gl/MMjvFNWpUTjiupHc9"],
  "includeReviews": true,
  "maxReviewsPerPlace": 5,
  "includeWebsiteEmails": true,
  "maxEmailsPerPlace": 3
}
```

**3. Specific places only (no list)**

```json
{
  "listUrls": [
    "https://maps.google.com/?cid=214453715609291850",
    "https://www.google.com/maps/place/SAVOY/@35.6548695,139.7364827,17z/data=!3m1!4b1!4m6!3m5!1s0x60188b9e21c65eb1:0x6e98556d60ee756e!8m2!3d35.6548695!4d139.7364827"
  ]
}
```

### Output Overview

Each dataset item is one place row containing:

- **Identity** — `name`, `localName`, `placeId` (feature id), `placeIdChIJ`, `cid`, `knowledgeId`, `googleMapsUrl`
- **Location** — `address`, `streetAddress`, `city`, `state`, `postalCode`, `countryCode`, `latitude`, `longitude`, `plusCode`, `timezone`
- **Business info** — `categories`, `rating`, `phone`, `website`, `websiteDomain`, `currentStatus`, `businessHours`, `photos`, `thumbnail`
- **List context** — `note` (the creator's note), `listId`, `listTitle`, `sourceListUrl`, `sourceIndex`, `savedByName`, `savedAt`
- **Optional enrichment** — `reviews[]` (when `includeReviews`), `emails[]` (when `includeWebsiteEmails`)

Fields Google doesn't publish for a given place come back `null` — e.g. a place without a phone listing has `phone: null`. `businessHours` carries the upcoming day(s) Google exposes publicly, not always the full week. If the same place appears in more than one submitted list, the first eligible match is kept and later matches are skipped.

### Output Samples

#### Place from a shared list, with reviews

```jsonc
{
  "name": "Tamai Nihonbashi Main Branch",
  "note": null,
  "address": "2 Chome-9-9 Nihonbashi, Chuo City, Tokyo 103-0027, Japan",
  "city": "Chuo City",
  "state": "Tokyo",
  "postalCode": "103-0027",
  "countryCode": "JP",
  "latitude": 35.6812297,
  "longitude": 139.7746943,
  "plusCode": "8Q7XMQJF+FV",
  "timezone": "Asia/Tokyo",
  "categories": ["Anago restaurant", "Japanese restaurant", "Kaiseki restaurant"],
  "rating": 4.5,
  "phone": "+81 3-3272-3227",
  "website": "http://anago-tamai.com/",
  "websiteDomain": "anago-tamai.com",
  "currentStatus": "Closed · Opens 11 AM",
  "businessHours": [
    { "day": "Wednesday", "hours": ["11 AM–2:30 PM", "5–9:30 PM"] },
    { "day": "Thursday", "hours": ["11 AM–2:30 PM", "5–9:30 PM"] }
  ],
  "photos": [{ "url": "https://lh3.googleusercontent.com/gps-cs-s/...", "caption": null }],
  "googleMapsUrl": "https://maps.google.com/?cid=214453715609291850",
  "placeId": "0x601889579765230f:0x2f9e484a3a78c4a",
  "placeIdChIJ": "ChIJDyNll1eJGGARSoyno4Tk-QI",
  "cid": "214453715609291850",
  "listId": "PiSwyqmbpwpP_Nr5sAang4x5QxbKwA",
  "listTitle": "Tokyo",
  "sourceListUrl": "https://maps.app.goo.gl/MMjvFNWpUTjiupHc9",
  "sourceIndex": 3,
  "savedByName": "Shahed Khan",
  "savedAt": "2026-05-08T06:26:59.000Z",
  "reviews": [
    {
      "rating": 5,
      "text": "The eel was delicious, but I always felt unsatisfied eating just the small box…",
      "originalLanguage": "zh-Hant",
      "relativeDate": "9 hours ago",
      "publishedAt": "2026-07-22T11:08:24.602Z",
      "authorName": "42绿子",
      "reviewUrl": "https://www.google.com/maps/reviews/data=..."
      /* + reviewId, originalText, authorProfileUrl, authorPhoto, photos[] */
    }
    /* ... more reviews ... */
  ]
}
```

#### Place with a creator note

```jsonc
{
  "name": "SAVOY Azabujuban",
  "note": "Bluefin tuna pizza",
  "rating": 4.4,
  "phone": "+81 3-6665-8102",
  "website": "https://savoy-azabujyuban.com/",
  "sourceIndex": 1
  /* same place shape as above */
}
```

### Key Output Fields

#### Place identity

- `name`, `localName`, `placeId`, `placeIdChIJ`, `cid`, `knowledgeId`, `googleMapsUrl`

#### Location

- `address`, `streetAddress`, `city`, `state`, `postalCode`, `countryCode`
- `latitude`, `longitude`, `plusCode`, `compoundPlusCode`, `timezone`

#### Business

- `categories[]`, `rating`, `phone`, `website`, `websiteDomain`
- `currentStatus`, `businessHours[]` (`{day, hours[]}`), `photos[]` (`{url, caption}`), `thumbnail`

#### List context

- `note`, `listId`, `listTitle`, `sourceListUrl`, `sourceIndex`, `savedByName`, `savedAt`

#### Reviews (when `includeReviews: true`)

- `reviews[].rating`, `reviews[].text`, `reviews[].originalText`, `reviews[].originalLanguage`
- `reviews[].authorName`, `reviews[].publishedAt`, `reviews[].photos[]`

#### Emails (when `includeWebsiteEmails: true`)

- `emails[]`, `websitePhones[]` (phones found on the website, when any)

### FAQ

#### Can I use a short Google Maps link?

Yes. Short `maps.app.goo.gl` links and full `google.com/maps` list links both work — short links are resolved automatically.

#### Can I scrape a private Google Maps list?

No. The list must open without a Google login. Only public shared lists are supported.

#### Do I need a Google Maps API key?

No. The actor reads the same public endpoints the Google Maps website uses — no API key, no browser.

#### Why are some place fields empty?

Google Maps doesn't publish the same details for every place. A place without a listed phone number has `phone: null`; a place without a website has `website: null`. The actor keeps whatever public fields exist.

#### Does `businessHours` contain the full week?

Not always. Google's public place payload carries the upcoming day or two; the row also includes `currentStatus` (e.g. "Closed · Opens 11 AM"). If you need full weekly hours for every place, tell me via the Issues tab and I'll look at adding a deeper fetch.

#### What happens when a place is in two lists?

The first eligible match is saved; later matches for the same place are skipped, so the saved row keeps the first list's context (note, index, savedAt).

#### How reliable is the email lookup?

Best-effort. The actor visits the place's own website (homepage plus contact/about pages) and harvests publicly displayed emails. Places whose "website" is an Instagram or delivery-platform link usually yield no email — that's a property of the data.

#### What about rate limits?

Lists are fetched with a single call each and place details in parallel. For runs of hundreds of places, keep the default residential proxy enabled; Google may throttle heavy anonymous traffic from datacenter IPs.

### Support

- Open an issue on the actor's Issues tab in Apify Console
- Email: <muhamed.didovic@gmail.com>

### Additional Services

- Custom output shape, additional fields, or one-off datasets: <muhamed.didovic@gmail.com>
- Need scheduled monitoring of a list (alert on new saved places)? Drop an email.
- For API access (no Apify fee, just a usage fee for the API): <muhamed.didovic@gmail.com>

### Explore More Scrapers

If this scraper was useful, see other scrapers and actors at [memo23's Apify profile](https://apify.com/memo23) — covering job boards, real estate, local business data, social media, and more.

### 🤖 For AI Agents & LLM Apps

Compact reference for AI agents calling this actor via the [Apify MCP server](https://mcp.apify.com) or the Apify API (actor: `memo23/google-maps-list-scraper`).

**Purpose:** exports every place saved in a public Google Maps shared list (or given as a direct place link) as one structured row — including the list creator's notes, plus optional reviews and website emails.

**Minimal input:**

```json
{ "listUrls": ["https://maps.app.goo.gl/MMjvFNWpUTjiupHc9"], "maxResults": 10 }
```

**Output:** one dataset row per place — `name, note, address, streetAddress, city, state, postalCode, countryCode, latitude, longitude, plusCode, timezone, categories[], rating, phone, website, websiteDomain, currentStatus, businessHours [{day, hours}], photos [{url, caption}], googleMapsUrl, placeId, placeIdChIJ, cid, listId, listTitle, sourceListUrl, sourceIndex, savedByName, savedAt`, plus `reviews[]` when `includeReviews` and `emails[]` when `includeWebsiteEmails`.

**Behaviors an agent should know:**

- Always set `maxResults` — a list can hold up to 500 places, and multiple lists multiply that.
- `listUrls` accepts short links, full list links, direct place links, `?cid=` URLs, and bare CIDs — mixed freely.
- Duplicate places across lists are deduplicated; the first match wins.
- Billing is per place row saved to the dataset. Unresolvable links and empty lists produce no charge.
- `includeWebsiteEmails` bills separately, as **Contact email found**, and only when an email is actually discovered — a place whose website yields nothing is never charged for the attempt. Leave the option off and you never see the event at all.
- Private (login-gated) lists fail with a logged warning — only public lists work. Fields Google doesn't publish are `null`.

***

### ⚠️ Disclaimer

This Actor is an independent tool and is not affiliated with, endorsed by, or sponsored by Google LLC or any of its subsidiaries. All trademarks, including Google Maps, are the property of their respective owners.

The scraper accesses only publicly available Google Maps pages and endpoints — no authenticated endpoints, no private lists, and no content behind the Google login wall. Users are responsible for ensuring their use complies with Google's Terms of Service, applicable data-protection law (GDPR, CCPA, etc.), and any contractual obligations of their own organization.

***

### SEO Keywords

google maps shared list scraper, scrape google maps list, google maps saved places export, google maps list to spreadsheet, maps.app.goo.gl scraper, google maps list export csv, google maps place scraper, google maps reviews scraper, google maps lead generation, local business data scraper, google maps email finder, place list export, travel list export, restaurant list scraper, google maps data extraction, saved places to excel, google maps API alternative, local leads scraper, Apify google maps

# Actor input Schema

## `listUrls` (type: `array`):

Public Google Maps shared-list links (short or full) and/or direct place links. Mix both in the same array if useful.

## `maxPlacesPerList` (type: `integer`):

Maximum saved places to export from each shared list.

## `maxResults` (type: `integer`):

Optional cap across all lists and direct places in this run. Use this to limit billing.

## `includeReviews` (type: `boolean`):

When enabled, each place row gets a `reviews` array with the newest Google reviews. Costs one extra HTTP call per 10 reviews per place.

## `maxReviewsPerPlace` (type: `integer`):

Maximum reviews to keep for each place when reviews are enabled.

## `includeWebsiteEmails` (type: `boolean`):

Visit discovered business websites (homepage + contact/about pages) and add public contact emails when found. Best-effort — places without a website, or websites without a public email, yield an empty `emails` array.

## `maxEmailsPerPlace` (type: `integer`):

Maximum public emails to keep for each place website when the email lookup is on.

## `maxConcurrency` (type: `integer`):

Maximum number of place-detail requests processed in parallel.

## `proxy` (type: `object`):

Google rate-limits heavy anonymous traffic — a residential proxy is recommended for larger runs.

## Actor input object example

```json
{
  "listUrls": [
    "https://maps.app.goo.gl/MMjvFNWpUTjiupHc9"
  ],
  "maxPlacesPerList": 100,
  "maxResults": 1000,
  "includeReviews": false,
  "maxReviewsPerPlace": 10,
  "includeWebsiteEmails": false,
  "maxEmailsPerPlace": 3,
  "maxConcurrency": 10,
  "proxy": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "US"
  }
}
```

# Actor output Schema

## `results` (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 = {
    "listUrls": [
        "https://maps.app.goo.gl/MMjvFNWpUTjiupHc9"
    ],
    "proxy": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ],
        "apifyProxyCountry": "US"
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("memo23/google-maps-list-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 = {
    "listUrls": ["https://maps.app.goo.gl/MMjvFNWpUTjiupHc9"],
    "proxy": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
        "apifyProxyCountry": "US",
    },
}

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

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

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

```

## CLI example

```bash
echo '{
  "listUrls": [
    "https://maps.app.goo.gl/MMjvFNWpUTjiupHc9"
  ],
  "proxy": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "US"
  }
}' |
apify call memo23/google-maps-list-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,memo23/google-maps-list-scraper"
        }
    }
}

```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/MYfSz51PIX2MGOzR6/builds/DftTkpSfH2cWFY5Eg/openapi.json
