OmniScrape
ProductsSolutionsGuidesDocs ↗PricingAbout
ProductsSolutionsGuidesDocs ↗PricingAbout
← All guides
Site-Specific Scrapers

Google Maps Scraper: Extract Business Listings and Place Data

Google Maps is the authoritative source for local business data on the web: NAP (name, address, phone), opening hours, star ratings, review counts, price levels, and GPS coordinates for hundreds of millions of places worldwide. The challenge is that the Maps web client is almost entirely JavaScript-driven — a raw HTTP GET returns a hollow map canvas, not a rendered listing. You need a real browser execution environment to see any place data at all.

This guide covers the practical mechanics of scraping Maps place detail pages and local search result panels: which URL shapes to target, where data lives in the rendered DOM, how to configure OmniScrape for reliable extraction, and where the official Places API is the smarter choice. For pipelines that merge Maps data with other sources, see lead generation web scraping. For a deeper look at why JavaScript-heavy pages require browser rendering, see scrape JavaScript rendered pages.

On this page

1. Place data fields worth extracting2. Google Maps URL patterns3. Where listings hide in the rendered DOM4. Maps anti-bot protections and access restrictions5. Scrape a place detail page6. Scrape local search results7. Deduplicate by place_id, not business name8. Google Places API vs scraping the web UI9. Storage, attribution, and legal considerations10. FAQ

1.Place data fields worth extracting

The value of a Maps scrape depends entirely on which fields you actually capture. Local SEO teams typically need consistent NAP data — name, address, and phone — to audit citations across directories. Market researchers layer on ratings, review volume, price level, and category tags to benchmark competitors. Mapping and logistics applications need precise lat/lng coordinates. Knowing your target fields upfront determines which CSS selectors you build and how you structure your output schema.

Some fields are always visible in the initial render; others require user interaction (expanding hours, loading reviews) and are harder to extract reliably at scale. Prioritize stable, always-visible fields for production pipelines and treat interactive fields as best-effort.

  • Business name and place_id (the only truly stable identifier across updates)
  • Formatted address, plus individual components: street, city, postal code, country
  • Phone number — note that international formatting varies significantly by region
  • Website URL and the canonical Google Maps share link for the place
  • Star rating (1–5) and total review count
  • Opening hours per weekday, including special hours for holidays
  • Primary business category and attribute tags (e.g., wheelchair accessible, outdoor seating)
  • Latitude and longitude — extractable from the URL or embedded JSON state
  • Price level indicator ($ to $$$$) when present
  • Popular times histogram when visible in the panel
  • Photos count and thumbnail URLs for visual verification

2.Google Maps URL patterns

Targeting the right URL type is the first decision in any Maps scraping project. Place detail URLs are the stable, canonical unit — they represent a single business and contain the place_id encoded in the data= segment. Avoid reverse-engineering internal RPC endpoints like batchexecute or the /maps/api/js tile calls; these are undocumented, change without notice, and are far more aggressively rate-limited than the web UI.

For search-style queries, Maps uses /maps/search/ URLs that return a scrollable list panel. These are useful for discovery — finding all businesses of a given type in a geographic area — but extracting more than the first ~20 results requires scroll simulation, since Maps uses infinite scroll rather than paginated query parameters.

  • Place detail (coordinate form): https://www.google.com/maps/place/Starbucks/@40.7580,-73.9855,17z/data=!3m1!4b1!4m6!3m5!1s0x89c25855c6480299:0x55194ec5a1ae072e
  • Place by text query: https://www.google.com/maps/search/coffee+shops+in+austin+tx
  • Place by CID: https://maps.google.com/?cid=12345678901234567890 — the CID is a legacy numeric identifier, still widely used in embeds
  • Short link: https://goo.gl/maps/... — always resolves via redirect; use data.final_url from the API response to get the canonical URL with coordinates
  • Embed URL: https://www.google.com/maps/embed?pb=... — renders a stripped-down panel, fewer fields available

3.Where listings hide in the rendered DOM

Once Maps renders in a headless browser, the place panel appears inside div[role="main"]. The business name is in an h1 element — historically classed DUwDvf, though Google rotates obfuscated class names periodically. Relying purely on class names will break; prefer combining role attributes, data-item-id attributes, and aria-label values, which are more stable because they serve accessibility purposes Google is less likely to rename.

Address data appears as a button with data-item-id="address" — the full formatted address is in its aria-label. Phone numbers use button[data-item-id^="phone:tel:"], where the tel: prefix makes them reliably selectable. The website link is an anchor with data-item-id="authority". These data-item-id values have been stable across multiple Maps UI refreshes and are your most reliable extraction hooks.

