Google Maps Scraper avatar

Google Maps Scraper

Pricing

from $1.95 / 1,000 record scrapeds

Go to Apify Store
Google Maps Scraper

Google Maps Scraper

Extract data from thousands of locations: get reviews, pricing, photos, and B2B contact info (names, emails, titles). Automate your workflow by scheduling recurring runs, monitoring performance, and integrating directly via our API.

Pricing

from $1.95 / 1,000 record scrapeds

Rating

4.8

(2)

Developer

F

F

Maintained by Community

Actor stats

0

Bookmarked

4

Total users

2

Monthly active users

an hour ago

Last modified

Share

Google Maps Custom Target Schema Scraper

A production-ready Apify Actor designed to scrape Google Maps business profiles. This scraper extracts deep, comprehensive data and formats its JSON output in a clean, structured schema.

💰 Pricing & Monetization

This Actor is billed under a highly transparent Pay per event + usage model:

  • Actor Start: $0.05 per 1,000 runs ($0.00005 flat rate per run).
  • Record Scraped: $1.95 per 1,000 places ($0.00195 per successfully scraped record).

🚀 Features

  • Structured Outputs: Output objects are organized in a clean, consistent, industry-standard schema format.
  • Dynamic Content Support: Fully renders modern interactive pages to extract dynamic lists and profiles seamlessly.
  • Advanced Anti-Detection Measures:
    • Ghost-Cursor: Emulates human-like bezier-curve mouse movements prior to clicks to bypass Google's advanced bot detection.
    • Automatically avoids rate limits and IP bans to ensure stable scraping.
    • Automatically dismisses cookie consent banners.
  • Deep Extraction Strategy:
    • Fuzzy Match Fallback: Utilizes Dice's Coefficient string similarity to accurately resolve and click the correct business when Google returns an ambiguous list view.
    • Extracts core data (Title, Rating, Coordinates, Address, Plus Codes).
    • Decomposes addresses into logical segments (Street, Neighborhood, City, State, Country).
    • Captures reviews distribution, booking links, and restaurant reservation providers.
    • Grabs "People Also Search For", similar nearby hotels, and hotel ads.
    • Extracts photo gallery metadata (including author, URL, and upload dates).
    • Gathers extended review metadata (including translation status, reviewer photos, detailed sub-ratings).
  • Data Sanitization Pipeline: Built-in middleware aggressively scrubs zero-width spaces, invisible byte-order marks, and soft hyphens to guarantee pristine string formatting.
  • Fast & Efficient: Optimized to scrape data quickly while minimizing execution time and cost.

Prerequisites

  • Node.js (v18 or higher recommended)
  • Apify CLI (if running or deploying to the Apify Platform)

Installation

# Clone or download the repository
cd apify_scrapper
# Install dependencies
npm install

Running Locally

To run the scraper locally, you must provide valid input data matching the expected schema.

  1. Create a directory named apify_storage/key_value_stores/default.
  2. Inside that directory, create a file named INPUT.json with the following format:
{
"searchStrings": ["Best coffee shops in Seattle", "Pizza in NYC"],
"maxResultsPerSearch": 20,
"language": "en",
"maxReviews": 5,
"maxImages": 5
}
  1. Important: Because this scraper requires Residential Proxies to bypass Google's blocks, you must run the scraper with your Apify API token exported to your environment:
# Export your Apify API Token
export APIFY_TOKEN="your_apify_api_token_here"
# Start the scraper
npm start

Extracted data will be saved to apify_storage/datasets/default/.

🐍 Programmatic Integration (Python)

You can run this Actor programmatically from your Python applications using the official apify-client library.

1. Install Apify Client

$pip install apify-client

2. Python Code Example

