Google Maps Scraper: Places, Contacts & Coordinates avatar

Google Maps Scraper: Places, Contacts & Coordinates

Pricing

from $1.40 / 1,000 place scrapeds

Go to Apify Store
Google Maps Scraper: Places, Contacts & Coordinates

Google Maps Scraper: Places, Contacts & Coordinates

Search Google Maps by keyword and area. Names, categories, addresses, coordinates, phones, websites, ratings and opening hours, each with a stable Google place ID. No API key, no billing account.

Pricing

from $1.40 / 1,000 place scrapeds

Rating

0.0

(0)

Developer

Arman Hossain

Arman Hossain

Maintained by Community

Actor stats

0

Bookmarked

1

Total users

0

Monthly active users

2 days ago

Last modified

Share

Google Maps Scraper: Places by keyword and area, addresses, coordinates, phones, websites, ratings and hours

Google Maps Scraper searches Google Maps by keyword and area and returns one clean row per business: name, categories, a split address, coordinates, phone in two formats, website, rating, opening hours and accessibility attributes.

Every row carries placeId, Google's stable identifier, so results join across runs and against your own records. No API key, no billing account, no proxy setup and no browser.

Agent skill: SKILL.md

https://api.apify.com/v2/key-value-stores/t7YoTxpZEJOWvw4Ug/records/google-maps-scraper.md

What you get

Output fieldMeaning
placeIdGoogle's stable place ID, ChIJ…. Use it as your join key
cid, fidGoogle's internal identifiers for the same place
kgmidKnowledge Graph ID, /g/… or the legacy /m/…, joins to Google's entity graph
titleBusiness name
categoryName, categoriesPrimary category, and every category Google files it under
addressFormatted single-line address
street, city, postalCode, countryCodeAddress components as Google publishes them, not split out of the display string
neighborhood, localityMitte, and Berlin, Germany
latitude, longitudeCoordinates
timezoneIANA zone, e.g. Europe/Berlin
phone, phoneNationalE.164 and the locally formatted number
website, domainThe listed website and its host
ratingStar rating, always a number
reviewsCount, priceLevelAlways null. Google does not publish either in search results
permanentlyClosed, temporarilyClosedClosure flags
openingHoursPer day: the display text plus a machine-comparable interval of hour/minute pairs
attributes{group, name, available}. Read available — Google publishes absence as its own entry
editorialSummaryGoogle's one-line blurb, where it has written one
bookingLinksReservation URLs, where the place has them
imageUrlThumbnail
urlGoogle Maps link built from placeId
searchTerm, gridCell, positionWhich input produced the row, which grid cell it came from, and where it ranked
scrapedAtRun timestamp

A RUN_SUMMARY record in the key-value store holds the resolved centre point, places saved, what each filter removed, duplicates collapsed, whether each term exhausted its area, and any field that failed its shape check.

Input

FieldTypeDefaultNotes
searchTermsarrayrequiredWhat you would type into the Maps search bar. Each term is searched independently across the whole area
locationstringFree text, e.g. Berlin, Germany. Resolved once per run to a centre point, which is reported in RUN_SUMMARY
latitude, longitudenumberExplicit centre point. Overrides location. Use when a place name is ambiguous
radiusKminteger5How far around the centre to search. Smaller is denser and more local; larger covers more ground with less depth
maxPlacesPerTerminteger200Cap per term. The main cost control, since charging is per place
gridSizeinteger1Split the area into N×N cells and search each. 2 finds substantially more places at four times the requests
minRatingnumberKeep only places at or above this rating. Also removes unrated places
skipClosedbooleanfalseDrop permanently and temporarily closed places
websiteFilterstringallall, withWebsite, or withoutWebsite

Either location or a latitude/longitude pair is required. There is no global Maps search, so a run without an area fails with a named error rather than returning nothing.

{
"searchTerms": ["specialty coffee"],
"location": "Berlin, Germany",
"radiusKm": 10,
"maxPlacesPerTerm": 200,
"gridSize": 2,
"minRating": 4.0
}

Output

One record per unique place. Every field is present on every record; null means Google does not publish it for that place, never that it was skipped.