Opening hours are collapsed by default behind an expandable button. A single-pass fetch typically returns only today's hours or a summary. To get the full weekly schedule you either need to simulate a click on the hours button (possible with BaaS session control) or accept partial data. Coordinates appear in the canonical URL's @lat,lng,zoom segment after JavaScript navigation completes — check data.final_url in the API response. They also appear in APP_INITIALIZATION_STATE or INITIAL_DATA JSON blobs embedded in script tags, but that structure changes frequently and is fragile to parse.

4.Maps anti-bot protections and access restrictions

Google Maps applies several layers of protection that make naive scraping unreliable at any meaningful volume. Session tokens are generated per map load and tied to JavaScript execution — without them, requests to data endpoints return errors or empty responses. Datacenter IP ranges trigger aggressive throttling and CAPTCHAs on search queries far sooner than residential IPs. EU users see a consent banner on first load that blocks the place panel until accepted, which means your headless browser must handle that interstitial before the actual content renders.

Beyond technical protections, Google's Terms of Service for Maps Platform explicitly restrict bulk extraction, caching of place data beyond short windows, and creating competing databases from Maps content. For production commercial use cases, the official Places API is both the compliant path and often the more cost-predictable one. Scraping the web UI is more defensible for small-scale internal research, competitive monitoring, or one-time data collection where API costs are prohibitive.

One practical implication: CSS class names on Maps are obfuscated and rotated. Build your selectors around structural attributes (role, data-item-id, aria-label) rather than class strings, and plan for selector maintenance as part of your pipeline operations.

  • Full JavaScript execution required — no data is present in the initial HTTP response body
  • Session tokens tied to map initialization; raw API endpoint calls fail without them
  • Datacenter IPs throttled aggressively on search queries; use residential proxies
  • EU consent interstitials block place panel on first render
  • Obfuscated, rotating CSS class names — prefer data-item-id and aria-label selectors
  • Infinite scroll on search results — no simple page=N pagination parameter
  • place_id is stable across updates; all other identifiers are not
  • Attribution and caching restrictions in Google's Maps Platform Terms of Service

5.Scrape a place detail page

For a single place page, use mode js_rendering with a js_wait_selector targeting the business name h1. This ensures the scraper waits until the place panel has fully rendered before extracting — without it, you risk capturing an empty or partially loaded DOM. Set js_wait_timeout to at least 12 seconds; Maps initialization is slow, especially on first load from a cold session.

Match your proxy country to the market you are cataloging. A US business scraped through a European IP may show different hours formats, phone number formatting, or trigger the EU consent flow. The css_selectors map below targets the most stable DOM attributes available as of the current Maps UI. Expect to revisit the rating and review_count selectors most frequently, as those class names change more often than the data-item-id attributes.

Google Maps place detail request
json
123456789101112131415161718{
  "url": "https://www.google.com/maps/place/Blue+Bottle+Coffee/@37.7763,-122.4237,17z",
  "mode": "js_rendering",
  "output_format": "css_extractor",
  "proxy": "residential:us",
  "js_wait_selector": "h1",
  "js_wait_timeout": 12000,
  "css_selectors": {
    "name": "h1[class*=\"DUwDvf\"], h1",
    "rating": "div.F7nice span[aria-hidden=\"true\"]",
    "review_count": "div.F7nice span span[aria-label]",
    "address": "button[data-item-id=\"address\"]",
    "phone": "button[data-item-id^=\"phone:tel:\"]",
    "website": "a[data-item-id=\"authority\"]",
    "category": "button.DkEaL",
    "price_level": "span[aria-label*=\"price\"]"
  }
}

6.Scrape local search results

Search result URLs return a scrollable list panel rendered inside div[role="feed"]. Each business card is a div.Nv2PK containing the name, rating, address snippet, and a link to the place detail page. The most reliable extraction strategy is to collect place detail URLs from the search results, then scrape each individual place page for complete structured data — search cards contain abbreviated information only.

Pagination on Maps search is infinite scroll, not query-string based. A single fetch captures the first batch of results visible in the viewport — typically 10 to 20 listings. For broader coverage, use more specific geographic queries (neighborhood-level rather than city-level) to keep each result set small and complete, rather than trying to scroll through hundreds of results programmatically.

Set js_wait_selector to div[role="feed"] and give it a generous timeout. The feed container renders after the map tiles and initial JavaScript initialization complete, so 15 seconds is a reasonable minimum on residential proxies.