from apify_client import ApifyClient
# Initialize the Apify Client with your Apify API Token
client = ApifyClient("your_apify_api_token_here")
# Define the scraper inputs
run_input = {
"searchStrings": ["Pizza Hut, Boston"],
"maxResultsPerSearch": 10,
"language": "en",
"maxSearchRadius": 5,
"searchRadiusUnit": "km"
}
# Run the Actor and wait for its completion
run = client.actor("google-maps-custom-target-schema-scraper").call(run_input=run_input)
# Fetch and print the scraped results from the default dataset
dataset = client.dataset(run["defaultDatasetId"])
for item in dataset.iterate_items():
print(f"Name: {item.get('title')}")
print(f"Phone: {item.get('phone')}")
print(f"Address: {item.get('address')}")
print(f"Distance: {item.get('distanceFromSearchCenter')} {item.get('distanceUnit')}")
print("-" * 40)

Detailed Input Parameters

The Actor accepts the following JSON input parameters. These fields control what is searched and how deeply each profile is scraped.

You can target places using different search patterns based on your needs. Below are examples of how to configure your inputs:

1. Search by Business Name or Keyword (searchStrings)

Perfect for scraping matching businesses across a city or general keywords.

  • Plain Search: ["Pizza in Boston", "Dentists near Central Park"]
  • Specific Business Name: ["Union Oyster House Boston", "Giacomo's Restaurant Boston"]

Example Input:

{
"searchStrings": ["Seafood in Boston", "Union Oyster House Boston"]
}

2. Search by Latitude and Longitude (Geographic Viewport)

To search within a specific geographic coordinate area, use a Google Maps search URL with @latitude,longitude,zoomz coordinate parameters inside startUrls. The scraper will search only within that specific map viewport.

  • Format: https://www.google.com/maps/search/<query>/@<lat>,<lng>,<zoom>z
  • Example (Restaurants near Boston Common): https://www.google.com/maps/search/restaurants/@42.3554,-71.0640,16z

Example Input:

{
"startUrls": [
"https://www.google.com/maps/search/restaurants/@42.3554,-71.0640,16z"
]
}

3. Search by Polygons / Custom Areas (Grid Viewport Split)

Since Google Maps doesn't support drawing arbitrary polygons directly, you can scrape custom-shaped regions by splitting your polygon into a grid of overlapping coordinate viewports, and adding their search URLs to startUrls.

  • Example (Scraping two adjacent grid viewports covering a custom shape in the North End):
    1. Viewport 1 (North End West): https://www.google.com/maps/search/cafes/@42.3642,-71.0545,17z
    2. Viewport 2 (North End East): https://www.google.com/maps/search/cafes/@42.3655,-71.0510,17z

Example Input:

{
"startUrls": [
"https://www.google.com/maps/search/cafes/@42.3642,-71.0545,17z",
"https://www.google.com/maps/search/cafes/@42.3655,-71.0510,17z"
]
}
ParameterTypeDefaultDescription
searchStringsArray[]A list of Google Maps search queries. (Accepts strings or arrays, parsed safely with default fallback.)
startUrlsArray[]Direct Google Maps place URLs to scrape. (Accepts strings or arrays, parsed safely with default fallback.)
maxResultsPerSearchInteger20The maximum number of place profiles to scrape per search query. Defaults to 20 if missing, invalid, or malformed.
languageString"en"The language code appended to the Google Maps URLs (e.g., "en", "ar", "es").
maxReviewsInteger0The maximum number of individual reviews to extract per place. Defaults to 0 if invalid.
maxImagesInteger0The maximum number of image URLs to collect per place. Defaults to 0 if invalid.
maxSearchRadiusNumbernullThe maximum distance from the search center coordinates. Skipped if null, empty string, or invalid.
searchRadiusUnitString"km"Unit of measurement for the maximum search radius: "km" (Kilometers) or "mi" (Miles).
searchCenterLatNumbernullOptional manual override for the search center latitude. Parse-safe float.
searchCenterLngNumbernullOptional manual override for the search center longitude. Parse-safe float.

🌍 Internationalization & Arabic Localization Support

