Every business on Google Maps carries a gallery: the storefront, the interior, the dishes, the customer snapshots, the Street View panorama. Getting one photo out means right-clicking. Getting all of them, for a hundred places, means writing a scraper. I wanted the second thing and did not want to maintain the scraper, so I built the Google Maps Photos API on Apify, which turns a place name into one JSON row per photo.
Disclosure: the Apify links in this post are affiliate links. If you run the Actor, I may earn a referral commission at no extra cost to you.
Does Google Maps have a photos API?
Sort of, and the gap is the interesting part. Google's Places API has a Photo endpoint, but it returns a rendered image for a reference you already hold, and that reference comes from a separate Place Details call you also pay for. There is no "give me this restaurant's whole gallery, sorted, with stable IDs" call. Street View Static is the same shape: a rendered tile at coordinates you supply, not a listing of what is attached to a place. So the thing people mean by a Google Maps photos API is not what the official product does.
What the Google Maps Photos API returns
The Google Maps Photos API returns one row per photo as structured JSON: full-size image URL, thumbnail, gallery position, and a stable ID you can de-duplicate on across runs.
| Field | Example | Notes |
|---|---|---|
image |
https://lh3.googleusercontent.com/... |
Full-size photo URL |
thumbnail |
https://lh3.googleusercontent.com/...=w200 |
Smaller preview of the same shot |
position |
3 |
Where it sits in the place's gallery |
photo_id |
AF1QipM... |
Stable across runs, so you can dedupe |
place_title |
Mozart's Coffee Roasters |
Present when the place was found by search |
category_id |
CgIgARICCAI |
The gallery section filter that was applied |
Each place also gets one summary row carrying photos_returned, pages_fetched, and place_categories, the full list of gallery sections with the filter ID for each. That last field is how you discover a venue's own sections and then pull just one.
It does not return captions, EXIF, contributor names, or upload dates. If you need provenance, this is not the tool.
Who this is for
Listings sites that need real imagery instead of one hero thumbnail. Travel and hospitality apps pulling interiors and menus. Retail and real estate teams auditing what a location looks like online.
The manual way, and where it breaks
Load the place page in a headless browser, click into the photo grid, scroll until it stops loading, pull src off the tiles. It works the first afternoon. Then: the gallery lazy-loads in blocks, so you are scripting scroll-and-wait loops; image URLs are size-parameterized, so you must know which suffix gives the original; category tabs render client-side with opaque IDs; and the markup shifts often enough that selectors rot in weeks.
The faster way: run the Google Maps Photos API
Documented JSON in, documented JSON out, nothing to keep alive.
Apify Console
- Open the Google Maps Photos API and click Try for free.
- Type a place into
searchTerm, or paste a Google Maps URL intoplaceUrls. - Run it and download the dataset as JSON, CSV, or Excel.
REST
curl -X POST "https://api.apify.com/v2/acts/johnvc~google-maps-photos-api/runs?token=YOUR_APIFY_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "searchTerm": "Mozart'"'"'s Coffee Roasters, Austin TX", "maxPlacesPerSearch": 1, "maxPhotosPerPlace": 20 }'
Run endpoint reference: the Apify API docs.
Get Google Maps photos in Python
Photo rows and the per-place summary arrive in the same dataset, separated by result_type:
from apify_client import ApifyClient
client = ApifyClient("YOUR_APIFY_TOKEN")
run = client.actor("johnvc/google-maps-photos-api").call(
run_input={
"searchTerm": "Mozart's Coffee Roasters, Austin TX",
"maxPlacesPerSearch": 1,
"maxPhotosPerPlace": 20,
}
)
items = list(client.dataset(run.default_dataset_id).iterate_items())
for photo in [i for i in items if i.get("result_type") == "photo"]:
print(photo["position"], photo["photo_id"], photo["image"])
for place in [i for i in items if i.get("result_type") == "place_summary"]:
for category in place.get("place_categories", []):
print(category["id"], category["title"])
Note run.default_dataset_id: apify-client 3.x returns a typed Run object, not a dict, which trips up older snippets.
Street View and 360 panoramas without coordinates
This is the capability I did not expect to be the most useful one. Set photoCategory to street_view and you get the panoramas attached to a place, addressed by name rather than by coordinates. Street View Static wants a latitude, longitude, and heading, and getting from "this restaurant" to "the right pano facing the right way" is its own problem. The worked example is Get Street View and 360 Photos for Any Place. The videos category works the same way.
Bulk photo download across a list of places
maxPlacesPerSearch turns this from a one-place lookup into a sweep. A searchTerm of coffee shops in Austin, TX resolves to many places and pulls each gallery in one run. Two tasks cover the shapes: Bulk Download Every Photo for a List of Places when you already have the list, and Photos for Every Business in a Neighborhood when you want the search to find them.
Menu photos, and telling owner uploads from customer snapshots
Food and drink venues expose extra gallery sections, so photoCategory: menu gets the menu boards and dish shots without the parking lot: Get Menu and Food Photos for Any Restaurant, or the Chinese-language 获取餐厅菜品照片和店内实拍图. And by_owner separates what the business posted from what customers uploaded, which is the whole question in an imagery audit: Get Owner-Uploaded Photos for a Business.
Use it from Claude and other MCP clients
Apify exposes the Actor over the Model Context Protocol, so Claude, Claude Code, and Cursor can pull a gallery mid-conversation and answer "show me the interior shots for this hotel" with real URLs. More on Claude Code at claude.ai.
The example repo
📸 Google Maps Photos API: every place photo as clean JSON
Pull every photo Google Maps holds for a business or landmark, as structured JSON. Type a place name and get back full-size image URLs, thumbnails, gallery positions, and a stable photo ID for each one.
Actor: Google Maps Photos API on Apify Store
Video Walkthrough
https://www.youtube.com/watch?v=jREWahDGhJM
Quick Start
git clone https://github.com/johnisanerd/Apify-Google-Maps-Photos-API.git
cd Apify-Google-Maps-Photos-API
cp .env.example .env # paste your Apify token
uv sync
uv run google-maps-photos-api-example.py
Get a free Apify API key at apify.com.
Why Use This Google Maps Photos API?
- No place ID needed. Search by name, or by category and city. Paste a Google Maps URL if you have one. Identifiers work too, and skip the search charge.
- Filter to one gallery section. Menu, food and drink, vibe, by owner, videos, or Street View and 360.
- Stable photo IDs. Every row carries one, so a re-run…
Python quick start, the category-filter variants, and MCP setup for Claude, Cursor, and ChatGPT.
FAQ about scraping Google Maps photos
Is the Google Maps photos scraper free, and how much does it cost?
Billing is per photo returned, plus a small run start fee, so cost scales with what you receive rather than with pages fetched. Searching by name adds a per-search charge; passing a Google Maps URL or a place identifier skips it, which is the cheapest way to run at volume. New Apify accounts come with free platform credit, so a first run usually costs nothing.
How many photos will this scraper return for one place?
It varies, from a handful for a small office to several hundred for a busy restaurant. Photos arrive in blocks of twenty and maxPhotosPerPlace is your cap. The summary row reports photos_returned.
Can I run this scraper from Claude or another MCP client?
Yes. Connect the Apify MCP server and the Actor becomes a callable tool, so an agent pulls a gallery straight from your prompt.
Can I schedule this scraper to refresh galleries?
Yes, and the stable photo_id is what makes it worth doing: save a task, attach an Apify schedule, and dedupe on that ID to see only what is new. Start from the Google Maps Photos API.
Do the scraper's image URLs expire, and can it get historical photos?
The URLs are Google-hosted and can rotate, so re-host anything you intend to display long term rather than hotlinking. And no, it cannot give you a place's photos from five years ago: it returns the current gallery with no upload dates. Historical imagery belongs to Street View's own timeline, a different product.
Can an AI agent pay for this scraper in USDC with x402?
Yes. The Google Maps Photos API supports agentic payments via the x402 protocol, so AI agents and MCP clients can pay for runs in USDC on Base with no Apify account or API token. Point your agent at the Apify MCP server and it can discover, pay for, and run the scraper autonomously; the Apify x402 announcement has the details.
More from Truffle Pig Data
Same shape of JSON: Google Maps Places API to find the places whose photos you want, Google Maps Contributor Reviews API for what people said, and Apple Maps API for a second source.
Wrapping up
A place's gallery is public, structured, and annoying to collect by hand, a good description of most things worth automating. Try the Google Maps Photos API, or clone the example repo and point it at your own list of places.
Top comments (0)