Maps local search results request
json
123456789101112131415{
  "url": "https://www.google.com/maps/search/plumbers+near+denver+co",
  "mode": "js_rendering",
  "output_format": "css_extractor",
  "proxy": "residential:us",
  "js_wait_selector": "div[role=\"feed\"]",
  "js_wait_timeout": 15000,
  "css_selectors": {
    "business_names": "div.Nv2PK div.qBF1Pd",
    "ratings": "div.Nv2PK span.MW4etd",
    "review_counts": "div.Nv2PK span.UY7F9",
    "address_snippets": "div.Nv2PK span.W4Efsd:nth-child(2)",
    "place_links": "div.Nv2PK a.hfpxzc"
  }
}

7.Deduplicate by place_id, not business name

"Starbucks" appears tens of thousands of times across Maps. "McDonald's" more. Deduplication by name alone is useless. The place_id is Google's own stable identifier for a physical location — it persists across business name changes, address corrections, and Maps UI updates. Extract it from the data=!... segment of the place URL, specifically the hex string following 1s0x in the encoded data blob, or from links within the rendered page that contain the full place URL.

Store place_id as your primary key. When you re-scrape a location for updated hours or ratings, use place_id to match the existing record and update in place rather than creating a duplicate. This keeps your dataset clean across incremental refresh cycles.

When merging Maps data with other sources — Yelp, Apple Maps, Foursquare, or your own CRM — fuzzy name matching alone will create both false positives (different businesses with similar names) and false negatives (the same business with slightly different name spellings). Use coordinate proximity (within ~50 meters) combined with phone number match as secondary deduplication keys. Phone number is particularly reliable because it is business-controlled and changes infrequently.

8.Google Places API vs scraping the web UI

Google's Places API (New) returns structured JSON with explicit field-level pricing, SLA guarantees, and terms that permit commercial use with attribution. For production pipelines where data quality, reliability, and legal clarity matter, the Places API is the right tool. It handles pagination, returns place_id natively, and does not require you to maintain CSS selectors against a UI that changes without notice.

Scraping the Maps web UI is technically feasible for small-scale internal projects — competitive monitoring, one-time research, or augmenting existing datasets where API costs are prohibitive for the volume needed. The tradeoffs are real: selector maintenance overhead, higher per-request cost due to mandatory js_rendering, and ToS exposure for any commercial redistribution of the data. Evaluate both paths against your actual use case volume and legal requirements before committing to an architecture.

For a detailed breakdown of why Maps always requires browser-mode rendering and how to configure it efficiently, see scrape JavaScript rendered pages. For combining Maps data with other local sources in a lead generation context, see lead generation web scraping.

9.Storage, attribution, and legal considerations

Google's Maps Platform Terms of Service impose specific restrictions on what you can do with Maps content beyond the moment of display. Caching place data beyond short windows, bulk exporting to external databases, and building products that compete with or substitute for Google Maps are all restricted. Even data that is technically public — a business's phone number or address — carries contractual limitations when extracted from Maps specifically.

Attribution requirements apply when displaying Maps-derived content: you must show the Google logo and comply with the attribution guidelines in the Maps Platform Terms. Storing lat/lng coordinates extracted from Maps URLs is a grey area; coordinates themselves are not copyrightable, but the database as a whole may be subject to database rights in some jurisdictions.

The practical guidance: for internal analytics, competitive research, or one-time data collection at low volume, the risk profile is manageable. For building a sellable local business directory, a public API, or any product where Maps data is a core commercial asset, get legal review before proceeding. The Places API with its explicit commercial terms is the lower-risk path for those use cases.

Frequently asked questions

Can I use mode 'auto' instead of 'js_rendering' for Google Maps?

You can set mode to 'auto' and OmniScrape will attempt a fast HTTP fetch first, then escalate to js_rendering if the response looks like an empty shell. In practice, Maps almost always triggers the escalation, so you will be billed at js_rendering rates regardless. Setting mode to 'js_rendering' directly avoids the wasted fast-lane attempt and gets you to the rendered content faster. Always pair it with js_wait_selector on 'h1' or 'div[role="feed"]' to ensure the panel is fully loaded before extraction runs.

How do I extract coordinates from a Maps page?

The most reliable source is the canonical URL after JavaScript navigation completes. Check data.final_url in the API response — it will contain the @lat,lng,zoom segment even if the input URL was a short link or CID URL. Parse the segment with a regex like /@(-?\d+\.\d+),(-?\d+\.\d+)/. Embedded JSON state blobs (APP_INITIALIZATION_STATE, INITIAL_DATA) in script tags also contain coordinates but their structure changes frequently and requires fragile parsing — treat them as a fallback only.

Why are opening hours incomplete or missing from my scrape?