The scraper features robust built-in support for localized Middle-Eastern addresses and Arabic text parsing to prevent parsing errors and data loss:

  • Arabic Commas (، - U+060C): Google Maps profiles in Arabic regions use standard commas , and Arabic commas ،. The parser splits on both /[,،]/ to cleanly segment street, neighborhood, and city.
  • Arabic Country Mappings: Mappings are configured for Arabic country names (e.g. "السعودية" or "المملكة العربية السعودية" for Saudi Arabia, "الامارات"/"الإمارات" for United Arab Emirates, etc.) to successfully resolve ISO-2 country codes (like SA, AE, QA, BH, KW, OM, EG, JO, LB).
  • Asynchronous Search Resolving: Handles client-side JS redirects on single place searches dynamically before trying to extract feeds, preventing timeouts or empty results on direct business matches.

Exhaustive Output Data Structure

The output dataset is formatted as a structured JSON object. Below is a complete breakdown of every single key you will find in the output JSON objects.

Core Metadata & Search Context

  • searchString (String): The original query used to find this place.
  • rank (Integer): The position of this place in the search results (1-indexed).
  • searchPageUrl (String): The URL of the search results page.
  • searchPageLoadedUrl (String): The final URL after redirects.
  • isAdvertisement (Boolean): true if this place was a sponsored ad in the search results.

Place Identity & Basics

  • title (String): The business name.
  • subTitle (String): The subtitle shown below the business name on Google Maps.
  • description (String): The editorial summary/description written by Google.
  • categoryName (String): The primary category of the business (e.g., "Pizza restaurant").
  • categories (Array): A list of all categories assigned to the business.
  • price (String): The price tier (e.g., "$" or "$$$").
  • totalScore (Float): The overall star rating out of 5.0.
  • reviewsCount (Integer): The total number of reviews.

Location & Contact

  • address (String): The full, unparsed address string.
  • street (String): Parsed street address.
  • neighborhood (String): The specific neighborhood or district.
  • city (String): Parsed city name.
  • state (String): The full state or province name (e.g., "New York", not "NY").
  • postalCode (String): Parsed ZIP or postal code.
  • countryCode (String): The two-letter country code (e.g., "US", "GB").
  • location (Object): Contains the exact lat and lng coordinates.
  • locatedIn (String): If the business is located inside another building/mall.
  • plusCode (String): The Google Plus Code for the location.
  • phone (String): The formatted phone number.
  • phoneUnformatted (String): The raw phone number string with non-numeric characters removed.
  • website (String): The business's primary website URL.
  • distanceFromSearchCenter (Float): Calculated distance between the place and the resolved search center (in distanceUnit), or null if coordinates couldn't be resolved.
  • distanceUnit (String): The unit used for distance calculations ("km" or "mi"), or null.

Identifiers

  • placeId (String): The Google Maps Place ID.
  • cid (String): The unique Customer ID (decimal BigInt format).
  • fid (String): The Google Feature ID.
  • kgmid (String): The Knowledge Graph Machine ID.
  • url (String): The canonical Google Maps URL for this profile.

Business Status & Features

  • claimThisBusiness (Boolean): true if the business profile is unverified/unclaimed.
  • permanentlyClosed (Boolean): true if marked permanently closed.
  • temporarilyClosed (Boolean): true if marked temporarily closed.
  • operationalStatusText (String): Text indicator of current operational status (e.g., "Open", "Closed").
  • openingHours (Array): An array of objects [{ day, hours }].
  • additionalInfo (Object): Contains nested arrays of strings for various amenities and features.

Media & Engagement

  • imageUrl (String): The primary hero image URL for the business.
  • imagesCount (Integer): Total number of photos uploaded to the profile.
  • imageCategories (Array): The tab labels for photos (e.g., "All", "Menu", "Food").
  • images (Array): Detailed metadata for scraped images. Each object contains imageUrl, authorName, authorUrl, and uploadedAt.
  • imageUrls (Array): A flat list of the raw image URLs.

