Google Maps Reviews & Photos Scraper
Pricing
Pay per event
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
Maintained by CommunityActor stats
0
Bookmarked
3
Total users
2
Monthly active users
3 days ago
Last modified
Categories
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 output —
reviewUrland everyreviewImageUrls[]entry is guaranteed to be on*.google.com/*.googleusercontent.com/*.ggpht.comover 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
statusenum (ok | place_not_found | consent_wall | rate_limited | partial | parse_error). A run-summary record at end-of-run aggregates counts plusconsecutiveBlocksAtEndfor detecting session-wide regressions.
How to use Google Maps Scraper
- Open the Apify Console for this actor (private v0.1; share access first).
- For discovery (Mode A): paste search queries into
searchStringsArray. AdjustmaxPlacesPerQueryif any single query is expected to match many places. - For extraction (Mode B): paste Place IDs / CIDs / FIDs / short URLs / full URLs into
placeIds[]. AdjustmaxReviews,reviewsSort, and toggleincludePlacePhotos/onlyWithPhotosas needed. - Run. The dataset will contain records with a
recordTypediscriminator (place,review,place_photos,run_summary); filter or split downstream.
Note: pass
searchStringsArrayorplaceIds[], 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
| Field | Type | When emitted | Notes |
|---|---|---|---|
recordType | enum | always | place / review / place_photos / run_summary |
placeId | string | place / review / place_photos | Canonical ChIJ... form |
reviewUrl | string | review | https + Google host (allowlisted) |
reviewImageUrls | array | review (when photos present) | https + Google host (allowlisted) |
text / name | string | review | Used as caption / author by tile-rendering consumers |
rating | int | review when present | 1–5 |
originalText / originalLanguage | string | review when translated | Pre-translation form |
status | enum | every non-summary record | ok / 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: 2is conservative — Google rate-limits aggressively. Bump only if you have a residential proxy budget for it. - Set
languageto control which language Google's UI labels render in (does NOT filter reviews). For mixed-language places, leave at defaulten. - For places known to have many photos, set
includePlacePhotos: trueand tunemaxPlacePhotos(default 200, max 2000). - If the run-summary's
consecutiveBlocksAtEndis ≥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.