Hours are collapsed behind an expandable button in the Maps UI. A single-pass fetch typically captures only today's hours or a brief summary line. To get the full weekly schedule, you need to simulate a click on the hours expansion button during the browser session — this requires BaaS session control or a custom automation layer on top of the rendered page. For monitoring use cases where full hours are not critical, the summary line (e.g., 'Open until 9 PM') is usually present in the initial render and sufficient.

What is the best way to handle EU consent banners on Maps?

Google shows a consent interstitial on first load for users in EU/EEA regions. If your proxy routes through a European IP, the headless browser will hit this banner before the place panel renders, and your js_wait_selector will time out. The fix is to use a US or non-EU residential proxy when scraping non-EU businesses, or to handle the consent click in your automation flow for EU-region scraping. OmniScrape's enable_solver option handles some cookie consent dialogs automatically — set enable_solver: true as a first attempt.

How many results does a Maps search URL return, and can I paginate?

A single fetch of a Maps search URL captures the listings visible in the initial viewport — typically 10 to 20 results. Maps uses infinite scroll, not URL-based pagination, so there is no ?page=2 or &start=20 parameter. For broader coverage, the most practical approach is to break your query into smaller geographic areas (neighborhood or ZIP code level) so each result set is complete within the first load. Alternatively, use BaaS to programmatically scroll the feed container and trigger additional result loads.

How do I get a place_id from the Maps URL?

The place_id is encoded in the data= query parameter of a full Maps place URL. Look for the hex string following '1s0x' in the encoded blob — for example, '1s0x89c25855c6480299:0x55194ec5a1ae072e' decodes to place_id ChIJmVm8xFVYwokRbi6hWsVEFVU. You can also find it in links within the rendered page: any anchor pointing to a Maps place URL will contain the same data= segment. Store place_id as your primary key — it is the only identifier that remains stable across business name changes, address corrections, and Maps UI updates.

Is scraping Google Maps legal for commercial use?

Technical feasibility and legal permissibility are separate questions. Google's Maps Platform Terms of Service prohibit bulk extraction, caching beyond short windows, and building competing databases from Maps content. The ToS apply regardless of how the data is accessed — web scraping or API. For commercial use cases involving redistribution or productization of Maps-derived data, the official Places API with its explicit commercial terms is the appropriate path. Low-volume internal research carries a different risk profile than building a sellable directory. Get legal counsel before committing to any architecture where Maps data is a core commercial asset.

Related guides

  • Lead Generation Web Scraping: Compliant Inbound Enrichment for Sales Teams
  • Scrape JavaScript-Rendered Pages: SPAs, Hydration, and Hidden APIs
  • SERP Web Scraping: Agency Rank Tracking Workflow
  • Web Scraping with Python

Ready to scrape without blocks?

Get your API key in minutes. Test protected URLs from the dashboard — no credit card required to start.

Ready to get started?

Start scraping protected sites today — no credit card required.

OmniScrape

Web scraping infrastructure for developers. One API call to bypass any protection.

All systems operational

Product

  • Web Unlocker
  • Browser-as-a-Service
  • Residential Proxies
  • Pricing

Developers

  • API Reference ↗
  • Quickstart ↗
  • All Guides
  • Use Cases
  • Status

Company

  • About
  • Contact

Legal

  • Privacy Policy
  • Terms of Service
  • Refund Policy
  • Cookie Policy
  • Acceptable Use

Solutions

  • E-commerce Web Scraping: Catalog Intelligence at Production Scale
  • Real Estate Web Scraping: Listings, Comps, and Market Data
  • SERP Web Scraping: Agency Rank Tracking Workflow
  • Job Board Web Scraping: HR Tech Pipeline for Labor Market Intelligence
  • Price Monitoring with Web Scraping: A Practical Developer Guide
  • Lead Generation Web Scraping: Compliant Inbound Enrichment for Sales Teams
  • Market Research Web Scraping: Multi-Geo Data Collection for Research Firms
  • Sentiment Analysis Web Scraping: Build a Production Review Pipeline
  • Logistics Web Scraping: Carrier Rates, Port ETAs, and Sailing Schedules
  • Social Media Web Scraping: Brand Mention Monitoring from Public Pages
  • LLM Training Data Scraping: Building Clean Web Corpora
  • Travel Web Scraping: Hotel Rates, Flight Fares & Parity Monitoring