Reviews Data

  • reviewsDistribution (Object): Counts of reviews by star rating: { oneStar, twoStar, threeStar, fourStar, fiveStar }.
  • reviewsTags (Array): Popular keywords mentioned in reviews, e.g., [{ title: "pizza", count: 45 }].
  • topRelevantReviews (Array): Snippets of the top 5 'Relevant' reviews extracted directly from the main overview tab.
  • reviews (Array): Detailed individual reviews. Each object contains:
    • name, reviewerId, reviewerUrl, reviewerPhotoUrl, reviewerNumberOfReviews, isLocalGuide
    • publishAt, publishedAtDate
    • text, textTranslated, rating, stars
    • likesCount, reviewId, reviewUrl, reviewOrigin, reviewImageUrls
    • reviewContext, reviewDetailedRating (e.g., {"Food": 5, "Service": 4})
    • responseFromOwnerDate, responseFromOwnerText

Specialized Entities

  • restaurantData (Object): Contains booking provider info: { tableReservationProvider: { name, reserveTableUrl } }.
  • menu (String): Direct link to the menu.
  • reserveTableUrl (String): Direct link to book a table.
  • googleFoodUrl (String): Direct link to order food via Google.
  • hotelStars (String): Descriptive hotel rating (e.g., "4-star hotel").
  • hotelDescription (String): Editorial hotel description.
  • hotelAds (Array): Booking ads { title, googleUrl, isOfficialSite, price, url }.
  • similarHotelsNearby (Array): Competitors { name, rating, reviews, description, price }.
  • peopleAlsoSearch (Array): Related businesses { category, title, reviewsCount, totalScore }.

📊 Sample Output JSON

Below is a complete, realistic sample of a successfully scraped Google Maps place record, showing all the fields populated by the Actor:

