Google Maps Place Photos Scraper
Pricing
from $0.23 / 1,000 photo scrapeds
Google Maps Place Photos Scraper
Pull the photos and videos off a Google Maps place: a full-size image link, the caption someone typed, the pixel size, the upload date and where the camera stood, one row per picture. Paste a Maps link, a Share link or a place id. No API key, no Google account, no browser.
Pricing
from $0.23 / 1,000 photo scrapeds
Rating
0.0
(0)
Developer
Dami's Studio
Maintained by CommunityActor stats
0
Bookmarked
3
Total users
2
Monthly active users
8 hours ago
Last modified
Share
Point it at a Google Maps place and get back the pictures on it — one row per photo or video, with a full-size image link, the caption the person typed, the pixel dimensions, the date it went up and where the camera was standing. No API key, no Google account, no browser.
- A full-size image link, not a thumbnail. Ask for the width you want and the URL comes back at that width.
- Photos, videos and Street View entries are labelled, so you can keep only the kind you need.
- Every row carries the upload date, so a venue's pictures sort by year without any extra work.
- Runs with empty input return a labelled sample row, free, so you can see the shape before spending anything.
Price
$0.23 per 1,000 photos, plus a $0.0005 start fee per run.
This is a flat rate on every plan, free or paid. There are no volume tiers, no minimum spend, no subscription and no add-on fees. What you read here is what you pay on day one and on day four hundred.
| Photos | Total cost |
|---|---|
| 100 | $0.0235 |
| 1,000 | $0.2305 |
| 10,000 | $2.3005 |
| 100,000 | $23.0005 |
What is actually charged
- One
photo-scrapedevent per photo row written to the dataset. Nothing else is metered per row. - Free: the sample row an empty run returns, and every diagnostic row — a blocked target, a dead URL, a search that matched nothing. Those rows all carry
"charged": false. - A place that shows no pictures at all costs nothing beyond the start fee.
- Videos and Street View entries you filtered out are dropped before they are charged.
- A run that finds nothing costs the start fee and nothing more.
- Rows never leave the dataset without a charge, and are never charged without a row. The billed event is a named one, so there is no price quietly attached to
apify-default-dataset-item— the trick that makes some scrapers bill you for their own error messages.
Input
{"placeUrls": ["https://www.google.com/maps/place/Starbucks+Reserve+Roastery/data=!4m2!3m1!1s0x54906acdccf44db1:0x6da0c1f2d7a6736e","https://maps.app.goo.gl/example"],"maxPhotosPerPlace": 30,"maxPhotos": 60,"mediaType": "all","imageWidth": 1600}
| Field | What it does |
|---|---|
placeUrls | One or more places, up to 50 per run. A full Google Maps link works, so does the short link from the Share button, a ?cid= link, or the raw 0x...:0x... id if you already have one. |
maxPhotosPerPlace | Ceiling on rows from a single place. Default 30. |
maxPhotos | Total rows for the whole run, shared evenly between your places. Default 200. Keep it low while you are testing — you pay per row. |
mediaType | all (default), photos to drop videos and Street View, or videos to keep only clips. |
imageWidth | Pixel width for the photoUrl link. Default 1600. The height follows the original aspect ratio. |
language | Two-letter interface language, default en. It changes captions Google has translated, not the pictures. |
country | Two-letter country for the request, default us. |
proxyUrls | Leave empty. Fill it in only if you want the traffic to leave through proxy servers you already pay for, as http://user:pass@host:port. |
Run it with empty input and you get one clearly labelled sample row, free, so you can see the output shape before you spend anything.
Output
One row per photo. A real row from a real run:
{"ok": true,"charged": true,"recordType": "photo","target": "https://www.google.com/maps?cid=7899526995152106350","placeName": "Starbucks Reserve Roastery","placeId": "0x54906acdccf44db1:0x6da0c1f2d7a6736e","cid": "7899526995152106350","placeUrl": "https://www.google.com/maps?cid=7899526995152106350","photoId": "CIHM0ogKEICAgICxpfGj4wE","mediaType": "photo","photoUrl": "https://lh3.googleusercontent.com/gps-cs-s/AHRPTWlnERVZyB9D3zFYFg7wFEDmh5g=w1600-h900-k-no","thumbnailUrl": "https://lh3.googleusercontent.com/gps-cs-s/AHRPTWlnERVZyB9D3zFYFg7wFEDmh5g=w203-h114-k-no","caption": "Espresso flight of 2","width": 4032,"height": 2268,"uploadedAt": "2022-11-10T20:00:00.000Z","latitude": 47.6140399947503,"longitude": -122.328100047145,"videoDurationMs": null,"videoUrl": null,"scrapedAt": "2026-09-19T12:04:11.418Z"}
Field notes
photoUrl— the image at the width you asked for. Google serves these directly; no token, no expiry seen in testing.thumbnailUrl— the small version exactly as Google returned it, if you want to avoid re-requesting a big file.caption— what the uploader typed under the picture. Null when they typed nothing, and null when the only text was the place name repeated.mediaType—photo,videoorstreet_view.uploadedAt— when the picture went up, to the hour, in UTC. Google does not publish the minute.latitude/longitude— where the camera was, not the centre of the business. On a large place these differ by tens of metres.videoDurationMs— clip length in milliseconds, on video rows only.placeId— Google's internal feature id for the place, stable across runs and the safest join key.cid— the same place as a decimal id, which is whatmaps?cid=links use.
Every real row carries "charged": true. Sample rows carry "_sample": true and diagnostic rows carry "_diagnostic": true with an errorCode you can filter on, and neither is ever billed.
How it works
- The gallery is not in the page source — a logged-out fetch of a place page comes back with no image links in it at all. The pictures come from the JSON feed the Maps front end calls for itself, so that is what this reads.
- That feed only answers properly once it has handed the caller a guest session, so each run asks Google for its own front page first and keeps what it gets back. Nobody logs in and no account is involved.
- Requests are paced, because the feed limits how often it will answer.
- If an address stops serving the gallery, the run moves to another one rather than reporting the place as empty. That distinction matters: "no photos here" and "this run got throttled" look identical unless you check, and only one of them is true.
What people use it for
- Filling a listing, directory or travel page with real photography of a venue instead of stock images.
- Watching what a location looks like over time — the upload dates let you sort a venue's pictures by year and see a refurbishment happen.
- Checking whether a business's own pictures are outnumbered by customer ones, which is usually the first sign a listing has been left to drift.
- Building an image set for a computer-vision job, where the caption and the pixel dimensions matter as much as the file.
- Auditing a chain: run every branch through in one go and compare how many pictures each location has.
Reading the output
Every run writes three kinds of row, and they are easy to tell apart:
- Real rows carry
"charged": trueand"recordType": "photo". One billed event each. - The sample row carries
"_sample": trueand"charged": false. There is exactly one, it only appears when the input had no places in it, and it is there so you can look at the shape of the output before you spend anything. - Diagnostic rows carry
"_diagnostic": true,"charged": falseand anerrorCodeyou can switch on:PLACE_NOT_RESOLVEDwhen the link could not be tied to a place,NO_PHOTOSwhen the place genuinely shows none,BLOCKEDwhen every request the run made was refused the gallery,PHOTOS_PARSEwhen the feed answered with something else,NETWORKwhen it could not be reached, andTIME_BUDGETwhen the run ran out of time before reaching a place.
If you only want the data, filter on charged == true. The count of those rows equals the number of events you were billed for, so the dataset is its own invoice.
Limitations
- This returns the pictures on a place, and nothing else about it. No rating, no address, no opening hours, no reviews.
- There is no uploader name in the feed and none is invented, so rows tell you what the picture is and when it went up, not who took it.
- Captions are rare. On a 150-picture venue measured here, 4 rows carried a caption and the other 146 came back with
caption: null. Treat it as a bonus, not a field you can plan around. - The upload time is only published to the hour, so two pictures added in the same hour cannot be ordered between themselves.
- Street View entries are labelled and returned, but they are panorama stills rather than someone's photograph, so most callers will want
mediaType: "photos". - A place with no pictures returns an uncharged diagnostic row, not an empty dataset.
- Short links and
ChIJids each cost one extra request to turn into a place before any picture is read. - The hard ceiling is 50 places per run. For more than that, split the work across runs.
Questions
Do I need a Google API key?
No. There is no key, no OAuth and no Google account anywhere in the run. It reads the same public feed the Maps website uses when you click the photo grid.
How do I get the link for a place?
Open the place on Google Maps and copy the address bar, or press Share and copy the short link. Both work. If you already store Google place ids, paste those instead.
Can I get the original resolution?
Ask for the width you want with imageWidth. Each row also reports the picture's native width and height, so you can tell whether you asked for more pixels than exist.
Will the run fail if a place is blocked?
No. You get an uncharged diagnostic row saying what happened and the run finishes as succeeded. A failed run would still bill the start fee, which would mean paying to be told something went wrong.
Does it tell me who uploaded each photo?
No. The feed this reads does not carry the uploader, and nothing is guessed or joined in from elsewhere. If you need attribution, the photo page on Google Maps shows it.
Can I run this on a schedule?
Yes. Nothing is held between runs. photoId is stable, so diff on it to see only what is new since last time.