Web Scraping by Language

  • Web Scraping with Python
  • Web Scraping with Node.js: fetch, Cheerio, and the OmniScrape API
  • Web Scraping with Java: HttpClient, Jsoup, and OmniScrape API
  • Web Scraping with PHP
  • Web Scraping with Go (Golang)
  • Web Scraping with Ruby: Faraday, Nokogiri, Sidekiq & OmniScrape
  • Web Scraping with C#: HttpClient, AngleSharp, and OmniScrape API
  • Web Scraping with Rust
  • Web Scraping with R: httr2, rvest, and the OmniScrape API
  • Web Scraping with C++
  • Web Scraping with Elixir
  • Web Scraping with Perl: Mojo::UserAgent, Mojo::DOM, and OmniScrape

Anti-Bot Bypass

  • How to Bypass Cloudflare When Web Scraping
  • How to Bypass DataDome When Web Scraping
  • How to Bypass Akamai Bot Manager When Web Scraping
  • How to Bypass PerimeterX (HUMAN Security) When Web Scraping
  • Bypassing AWS WAF When Web Scraping: Rate Rules, Bot Control, and Residential Proxies
  • How to Bypass Imperva (Incapsula) When Web Scraping
  • How to Bypass Kasada Bot Protection When Web Scraping
  • How to Bypass F5 BIG-IP Bot Defense When Web Scraping
  • How to Bypass Distil Networks When Web Scraping
  • How to Bypass reCAPTCHA When Web Scraping

Scraping Tools

  • Playwright Web Scraping: Practical Patterns for Protected Sites
  • Puppeteer Web Scraping: Patterns, Anti-Bot Limits, and BaaS Integration
  • Selenium Web Scraping: Practical Patterns for Real-World Projects
  • Scrapy Web Scraping with OmniScrape: Download Middleware, Pipelines, and Scale
  • Beautiful Soup Web Scraping: A Practical Guide
  • cURL Web Scraping: Shell-Native Patterns with OmniScrape
  • HTTPX Web Scraping: Async Python with OmniScrape
  • Cheerio Web Scraping: A Practical Guide

Site-Specific Scrapers

  • Amazon Scraper: Product Data, Buy Box, Reviews, and Multi-Marketplace
  • Google Search Scraper: Extract SERP Rankings and Features
  • Google Maps Scraper: Extract Business Listings and Place Data
  • LinkedIn Scraper: Companies, Jobs, and Public Profiles
  • Walmart Scraper: Prices, Stock, Rollback Deals, and Fulfillment Data
  • eBay Scraper: Extract Listings, Auctions, and Sold Prices
  • Shopify Scraper: Products, Variants, and JSON Endpoints
  • Indeed Scraper: Extract Job Listings, Salaries, and Company Data
  • Zillow Scraper: Extract Listings, Zestimates, and Price History
  • Reddit Scraper: Posts, Comments, and Subreddit Data
  • X (Twitter) Scraper: Tweets, Profiles, and Hashtags
  • Instagram Scraper: Posts, Reels, and Profile Metrics
  • TikTok Scraper: Extract Videos, Hashtags, and Trend Data
  • YouTube Scraper: Extract Video Metadata, Comments, and Channel Stats
  • Booking.com Scraper: Hotel Rates, Room Types, and Availability
  • Airbnb Scraper: Listings, Calendars, and Nightly Rates
  • Crunchbase Scraper: Extract Funding Rounds, Companies, and Investors
  • Yelp Scraper: Extract Business Listings, Ratings, and Reviews
  • Glassdoor Scraper: Employer Ratings, Salaries, and Review Data
  • Trustpilot Scraper: TrustScore, Star Distribution, and Review Monitoring

How We Compare

  • OmniScrape vs ScrapingBee
  • OmniScrape vs ZenRows
  • OmniScrape vs ScraperAPI: A Practical Developer Comparison
  • OmniScrape vs Bright Data: Which Web Scraping Platform Fits Your Team?
  • OmniScrape vs Oxylabs
  • OmniScrape vs Smartproxy
  • OmniScrape vs Crawlbase: API Design, Observability, and Migration Guide
  • OmniScrape vs Apify

Web Scraping Guides

  • Web Scraping Without Getting Blocked
  • Web Scraping Proxy Guide: Types, Sessions, Geo, and OmniScrape Integration
  • Solve CAPTCHAs While Web Scraping
  • Web Scraping vs Web Crawling: Architecture, Patterns, and When to Use Each
  • Headless Browser Scraping: When to Use It and How to Do It Right
  • Web Scraping API: Endpoint, Modes, Output Formats & Integration Patterns
  • Rotating Proxies for Web Scraping: Policies, Session Binding, and Geo Pools
  • Scrape JavaScript-Rendered Pages: SPAs, Hydration, and Hidden APIs

© 2026 OmniScrape. All rights reserved.

PrivacyTermsRefundsAcceptable Use