Google Maps Reviews & Photos Scraper avatar

Google Maps Reviews & Photos Scraper

Pricing

Pay per event

Go to Apify Store
Google Maps Reviews & Photos Scraper

Google Maps Reviews & Photos Scraper

Two-mode Google Maps scraper. Pass search queries to discover candidate places, or Place IDs / CIDs / FIDs / short URLs / full URLs to extract reviews + Photos-tab images per place. Generic review fields: rating, date, owner response, translation, reviewer profile, helpful votes. Pay-per-event.

Pricing

Pay per event

Rating

0.0

(0)

Developer

Arnas

Arnas

Maintained by Community

Actor stats

0

Bookmarked

3

Total users

2

Monthly active users

3 days ago

Last modified

Share

Google Maps Scraper

Two-mode Google Maps scraper. Shallow mode turns search queries into candidate places (with stable Place IDs); deep mode extracts reviews + Photos-tab images for given Place IDs.

Status: private v0.1. Not yet on Apify Store.

What does Google Maps Scraper do?

This actor extracts public Google Maps data in two stages so callers can chain a discovery pass with a deep-extraction pass without reimplementing the join. It uses PlaywrightCrawler (Chromium) under the hood with residential-proxy default, session rotation, fingerprint randomization, and CONSENT cookie pre-seeding to handle the EU/UK consent wall.

  • Shallow mode — pass searchStringsArray (e.g., ["Paradise Hyderabad"]) and the actor returns one record per candidate place per query: Place ID, CID, name, address, Maps URL, lat/lng, rating, review count, categories, plus phone / website / hours when surfaced on the search result card.
  • Deep mode — pass placeIds[] containing any mix of: canonical Place IDs (ChIJ...), CIDs (numeric), FID hex pairs (0x...:0x...), short URLs (goo.gl/maps/..., maps.app.goo.gl/...), or full /maps/place/... URLs. The actor normalizes each to a canonical Place ID, then per place emits one record per review (with reviewer info, rating, date, owner response, photos when present, and the originally-translated text when the review was non-English). Optionally also emits a per-place Photos-tab bundle.

Why use Google Maps Scraper?

  • Two-stage pipeline decouples discovery from extraction — pick which candidates to deep-scrape instead of paying for full reviews on every search match.
  • Generic field set — return whatever Maps surfaces: rating, publishedAt, owner response, detected language, reviewer profile URL, reviewer review count, helpful votes, original-vs-translated text. No per-field knobs to fight with.
  • Host-allowlisted outputreviewUrl and every reviewImageUrls[] entry is guaranteed to be on *.google.com / *.googleusercontent.com / *.ggpht.com over HTTPS. Tile-rendering UIs that hard-reject other hosts can trust the contract.
  • Identifier flexibility — paste any of five common ID forms; the actor normalizes them. No "what's a CID" homework for callers.
  • Failure visibility — every record carries a status enum (ok | place_not_found | consent_wall | rate_limited | partial | parse_error). A run-summary record at end-of-run aggregates counts plus consecutiveBlocksAtEnd for detecting session-wide regressions.

How to use Google Maps Scraper

  1. Open the Apify Console for this actor (private v0.1; share access first).
  2. For discovery (Mode A): paste search queries into searchStringsArray. Adjust maxPlacesPerQuery if any single query is expected to match many places.
  3. For extraction (Mode B): paste Place IDs / CIDs / FIDs / short URLs / full URLs into placeIds[]. Adjust maxReviews, reviewsSort, and toggle includePlacePhotos / onlyWithPhotos as needed.
  4. Run. The dataset will contain records with a recordType discriminator (place, review, place_photos, run_summary); filter or split downstream.

Note: pass searchStringsArray or placeIds[], not both. Both populated is a validation error.

Input

See the Input tab for the full schema and live editor. Key fields:

{
"searchStringsArray": ["Paradise Hyderabad"], // OR
"placeIds": ["ChIJN1t_tDeuEmsRUsoyG83frY4"], // mutually exclusive
"maxReviews": 50,
"reviewsSort": "mostRelevant", // mostRelevant | newest | highestRating | lowestRating
"language": "en", // sets hl param; does NOT filter reviews
"onlyWithPhotos": false,
"includePlacePhotos": false,
"maxPlacesPerQuery": 5,
"maxPlacePhotos": 200,
"maxReviewsScanned": 0, // 0 = auto (maxReviews × 10)
"maxConcurrency": 2,
"requestTimeoutSecs": 45,
"proxyConfiguration": { "useApifyProxy": true, "apifyProxyGroups": ["RESIDENTIAL"] }
}

Output

The dataset is heterogeneous — every record carries a recordType discriminator. Filter by type or split downstream.