{
"placeId": "ChIJnS0AQjdRqEcRJk8cf-B4y1M",
"cid": "0x47a8513742002d9d:0x53cb78e07f1c4f26",
"kgmid": "/g/11s5wzp9md",
"title": "Fauve Coffee Berlin",
"categoryName": "Coffee shop",
"categories": ["Coffee shop", "Cafe", "Coffee roastery"],
"address": "Fauve Coffee Berlin, Neue Schönhauser Str. 8, 10178 Berlin, Germany",
"street": "Neue Schönhauser Str. 8",
"city": "Berlin",
"postalCode": "10178",
"countryCode": "DE",
"neighborhood": "Mitte",
"locality": "Berlin, Germany",
"latitude": 52.5247417,
"longitude": 13.404067,
"timezone": "Europe/Berlin",
"phone": null,
"phoneNational": null,
"website": null,
"domain": null,
"rating": 4.7,
"reviewsCount": null,
"priceLevel": null,
"permanentlyClosed": false,
"temporarilyClosed": false,
"openingHours": [{ "day": "Saturday", "hours": "11 AM–6:30 PM", "interval": [[11], [18, 30]] }],
"attributes": [{ "group": "Accessibility", "name": "Wheelchair accessible parking lot", "available": false }],
"editorialSummary": null,
"bookingLinks": null,
"imageUrl": "https://lh5.googleusercontent.com/-OaxK-r1h6zM/…/photo.jpg",
"url": "https://www.google.com/maps/place/?q=place_id:ChIJnS0AQjdRqEcRJk8cf-B4y1M",
"searchTerm": "specialty coffee",
"gridCell": "r1c1",
"position": 1,
"scrapedAt": "2026-08-08T21:04:11.000Z"
}

Use cases

  • Local lead lists. Every business of a category in a metro area, with phone and website attached.
  • Prospecting for businesses without a website. websiteFilter: "withoutWebsite" is the whole pitch for a web agency, and it is one field.
  • Geocoding and enrichment. Turn a category into rows with coordinates, a timezone and a stable ID that joins to anything else keyed on Google places.
  • Coverage and reputation tracking. Run on a schedule and watch ratings, hours and new entrants move.
  • Territory planning. gridSize with a wide radiusKm gives even coverage of a city rather than whatever ranks for one centre point.

Limits and behaviour

  • Results churn about 10% between runs. The same query returns a roughly 90% stable set of place IDs; Google reorders its own results. Join on placeId and treat a single absence as noise, not a closure.
  • How many places a term yields depends on density, not a fixed cap. A dense term in a large city paginates into the hundreds; a quiet suburb returns a handful. When a term exhausts its area, RUN_SUMMARY.ceilingHit says so — raise radiusKm or gridSize rather than maxPlacesPerTerm.
  • reviewsCount and priceLevel are always null. Google does not publish them in search results. They are declared so the column exists rather than appearing later and breaking your pipeline.
  • Attributes can be negative. {"name": "Wheelchair accessible entrance", "available": false} means the place does not have it. Reading name without available inverts the meaning.
  • Grid cells overlap. Roughly a sixth to a third of what a grid fetches is a repeat. Duplicates are collapsed on placeId and charged once; RUN_SUMMARY.duplicatesCollapsed reports the count.
  • minRating also removes unrated places. Leave it unset to keep them.
  • Address components are null where Google does not publish them, never guessed. state is absent in most of the world and is not included.
  • No reviews, photo galleries or email enrichment. Those are separate products.
  • Public data only. No authentication, and nothing that gets around access controls.

FAQ

Do I need a Google API key or a billing account? No. You supply no credentials.

Why did I get fewer places than maxPlacesPerTerm? The area ran out of matches. Check ceilingHit in RUN_SUMMARY: if it is true, widen the area instead of raising the cap.

What is placeId for? It is Google's stable identifier for a business. Use it to join runs, deduplicate against your own records, and build a Maps link.

How do I cover a whole city? Raise radiusKm to cover it and set gridSize to 2 or 3. Cells overlap, and duplicates are charged once.

Why do two runs return slightly different places? Google reorders results per request. See Limits and behaviour.

Can I filter by number of reviews? No. The review count is not in the data this Actor reads, so such a filter would silently match nothing.

Can I plug it into something else? Yes. Apify API, the client libraries, webhooks, scheduled runs, dataset exports to JSON, CSV or Excel, or MCP. The output is structured JSON.

API example

curl -X POST "https://api.apify.com/v2/acts/arman-bd~google-maps-scraper/run-sync-get-dataset-items?token=YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"searchTerms": ["dentist"],
"location": "Austin, TX",
"radiusKm": 15,
"gridSize": 2,
"websiteFilter": "withoutWebsite"
}'

JavaScript example

import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: 'YOUR_TOKEN' });
const run = await client.actor('arman-bd/google-maps-scraper').call({
searchTerms: ['specialty coffee'],
location: 'Berlin, Germany',
radiusKm: 8,
minRating: 4.0,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
for (const p of items) console.log(`${p.title}, ${p.rating}, ${p.city}${p.website ?? 'no website'}`);