# Google Maps Photos Scraper: Images & Videos (`deepmine/google-maps-photos-scraper`) Actor

Google Maps photos scraper: a place's photos and videos, one row each, with image and MP4 links, size, upload date, owner or visitor, source and location. Categories: menu, food & drink, vibe, latest, by owner, Street View & 360°, videos, hotel rooms. By link, ID or search. No login.

- **URL**: https://apify.com/deepmine/google-maps-photos-scraper.md
- **Developed by:** [DeepMine](https://apify.com/deepmine) (community)
- **Categories:** Travel, Lead generation, Social media
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.09 / 1,000 photo or videos

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
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.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## 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.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Google Maps Photos Scraper

Get **the photos and videos of a Google Maps place from every photo category Google Maps shows, one row each**: Menu, Food & drink, Vibe, a hotel's Rooms and more, with the image at the size you choose, a playable MP4 for videos, Street View and 360° captures, upload date, owner or visitor, the app it came from and where it was taken. Paste place links or IDs, or find places with a search, and export to JSON, CSV or Excel. No login, no API key.

### What you get

- 🖼️ **One row per photo, video or Street View capture**, never bundled: easy to filter, count and download
- 🗂️ **Photo categories as on Google Maps**: All, Latest, Menu, Food & drink, Vibe, By owner, Street View & 360°, Videos, and for hotels Rooms, Amenities, Inside and Exterior
- 📐 **Your image size**: Original (as uploaded), Large (1600 px), Medium (800 px) or Thumbnail (200 px), plus a 200 px thumbnail on every row
- 🎬 **Videos with a playable MP4 link** and their cover frame
- 🏢 **Owner or visitor**, posted with a review or not, and the source (Android, iOS, web, the business, Street View)
- 📍 **Where it was taken** (latitude, longitude) and, for Street View and 360° captures, the heading
- 🔎 **Places by link, share link, place ID, CID or search** (keyword + city, a circle or a polygon)
- 🧾 **A free summary per place**: Google's photo count, what each category returned and whether it ran to its end
- 💸 **Pay per photo**, from $0.12 per 1,000. No start fee

### How to use it

1. Paste one or more **Google Maps places**: a place link from your browser, a `maps.app.goo.gl` share link, a place ID (`ChIJ...`), a CID or an FID (`0x...:0x...`).
2. Pick the **photo categories** (All by default) and the **photos per place** (100 by default, 0 = all Google serves).
3. Click **Start**. Rows appear within a second or two and keep coming place by place.

Or leave the places empty and use **🔍 Search terms** with a location (e.g. *coffee shop* in *Seattle, WA*): every place the search finds returns its photos.

#### Example input

```json
{
  "startUrls": [{ "url": "https://www.google.com/maps/place/Katz's+Delicatessen/@40.7223,-73.9874,17z/data=!3m1!4b1!4m6!3m5!1s0x89c2598f7ff4aa09:0x313547e757cb8cea!8m2!3d40.7223!4d-73.9874" }],
  "photoCategories": ["menu", "food_and_drink"],
  "maxPhotosPerPlace": 200,
  "imageSize": "large"
}
```

#### Photo categories

| Category | What it returns |
|---|---|
| `all` | Every category below except Latest, each photo once: Google's All tab, every visitor photo, Menu, Food & drink, Vibe, videos, Street View & 360°, the owner's photos, Rooms, Amenities, Inside and Exterior. A category the place doesn't have costs nothing |
| `latest` | Photos and videos uploaded in the past month, newest first |
| `menu` | Photos of the menu |
| `food_and_drink` | Dishes and drinks |
| `vibe` | The room, the crowd, the atmosphere |
| `by_owner` | Uploaded by the business itself |
| `street_and_360` | Google Street View captures inside and outside the place, and visitors' 360° photos |
| `videos` | Videos only (a playable MP4 link each) |
| `rooms`, `amenities`, `inside`, `exterior` | Hotels and other places: rooms, amenities (gym, meeting rooms), the interior, the building and its views |

`videosOnly: true` (an API option) is the same as `["videos"]`. Google Maps serves up to about 800-1,100 photos per category, so a large place returns a few thousand rows over all categories (Katz's Delicatessen: 4,715).

### Output

One row per photo, video or Street View capture (a row of the example run, long links shortened):

```json
{
  "placeName": "Katz's Delicatessen",
  "mediaType": "video",
  "category": "all",
  "categoryLabel": "All",
  "imageUrl": "https://lh3.googleusercontent.com/gps-cs-s/ANWiy9TIUFGk...=w1920-h1080-k-no",
  "thumbnailUrl": "https://lh3.googleusercontent.com/gps-cs-s/ANWiy9TIUFGk...=w200-h112-k-no",
  "videoUrl": "https://lh3.googleusercontent.com/gps-cs-s/ANWiy9TIUFGk...=m37",
  "width": 1920,
  "height": 1080,
  "uploadedAt": "2021-05-20T18:00:00.000Z",
  "uploadTimestamp": 1621533600,
  "uploadedBy": "visitor",
  "uploadedByOwner": false,
  "fromReview": false,
  "source": "ios",
  "sourceDetail": "photos:gmm_ios",
  "latitude": 40.72223300082572,
  "longitude": -73.98742897561966,
  "heading": null,
  "photoId": "CIHM0ogKEICAgIDKz8nA1AE",
  "internalCategoryCode": 8,
  "fid": "0x89c2598f7ff4aa09:0x313547e757cb8cea",
  "placeId": "ChIJCar0f49ZwokR6ozLV-dHNTE",
  "cid": "3545819340560108778",
  "placeKgmid": "/m/03tx_h",
  "placeUrl": "https://www.google.com/maps/search/?api=1&query=Katz%27s%20Delicatessen&query_place_id=ChIJCar0f49ZwokR6ozLV-dHNTE",
  "address": "205 E Houston St, New York, NY 10002",
  "placeCategory": "Deli",
  "placeImagesCount": 95537,
  "searchString": null,
  "placeRank": null,
  "searchPageUrl": null,
  "inputStartUrl": "https://www.google.com/maps/place/Katz's+Delicatessen/@40.7223,-73.9874,17z/data=...",
  "inputPlaceId": null,
  "scrapedAt": "2026-10-19T15:53:25.911Z"
}
```

| Field | Description |
|---|---|
| `mediaType` | `photo`, `video`, `streetView` (a Google Street View capture) or `photo360` (a visitor's 360° photo) |
| `category`, `categoryLabel` | The category that returned the row (`all` when you ask for All) |
| `imageUrl` | The image at your chosen size (a video's cover frame). Street View captures are at most 1024 x 512 |
| `thumbnailUrl` | 200 px version |
| `videoUrl` | Playable MP4, highest resolution Google serves |
| `width`, `height` | Size of the original upload (a Street View capture: the whole panorama) |
| `uploadedAt`, `uploadTimestamp` | Upload time (hour precision) as ISO 8601 and as Unix seconds |
| `uploadedBy`, `uploadedByOwner` | `owner` or `visitor` |
| `fromReview` | Posted with a review |
| `source`, `sourceDetail` | `android`, `ios`, `web`, `business`, `streetView` or `other`, and Google's own tag |
| `latitude`, `longitude`, `heading` | Where it was taken; the first view's compass heading of a Street View or 360° capture |
| `photoId` | Google's ID of the photo (unique per place in a run: dedupe on it) |
| `fid`, `placeId`, `cid`, `placeKgmid`, `placeUrl`, `address`, `placeCategory`, `placeImagesCount` | The place (`placeKgmid`: its Google Knowledge Graph ID) |
| `searchString`, `placeRank`, `searchPageUrl`, `inputStartUrl`, `inputPlaceId` | How the place was found |

#### Free place summary

The **places** dataset (in the run's Storage tab and the Output tab) has one row per place, not charged: `placeImagesCount` (the count Google shows), `photos` (rows you got), `capped` (your photos-per-place limit stopped it), `complete` (every category ran to its end) and, per category, Google's reported total (Google says 9,260 for any larger category), pages, rows and whether it ended.

Google's photo count is not the number of photos its gallery serves: the count includes uploads the gallery doesn't list, so a place with *95,537 photos* returns fewer rows even when `complete` is true. Every row you get is a real, distinct upload.

### Pricing

Pay per photo, video or Street View capture in your dataset. No start fee, no monthly rent; the place summary is free.

| Apify plan | Price per 1,000 photos |
|---|---|
| Free | $0.14 |
| Starter | $0.12 |
| Scale | $0.10 |
| Business | $0.09 |

Each photo is charged once per place per run, even when it appears in several categories. Set **Photos per place** or a maximum cost per run to stay in budget: the run stops at your limit and keeps everything it found.

### Tips

- **Need only some photos?** Pick a category (Menu, Videos, By owner, Street View & 360°...) instead of All: fewer rows, lower cost.
- **All the photos Google serves for a place**: set Photos per place to 0. Large places can return thousands of rows.
- **Downloading files**: every row has direct image and video links; use `thumbnailUrl` for previews and `imageUrl` at the size you need.
- **From the place list of another Google Maps scraper**: paste its place URLs or place IDs; `inputStartUrl` / `inputPlaceId` tell you which input each row came from.

### FAQ

**Do I need a Google account or API key?** No. The Actor reads public Google Maps data, logged out.

**Why do I get fewer photos than the count on Google Maps?** Google's count includes uploads its public gallery doesn't list. The place summary shows both numbers.

**Can I get only the menu photos?** Yes: pick Menu. Food & drink, Vibe and a hotel's Rooms, Amenities, Inside and Exterior work the same way. All includes them, and a category a place doesn't have costs nothing.

**Why is the same photo not repeated in two categories?** Rows are unique per place: you pay once per photo. The `category` is the first one that returned it.

**Does it download the image files?** It returns direct links (images at your chosen size, MP4 videos), not copies.

### Other Google Maps Actors

- [Google Maps Scraper](https://apify.com/deepmine/google-maps-scraper): full place records, reviews and website contacts
- [Google Maps Extractor](https://apify.com/deepmine/google-maps-extractor): fast business lists
- [Google Maps Email Extractor](https://apify.com/deepmine/google-maps-email-extractor): emails and social profiles of businesses
- [Google Maps Business Leads](https://apify.com/deepmine/google-maps-business-leads): one lead per business, emails included

# Actor input Schema

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

Place links (<code>https://www.google.com/maps/place/...</code>, share links <code>maps.app.goo.gl/...</code>, <code>?cid=</code> links). A search link (<code>https://www.google.com/maps/search/cafe/@48.86,2.35,14z</code>) runs as a search.

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

Google place IDs (<code>ChIJdzf8kEZZwokRXtSOY0-Kwaw</code>), CIDs (<code>12448382918606115934</code>) or FIDs (<code>0x89c2598f7ff4aa09:0x313547e757cb8cea</code>).

## `photoCategories` (type: `array`):

Which of the place's photo tabs to collect. <strong>All</strong> = every tab, each photo once (Google's All tab, every visitor photo, Menu, Food & drink, Vibe, videos, Street View & 360°, the owner's photos and a hotel's Rooms, Amenities, Inside and Exterior). A tab the place doesn't have costs nothing. Each row says which category returned it.

## `maxPhotosPerPlace` (type: `integer`):

Rows each place returns at most (photos, videos and Street View captures together), e.g. 500. <strong>0</strong> = every one Google Maps serves.

## `imageSize` (type: `string`):

Size of every image link: <strong>Original</strong> (as uploaded), Large (1600 px longest side), Medium (800 px) or Thumbnail (200 px). Every row also has a 200 px <code>thumbnailUrl</code>. Street View captures are at most 1024 x 512.

## `searchStringsArray` (type: `array`):

Find places by what you would type in Google Maps, e.g. <strong>coffee shop</strong>; each place found returns its photos.

## `locationQuery` (type: `string`):

City, neighborhood, ZIP code, state or country, e.g. <strong>Boise, ID</strong> or <strong>Berlin, Germany</strong>. Results stay inside the location's own boundary (a city search doesn't spill into its suburbs). Large areas are searched ZIP code by ZIP code and tile by tile automatically. Empty = Google picks the area your term names.

## `maxCrawledPlacesPerSearch` (type: `integer`):

Places each search term returns at most. Empty = every place Google Maps has for it in the location.

## `customGeolocation` (type: `object`):

A GeoJSON Polygon or MultiPolygon (\[longitude, latitude] order), or a Point with <code>radiusKm</code>. Overrides the location. Only places inside are kept.

## `language` (type: `string`):

Language of names, categories, hours and reviews, as Google Maps shows them.

## `proxyConfiguration` (type: `object`):

Leave as is: Apify datacenter proxy works for Google Maps. A call every datacenter IP refused is retried once on a residential IP.

## Actor input object example

```json
{
  "startUrls": [
    {
      "url": "https://www.google.com/maps/place/Katz's+Delicatessen/@40.7223,-73.9874,17z/data=!3m1!4b1!4m6!3m5!1s0x89c2598f7ff4aa09:0x313547e757cb8cea!8m2!3d40.7223!4d-73.9874"
    },
    {
      "url": "https://www.google.com/maps/place/Eiffel+Tower/@48.8583701,2.2944813,17z/data=!4m6!3m5!1s0x47e66e2964e34e2d:0x8ddca9ee380ef7e0!8m2!3d48.8583701!4d2.2944813"
    }
  ],
  "photoCategories": [
    "all"
  ],
  "maxPhotosPerPlace": 50,
  "imageSize": "original",
  "maxCrawledPlacesPerSearch": 20,
  "language": "en",
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

## `photos` (type: `string`):

No description

## `places` (type: `string`):

No description

## `summary` (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 = {
    "startUrls": [
        {
            "url": "https://www.google.com/maps/place/Katz's+Delicatessen/@40.7223,-73.9874,17z/data=!3m1!4b1!4m6!3m5!1s0x89c2598f7ff4aa09:0x313547e757cb8cea!8m2!3d40.7223!4d-73.9874"
        },
        {
            "url": "https://www.google.com/maps/place/Eiffel+Tower/@48.8583701,2.2944813,17z/data=!4m6!3m5!1s0x47e66e2964e34e2d:0x8ddca9ee380ef7e0!8m2!3d48.8583701!4d2.2944813"
        }
    ],
    "maxPhotosPerPlace": 50
};

// Run the Actor and wait for it to finish
const run = await client.actor("deepmine/google-maps-photos-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 = {
    "startUrls": [
        { "url": "https://www.google.com/maps/place/Katz's+Delicatessen/@40.7223,-73.9874,17z/data=!3m1!4b1!4m6!3m5!1s0x89c2598f7ff4aa09:0x313547e757cb8cea!8m2!3d40.7223!4d-73.9874" },
        { "url": "https://www.google.com/maps/place/Eiffel+Tower/@48.8583701,2.2944813,17z/data=!4m6!3m5!1s0x47e66e2964e34e2d:0x8ddca9ee380ef7e0!8m2!3d48.8583701!4d2.2944813" },
    ],
    "maxPhotosPerPlace": 50,
}

# Run the Actor and wait for it to finish
run = client.actor("deepmine/google-maps-photos-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 '{
  "startUrls": [
    {
      "url": "https://www.google.com/maps/place/Katz'\''s+Delicatessen/@40.7223,-73.9874,17z/data=!3m1!4b1!4m6!3m5!1s0x89c2598f7ff4aa09:0x313547e757cb8cea!8m2!3d40.7223!4d-73.9874"
    },
    {
      "url": "https://www.google.com/maps/place/Eiffel+Tower/@48.8583701,2.2944813,17z/data=!4m6!3m5!1s0x47e66e2964e34e2d:0x8ddca9ee380ef7e0!8m2!3d48.8583701!4d2.2944813"
    }
  ],
  "maxPhotosPerPlace": 50
}' |
apify call deepmine/google-maps-photos-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,deepmine/google-maps-photos-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/ZvdabNhhK1uhEWsY7/builds/ylMXqrOALnvfiGEH6/openapi.json