{
"searchString": "Pizza Hut, Boston",
"rank": 1,
"searchPageUrl": "https://www.google.com/maps/search/Pizza%20Hut%2C%20Boston?hl=en",
"searchPageLoadedUrl": "https://www.google.com/maps/search/Pizza%20Hut%2C%20Boston?hl=en",
"isAdvertisement": false,
"title": "Pizza Hut",
"subTitle": null,
"description": "Popular delivery, carry-out & dine-in chain known for pizza, wings & pasta, plus sides & desserts.",
"price": "$",
"categoryName": "Pizza delivery",
"address": "7 Sycamore St, Everett, MA 02149, United States",
"neighborhood": "Everett",
"street": "7 Sycamore St",
"city": "Everett",
"postalCode": "02149",
"state": "Massachusetts",
"countryCode": "US",
"website": "https://locations.pizzahut.com/ma/everett/7-sycamore-st",
"phone": "+1 781-321-3335",
"phoneUnformatted": "+17813213335",
"claimThisBusiness": false,
"location": {
"lat": 42.4144303,
"lng": -71.0467333
},
"locatedIn": "Glendale Square",
"plusCode": "CX73+Q8 Everett, Massachusetts, USA",
"menu": "https://www.pizzahut.com/c/content/menu",
"servicesLink": null,
"totalScore": 3.6,
"permanentlyClosed": false,
"temporarilyClosed": false,
"operationalStatusText": "Open",
"placeId": "ChIJkeNmq51x44kRyEDnYBTWnsk",
"categories": [
"Pizza delivery",
"Pizza restaurant"
],
"fid": "0x89e3719dab66e391:0xc99ed61460e740c8",
"cid": "14528284830957256904",
"reviewsCount": 843,
"reviewsDistribution": {
"oneStar": 190,
"twoStar": 36,
"threeStar": 19,
"fourStar": 115,
"fiveStar": 365
},
"imagesCount": 42,
"imageCategories": [
"All",
"Interior",
"Food & drink"
],
"scrapedAt": "2026-06-15T18:25:34.710Z",
"reserveTableUrl": null,
"googleFoodUrl": null,
"hotelStars": null,
"hotelDescription": null,
"checkInDate": null,
"checkOutDate": null,
"similarHotelsNearby": [],
"hotelReviewSummary": null,
"hotelAds": [],
"openingHours": [
{
"day": "Monday",
"hours": "10:30 AM - 12:00 AM"
},
{
"day": "Tuesday",
"hours": "10:30 AM - 12:00 AM"
},
{
"day": "Wednesday",
"hours": "10:30 AM - 12:00 AM"
},
{
"day": "Thursday",
"hours": "10:30 AM - 12:00 AM"
},
{
"day": "Friday",
"hours": "10:30 AM - 1:00 AM"
},
{
"day": "Saturday",
"hours": "10:30 AM - 1:00 AM"
},
{
"day": "Sunday",
"hours": "10:30 AM - 12:00 AM"
}
],
"additionalOpeningHours": null,
"openingHoursBusinessConfirmationText": null,
"popularTimesLiveText": null,
"popularTimesLivePercent": null,
"popularTimesHistogram": null,
"peopleAlsoSearch": [],
"placesTags": [],
"reviewsTags": [
{
"title": "classic hand tossed pizza",
"count": 2
},
{
"title": "friendly cashier",
"count": 5
}
],
"additionalInfo": {
"Service options": [
{
"Curbside pickup": true
},
{
"No-contact delivery": true
},
{
"Takeout": true
}
]
},
"gasPrices": [],
"questionsAndAnswers": [],
"updatesFromCustomers": [],
"ownerUpdates": [],
"url": "https://www.google.com/maps/place/Pizza+Hut/data=!4m7!3m6!1s0x89e3719dab66e391:0xc99ed61460e740c8!8m2!3d42.4144303!4d-71.0467333!16s%2Fg%2F1thx0fxl!19sChIJkeNmq51x44kRyEDnYBTWnsk",
"imageUrl": "https://lh3.googleusercontent.com/gps-cs-s/APNQkAFofbDgfNqaqjs94bbGiyoLNvnYcQOE6DN0SBjGw0z_PrelPB97mg5IhaPur1U9wSlQAtxQBrJP0MGutBUEvrhIB2YtOOs1RCp_XsiyE4Rj2PaIhNXiGLuCB5uBvO-vlcFjAY5SJeWx1a8U=w112-h112-p-k-no",
"kgmid": "/g/1thx0fxl",
"floor": null,
"isExternalServicePlace": false,
"externalServiceProvider": null,
"externalId": null,
"webResults": [],
"parentPlaceUrl": null,
"tableReservationLinks": [],
"bookingLinks": [],
"images": [
{
"imageUrl": "https://lh3.googleusercontent.com/gps-cs-s/APNQkAFofbDgfNqaqjs94bbGiyoLNvnYcQOE6DN0SBjGw0z_PrelPB97mg5IhaPur1U9wSlQAtxQBrJP0MGutBUEvrhIB2YtOOs1RCp_XsiyE4Rj2PaIhNXiGLuCB5uBvO-vlcFjAY5SJeWx1a8U=w112-h112-p-k-no",
"authorName": "Pizza Hut",
"authorUrl": "",
"uploadedAt": "3 years ago"
}
],
"imageUrls": [
"https://lh3.googleusercontent.com/gps-cs-s/APNQkAFofbDgfNqaqjs94bbGiyoLNvnYcQOE6DN0SBjGw0z_PrelPB97mg5IhaPur1U9wSlQAtxQBrJP0MGutBUEvrhIB2YtOOs1RCp_XsiyE4Rj2PaIhNXiGLuCB5uBvO-vlcFjAY5SJeWx1a8U=w112-h112-p-k-no"
],
"reviews": [
{
"name": "Jane Doe",
"text": "Great service and the pizza was hot and fresh!",
"publishAt": "2 weeks ago",
"likesCount": 3,
"reviewId": "ChZDSUhNMG9uQkNNR09Y...",
"reviewerId": "2394829384",
"reviewerUrl": "https://www.google.com/maps/contrib/2394829384",
"reviewerPhotoUrl": "https://lh3.googleusercontent.com/a-/ALV-...",
"reviewerNumberOfReviews": 15,
"isLocalGuide": false,
"reviewOrigin": "Google",
"stars": 4,
"responseFromOwnerDate": null,
"responseFromOwnerText": null
}
],
"userPlaceNote": null,
"restaurantData": {},
"topRelevantReviews": [
{
"text": "Great service and the pizza was hot and fresh!",
"rating": 5
}
],
"distanceFromSearchCenter": 5.48,
"distanceUnit": "km"
}