Request a tool
All toolsAutomationsGuidesMCP serverRequest a toolPlatformsCategories
Google Maps Place Photos Scraper icon

Google Maps Place Photos Scraper

Scrape the photos and videos on a Google Maps place: full-size image URL, caption, pixel size, upload date and coordinates. No API key, no account.

16 runs on Apify $0.00023 per photo ($0.23 / 1,000)
Run this in the cloudRun on Apify →

Developer & Research Tools

How it works

  1. 1
    Open it on Apify

    Hit Run on Apify — it opens the tool in the cloud, no install.

  2. 2
    Set the inputs

    Adjust placeUrls, maxPhotosPerPlace, maxPhotos (sensible defaults are pre-filled).

  3. 3
    Click Run

    The tool runs on Apify’s cloud and collects the data for you.

  4. 4
    Export the results

    Download as JSON, CSV or Excel, or pipe straight into your app, Google Sheets, or an AI agent.

Pricing

$0.00023 per photo = $0.23 per 1,000

You are charged forWhenPrice
Photo scrapedOne photo from a place gallery, with its size, upload date and caption where there is one. Blocked runs are never charged.$0.00023

Pay-per-event pricing: you are billed per result, not per subscription. Billing is handled by Apify on your own account. These are the live Apify store prices, in effect since 2026-09-20, and they are what you are actually charged.

Inputs

FieldWhat it doesType
placeUrlsThe places whose pictures you want, 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... place id if you already have one.array
maxPhotosPerPlaceCeiling on how many rows a single place may produce.integer
maxPhotosTotal rows for the whole run, shared evenly between your places. Keep it low while you are testing - you pay per row.integer
mediaTypeKeep everything, drop the videos and Street View panoramas, or keep only video clips.string
imageWidthWidth for the photoUrl link on each row. The height follows the picture's own aspect ratio. Each row also reports the native size, so you can tell when you asked for more pixels than exist.integer
languageTwo-letter interface language. It changes captions Google has translated, not the pictures themselves.string
countryTwo-letter country code used for the request.string
proxyUrlsLeave this empty. By default the run rotates a large pool of addresses that cost you nothing per gigabyte. Fill it in only if you specifically want the traffic to leave through proxy servers you already pay for, in the form http://user:pass@host:port.array

What you get

A structured dataset — each result includes fields like:

placeNamemediaTypephotoUrlcaptionwidthheightuploadedAtphotoIdplaceUrl

Export every run as JSON, CSV or Excel, or send it to your app, a database, Google Sheets, or an AI agent.

Related tools in Developer & Research Tools

Other ready-to-run tools in the same category — all pay-per-use on the Apify cloud.

GitHub Scraper iconDeveloper & Research Tools

GitHub Scraper

Search GitHub repos and users: stars, forks, language, topics, licence, plus user bio, company and followers. No token needed. $0.90 per 1,000 rows.

18 use cases

Stack Overflow / Stack Exchange Scraper iconDeveloper & Research Tools

Stack Overflow / Stack Exchange Scraper

Search Stack Overflow and Stack Exchange by keyword or tag. Score, answer count, views, reputation and body text. $2 per 1,000 questions.

2 use cases

Package Registry Scraper (npm + PyPI) iconDeveloper & Research Tools

Package Registry Scraper (npm + PyPI)

Get npm and PyPI package metadata as JSON. Version, license, author, repo, keywords and npm monthly downloads. $2 per 1,000 packages.

2 use cases

arXiv Scraper iconDeveloper & Research Tools

arXiv Scraper

Search arXiv papers by title, author, abstract or category. Get full abstracts, authors, categories, DOI, dates and PDF links. $2 per 1,000 papers.

2 use cases

OpenAlex Scholarly Works Scraper iconDeveloper & Research Tools

OpenAlex Scholarly Works Scraper

Search 250M+ OpenAlex papers with no API key. Get titles, authors, venue, year, citations, DOI, OA links and full abstracts. $2.00 per 1,000 papers.

2 use cases

Crossref Scholarly Works Scraper iconDeveloper & Research Tools

Crossref Scholarly Works Scraper

Search 150M+ papers on Crossref: DOI, title, authors, journal, publisher, date, citations and abstract. No API key. $1.00 per 1,000 works.

2 use cases

See all Developer & Research Tools →

Google Maps Place Photos Scraper

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.

PhotosTotal cost
100$0.0235
1,000$0.2305
10,000$2.3005
100,000$23.0005

What is actually charged

  • One photo-scraped event 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
}
FieldWhat it does
placeUrlsOne 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.
maxPhotosPerPlaceCeiling on rows from a single place. Default 30.
maxPhotosTotal rows for the whole run, shared evenly between your places. Default 200. Keep it low while you are testing — you pay per row.
mediaTypeall (default), photos to drop videos and Street View, or videos to keep only clips.
imageWidthPixel width for the photoUrl link. Default 1600. The height follows the original aspect ratio.
languageTwo-letter interface language, default en. It changes captions Google has translated, not the pictures.
countryTwo-letter country for the request, default us.
proxyUrlsLeave 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.
  • mediaTypephoto, video or street_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 what maps?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": true and "recordType": "photo". One billed event each.
  • The sample row carries "_sample": true and "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": false and an errorCode you can switch on: PLACE_NOT_RESOLVED when the link could not be tied to a place, NO_PHOTOS when the place genuinely shows none, BLOCKED when every request the run made was refused the gallery, PHOTOS_PARSE when the feed answered with something else, NETWORK when it could not be reached, and TIME_BUDGET when 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 ChIJ ids 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.