# Google Maps Photo Gallery Extractor by Category (`nimbryxo-studio/google-maps-photo-gallery-extractor-by-category`) Actor

Get full-resolution photos and videos from a Google Maps place, filtered by gallery tab, with contributor name, caption, dimensions and upload date.

- **URL**: https://apify.com/nimbryxo-studio/google-maps-photo-gallery-extractor-by-category.md
- **Developed by:** [Nimbryxo-studio](https://apify.com/nimbryxo-studio) (community)
- **Categories:** Automation, Lead generation, Travel
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.49 / 1,000 results

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.
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 Photo Gallery Extractor by Category

Pull every photo and video thumbnail that Google Maps publicly shows for a place — full-resolution image URLs, contributor names, captions, dimensions, and the gallery category tab each one came from (Menu, Food & Drink, Outdoor, and more).

### What this actor does

- Reads a place's public photo gallery by place URL or canonical Place ID and returns up to the number of photos you request
- Filters to a single gallery tab, such as Menu, Food & Drink, Outdoor, Indoor, or Videos, instead of the full mixed gallery
- Captures each photo's contributor name and profile link, observed upload-date text, caption, and tags when the contributor published them
- Distinguishes short video clips from photos and reports their length as Google displays it
- Omits any field Google doesn't publicly expose for a given photo rather than returning a null or guessed value

### Output per photo

| Field | Description |
|---|---|
| `recordType` | Always `"photo"` |
| `photoId` | Google's photo identifier |
| `photoIndex` | Observed position in the active gallery/category tab; a useful ordering hint, but not guaranteed stable across runs |
| `photoUrl` | Full-resolution photo URL |
| `thumbnailUrl` | Thumbnail-size photo URL |
| `category` | The gallery tab this photo came from, included only when a specific (non-"all") tab was visibly selected |
| `contributorName` / `contributorUrl` | Name and profile link of whoever uploaded the photo, when shown |
| `uploadDateOriginal` | Upload date exactly as displayed, e.g. "2 months ago" |
| `uploadDate` | ISO 8601 upload date, only when Google publishes a complete calendar date |
| `caption` | Caption text, when the contributor added one |
| `tags` | Tags attached to the photo, when published |
| `width` / `height` / `aspectRatio` / `orientation` | Original dimensions and derived aspect ratio/orientation |
| `isVideo` | `true` when the item is a short video clip rather than a still photo |
| `videoDuration` | Clip length exactly as Google displays it, e.g. "0:29" — only present when `isVideo` is true |
| `businessName` | Name of the place the gallery belongs to |
| `placeId` | Canonical Google Place ID (`ChIJ…`), when that's the form Google exposes |
| `featureId` | Legacy Google feature ID (`0x…:0x…`), when that's the form Google exposes instead |
| `placeUrl` | The place's Google Maps URL, echoed on every row so the dataset stays joinable |
| `sourceUrl` | URL the record was read from |
| `scrapedAt` | UTC timestamp when the record was scraped |

### Input

| Field | Type | Default | Description |
|---|---|---|---|
| `placeUrl` | string | – | Google Maps place URL to read photos from. Country domains, short links and tracking parameters are normalized. Required unless `placeId` is set. |
| `placeId` | string | – | Canonical Google Place ID beginning with `ChIJ`; use instead of `placeUrl` when available |
| `maxPhotos` | integer | 100 | Maximum number of photos to extract (1-100000); the place's gallery may expose fewer |
| `photoCategory` | string | all | Gallery tab to read: all, by_owner, by_visitors, food_and_drink, outdoor, indoor, menu, or videos |
| `enrichDetails` | boolean | true | Read each photo's contributor name/URL, upload date, dimensions, caption and tags. Turn off for faster, thumbnail-level rows (photo/thumbnail URL, business context, category, video flag and duration) only. |
| `proxyConfiguration` | object | Apify Proxy enabled | Optional proxy override |

#### Example: full enriched gallery

```json
{
  "placeUrl": "https://www.google.com/maps/place/Empire+State+Building/@40.7484405,-73.9878584,17z/",
  "maxPhotos": 50,
  "photoCategory": "all",
  "enrichDetails": true
}
```

#### Example: fast export of visitor photos only

```json
{
  "placeUrl": "https://www.google.com/maps/place/Sushi+no+Midori/@35.6704396,139.7596577,17z/",
  "maxPhotos": 1000,
  "photoCategory": "by_visitors",
  "enrichDetails": false
}
```

#### Example: menu photos by Place ID

```json
{
  "placeId": "ChIJabtoT7hlwokRLECK0SW3_8I",
  "maxPhotos": 500,
  "photoCategory": "menu"
}
```

### Use cases

- **Visual asset libraries** — build a photo set for a restaurant, hotel, or venue
- **Menu and interior monitoring** — track how a place's menu or interior photos change over time
- **UGC sourcing** — pull contributor photography for travel or food content
- **Place previews** — power a "what does this place look like" preview in your own app
- **Lead enrichment** — add visual context to a business-lead dataset

### FAQ

**Do I need to log in or provide cookies?**
No. The actor reads Google Maps' public photo gallery; no login or cookies are accepted. A proxy is only used automatically as a fallback if direct access doesn't return a usable gallery.

**Why does `category` sometimes not appear on a row?**
It's only set when a specific tab (not "all") was visibly selected for that photo. If you request a specific category and Google doesn't expose that tab for the place, the run reports the problem rather than mislabeling "all" photos under the wrong category.

**What does `enrichDetails` change?**
With it on, each photo's contributor, upload date, dimensions, caption and tags are read in addition to the photo and thumbnail URLs. With it off, you get faster thumbnail-level rows without those extra fields.

**Are videos included?**
Yes, when `photoCategory` is "all" or "videos". Video rows have `isVideo: true` and a `videoDuration` showing the clip length as Google displays it.

**Why might I get fewer photos than `maxPhotos`?**
A place's public gallery may simply contain fewer photos than you requested, or fewer for the specific category chosen; the actor returns everything available rather than padding the output.

**Is this affiliated with Google?**
No. This actor reads Google Maps' public pages and is not affiliated with, endorsed by, or sponsored by Google.

# Actor input Schema

## `placeUrl` (type: `string`):

Google Maps URL of the place to scrape photos from.

## `placeId` (type: `string`):

Canonical Google Place ID beginning with ChIJ. Use this instead of placeUrl when available.

## `maxPhotos` (type: `integer`):

Maximum number of photos to extract (1-100000). Google may expose fewer photos for a place or category.

## `photoCategory` (type: `string`):

Filter photos by category tab.

## `enrichDetails` (type: `boolean`):

When enabled, reads each photo's metadata: contributor name/URL, observed upload date, original dimensions, aspect ratio, orientation, caption, and tags. Disable for thumbnail-level rows (photo/thumbnail URL, business context, category, video flag and duration). Every surface is walked in both modes, so disabling trims per-photo work instead of skipping the walk.

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

Optional proxy settings. Direct access is tried first; bounded recovery uses the hardcoded US RESIDENTIAL group unless direct-only mode is explicitly selected with useApifyProxy=false.

## Actor input object example

```json
{
  "placeUrl": "https://www.google.com/maps/place/Empire+State+Building/@40.7484405,-73.9878584,17z/data=!3m1!4b1!4m6!3m5!1s0x89c259a9b3117469:0xd134e199a405a163!8m2!3d40.7484405!4d-73.9878584!16zL20vMDJfMjg2",
  "placeId": "ChIJabtoT7hlwokRLECK0SW3_8I",
  "maxPhotos": 20,
  "photoCategory": "all",
  "enrichDetails": true,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "US"
  }
}
```

# Actor output Schema

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

Each item contains only photo metadata observed from the public gallery.

# 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 = {
    "placeUrl": "https://www.google.com/maps/place/Empire+State+Building/@40.7484405,-73.9878584,17z/data=!3m1!4b1!4m6!3m5!1s0x89c259a9b3117469:0xd134e199a405a163!8m2!3d40.7484405!4d-73.9878584!16zL20vMDJfMjg2",
    "maxPhotos": 20,
    "enrichDetails": true,
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ],
        "apifyProxyCountry": "US"
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("nimbryxo-studio/google-maps-photo-gallery-extractor-by-category").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 = {
    "placeUrl": "https://www.google.com/maps/place/Empire+State+Building/@40.7484405,-73.9878584,17z/data=!3m1!4b1!4m6!3m5!1s0x89c259a9b3117469:0xd134e199a405a163!8m2!3d40.7484405!4d-73.9878584!16zL20vMDJfMjg2",
    "maxPhotos": 20,
    "enrichDetails": True,
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
        "apifyProxyCountry": "US",
    },
}

# Run the Actor and wait for it to finish
run = client.actor("nimbryxo-studio/google-maps-photo-gallery-extractor-by-category").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 '{
  "placeUrl": "https://www.google.com/maps/place/Empire+State+Building/@40.7484405,-73.9878584,17z/data=!3m1!4b1!4m6!3m5!1s0x89c259a9b3117469:0xd134e199a405a163!8m2!3d40.7484405!4d-73.9878584!16zL20vMDJfMjg2",
  "maxPhotos": 20,
  "enrichDetails": true,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "US"
  }
}' |
apify call nimbryxo-studio/google-maps-photo-gallery-extractor-by-category --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,nimbryxo-studio/google-maps-photo-gallery-extractor-by-category"
        }
    }
}
```

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/K7DeeVzu6Yf2lSKbO/builds/ZPExhSxvaycumD5OO/openapi.json