// recordType: "place" — emitted in shallow mode (one per candidate per query)
{
"recordType": "place",
"scrapedAt": "2026-05-04T12:00:00Z",
"status": "ok",
"searchString": "Paradise Hyderabad",
"placeId": "ChIJ...",
"cid": "15402384604550917548",
"name": "Paradise Restaurant",
"address": "...",
"googleMapsUrl": "https://www.google.com/maps/place/...",
"latitude": 17.4435,
"longitude": 78.4869,
"rating": 4.2,
"reviewCount": 43210,
"categories": ["Biryani restaurant", "Indian restaurant"],
"phone": "+91 ...",
"website": "https://...",
"hours": null
}
// recordType: "review" — emitted in deep mode (one per review)
{
"recordType": "review",
"scrapedAt": "2026-05-04T12:00:00Z",
"status": "ok",
"placeId": "ChIJ...",
"reviewUrl": "https://www.google.com/maps/contrib/.../reviews/...",
"reviewImageUrls": [
{ "url": "https://lh3.googleusercontent.com/...=w400-h300-k-no",
"originalUrl": "https://lh3.googleusercontent.com/...=s0" }
],
"text": "Great biryani.",
"name": "A. Reviewer",
"rating": 5,
"publishedAt": "2025-12-01T00:00:00Z",
"ownerResponse": { "text": "...", "publishedAt": "2025-12-02T00:00:00Z" },
"detectedLanguage": "en",
"originalText": null,
"originalLanguage": null,
"reviewerProfileUrl": "https://www.google.com/maps/contrib/...",
"reviewerReviewCount": 87,
"helpfulVotes": 12
}
// recordType: "place_photos" — emitted when includePlacePhotos=true (one per place)
{
"recordType": "place_photos",
"scrapedAt": "2026-05-04T12:00:00Z",
"status": "ok",
"placeId": "ChIJ...",
"photoUrls": [
{ "url": "https://lh3...=w400-h300-k-no", "originalUrl": "https://lh3...=s0",
"uploaderName": "...", "uploadedAt": null, "category": "By owner" }
]
}
// recordType: "run_summary" — emitted exactly once at end-of-run
{
"recordType": "run_summary",
"scrapedAt": "2026-05-04T12:34:56Z",
"runOutcome": "normal",
"placesAttempted": 10,
"placesOk": 8,
"placesNotFound": 1,
"consentWallHits": 1,
"rateLimitHits": 0,
"reviewsEmitted": 234,
"photosEmitted": 0,
"consecutiveBlocksAtEnd": 0
}

You can download the dataset in JSON, CSV, Excel, or HTML.

Data table

FieldTypeWhen emittedNotes
recordTypeenumalwaysplace / review / place_photos / run_summary
placeIdstringplace / review / place_photosCanonical ChIJ... form
reviewUrlstringreviewhttps + Google host (allowlisted)
reviewImageUrlsarrayreview (when photos present)https + Google host (allowlisted)
text / namestringreviewUsed as caption / author by tile-rendering consumers
ratingintreview when present1–5
originalText / originalLanguagestringreview when translatedPre-translation form
statusenumevery non-summary recordok / place_not_found / consent_wall / rate_limited / partial / parse_error

Pricing / cost estimation

Private v0.1 — no PPE configured. Cost on Apify is the platform compute cost (Chromium + residential proxy). Expect 30–60s per place for review scraping at default knobs; budget accordingly.

Tips

  • Default maxConcurrency: 2 is conservative — Google rate-limits aggressively. Bump only if you have a residential proxy budget for it.
  • Set language to control which language Google's UI labels render in (does NOT filter reviews). For mixed-language places, leave at default en.
  • For places known to have many photos, set includePlacePhotos: true and tune maxPlacePhotos (default 200, max 2000).
  • If the run-summary's consecutiveBlocksAtEnd is ≥3, treat it as a session-wide regression signal — re-queue from a fresh run rather than tweaking concurrency.

FAQ, disclaimers, support

  • Is this allowed? Scraping public Maps pages is a contested area; this actor is for private v0.1 use only. Public Store publish is deferred until a separate legal review (ToS, GDPR transparency obligations, abuse limits) is complete.
  • GDPR. Personal data (reviewer names, photos, profile URLs) flows through this actor. Persistence, lawful-basis assessment (Art. 6), transparency obligations toward data subjects (Art. 14), and erasure paths (Art. 17) are the caller's responsibility — this actor produces a transient dataset and does not persist anything.
  • Limitations. Maps DOM evolves; per-field extraction may degrade silently. Watch the status: 'partial' rate and the run-summary aggregate.