
Website Content Crawler
scrape.ly/store/querystone/website-content-crawlerAIDeveloper tools
1 monthly user

Extract public Google Maps places by keyword, category, location, or Maps URL. Get addresses, ratings, reviews count, phones, websites, opening hours, and GPS coordinates for every place.
Turn a keyword, a city, or a Google Maps link into a dataset of public places: name, address, phone, website, rating, and hours when Maps publishes them.
Built for lead lists, local research, and location ops that need the same search again next week, not a one-off export.
place_id:ChIJ... when you already know the place.cid links, maps.app.goo.gl links, and goo.gl/maps links. A URL that is not one of those fails the run and is not stored. A short link that does not open a public place fails the run the same way.locationQuery is kept only when that text appears on the place address. A polygon or multipolygon keeps places that fall inside that shape.scrapePlaceDetailPage) for fields that show up on the place page. A failed preview keeps the values already on the search card.language code. A two-letter countryCode is sent with the search.Request-based fetches, not a browser session per place, so a production run spends its time on Maps responses. You get a normal Scrapely dataset: schedule it, call it from the API, and export. Every stored row is a place Maps or the business's own public page actually returned. A start URL that is not Google Maps fails the run, and no error row is written. A search that returns no places exits with an empty dataset.
{
"language": "en",
"locationQuery": "New York, USA",
"maxCrawledPlacesPerSearch": 20,
"scrapeSocialMediaProfiles": {
"facebooks": false,
"instagrams": false,
"tiktoks": false,
"twitters": false,
"youtubes": false
},
"searchStringsArray": [
"restaurant"
]
}
One item from a real run of the example input, shortened. Long text is cut and lists show their first entries.
{
"title": "Gramercy Tavern",
"placeId": "ChIJvSQIgqFZwokRFYQbJdzceSs",
"address": "42 E 20th St, New York, NY 10003, United States",
"categoryName": "New American restaurant",
"categories": [
"New American restaurant"
],
"totalScore": 4.6,
"reviewsCount": 5067,
"location": {
"lat": 40.7384555,
"lng": -73.98850639999999
},
"street": "42 E 20th St",
"city": "New York",
"postalCode": "10003",
"state": "New York",
"neighborhood": "Manhattan",
"countryCode": "US",
"phone": "+1 555 0100",
"phones": [
"+1 555 0100"
],
"phoneUnformatted": "+1 555 0100",
"website": "https://www.gramercytavern.com/",
"openingHours": [
{
"day": "Monday",
"hours": "11:30 am to 11 pm"
},
{
"day": "Tuesday",
"hours": "11:30 am to 11 pm"
}
],
"imageUrl": "https://lh3.googleusercontent.com/…",
"imagesCount": 10041,
"fid": "0x89c259a1820824bd:0x2b79dcdc251b8415",
"cid": "3132777853886366741",
"businessProfileId": "18235747991152489841",
"url": "https://www.google.com/maps/search/?api=1&query=Gramercy%20Tavern&query_place_id=ChIJvSQIgqFZwokRFYQbJdzceSs"
}
The fields that change what this actor collects:
| Field | What it does |
|---|---|
searchStringsArray | Keywords, or place_id: values. Ignored when startUrls is set. |
locationQuery | Free-text location. This wins over city, state, county, postal code, country, and customGeolocation. A place is kept only when this text appears in the city, state, address, neighborhood, or street. |
city, county, state, postalCode, countryCode | Used together as the location when locationQuery is empty. City and state must match the place, the postal code must start with the requested code, and the county name must appear in the address, city, or state. |
customGeolocation | GeoJSON. A point recenters the search. A multipoint recenters on those coordinates and keeps places near them. If none are public, the run fails and stores nothing. A polygon or multipolygon also drops places outside the shape. Coordinate order is [longitude, latitude]. |
startUrls | Google Maps URLs. These take priority over search terms. |
maxCrawledPlacesPerSearch | How many places to keep per search term or URL. Default 20. The cap is 1000. |
language | Results language, default en. |
searchMatching | all, only_includes, or only_exact against the place name. |
placeMinimumStars | two through fourAndHalf. Places with no reviews are skipped. |
website | allPlaces, withWebsite, or withoutWebsite. |
skipClosedPlaces | Drop places Maps marks permanently or temporarily closed. |
categoryFilterWords | Keep places whose categories overlap these words. |
scrapePlaceDetailPage | Open each place preview and fill fields the preview actually contains. |
scrapeTableReservationProvider | Store the reserve link Maps already puts on the place (OpenTable, Resy, and the same kind of host). Provider email, phone, and address are omitted when Maps does not send them. |
scrapeContacts | Read the business website and its contact page for emails and social profile URLs. Placeholder addresses such as jane@example.com are dropped. Chain sites in the input description (McDonald's, Starbucks, and the rest of that list) are skipped. |
includeWebResults | Public web results for the place name and city, with title, URL, host, and snippet. When those pages send nothing, the place is still stored and webResults is left off. |
scrapeOrderOnline | Open the food-ordering page linked from the place. Store each provider name and link, plus the fee and ready time that page prints. |
scrapeDirectories | Businesses listed under "At this place", each with locatedIn set to the parent place. |
maximumLeadsEnrichmentRecords | Up to this many leads per place, read from the place website's home, contact, about and team pages: people (name, job title, email, phone, LinkedIn) first, then company emails. Each lead also carries the company name, website, phone, LinkedIn page, city and industry. Places without a website, and chain sites, get no leads. |
verifyLeadsEnrichmentEmails | Adds emailVerification to each lead email: syntax, disposable-domain and mail-server (MX) checks, with result, quality, subResult, free and role. The mailbox itself is not contacted, so ok means the domain accepts mail. |
scrapeSocialMediaProfiles | For each chosen platform, a profile object per link on the place website (up to 3): URL and username, plus name, description, image and the counts the public page shows (Facebook followers, Instagram followers and posts, TikTok followers, likes and videos). X returns URL and username only, because it hides details from logged-out visitors; some Instagram pages do the same. Also turns on contacts, which collects LinkedIn links in linkedIns. |
maxQuestions is reserved: Google Maps no longer shows questions and answers, so it returns nothing and does not affect the run. Leads, verification and social profiles never fail the run when a site has nothing to show.
{
"searchStringsArray": ["restaurant"],
"locationQuery": "Konstanz",
"maxCrawledPlacesPerSearch": 8,
"language": "en",
"searchMatching": "all",
"website": "allPlaces",
"skipClosedPlaces": false,
"scrapePlaceDetailPage": false
}
Each place is one dataset item. A key is present only when the Maps payload had a value. Depending on the place, that can include:
title, placeId, address, neighborhood, street, city, state, postalCode, countryCode, location, plusCode, floor, categoryName, categories, price, totalScore, reviewsCount, reviewsDistribution, reviewsTags, phone, phones, phoneUnformatted, website, menu, openingHours, additionalOpeningHours, popularTimesHistogram, popularTimesLiveText, popularTimesLivePercent, imageUrl, imagesCount, imageCategories, fid, cid, kgmid, businessProfileId, url, searchPageUrl, searchString, language, rank, scrapedAt, description, ownerDescription, subTitle, additionalInfo, claimThisBusiness, reserveTableUrl, permanentlyClosed, temporarilyClosed, wasOpenAtScrapeTime.
A hotel place can also include hotelStars, hotelDescription, checkInDate, checkOutDate, hotelAds (provider title, the price string Maps printed, googleUrl, and isOfficialSite when Maps labels that row the official site), and similarHotelsNearby. A gas station can include gasPrices with the fuel name, the price tag, and the number from that tag. popularTimesHistogram uses Su through Sa, each with hour and occupancyPercent.
When the matching add-on is on and the page has the value, an item can also include tableReservationLinks, tableReservationProviders (name and reserveTableUrl), emails, facebooks, instagrams, youtubes, tiktoks, twitters, webResults (title, url, displayedUrl, description), orderOnline, locatedIn, and parentPlaceUrl.
inputStartUrl is set for rows that came from startUrls. inputPlaceId is set for a direct place id. openingHours is a list of {day, hours}. location is {lat, lng}. Rank is the kept-place order, including directory rows.
You pay per event, plus platform usage (compute and proxy traffic).
| Event | When it is charged | Price |
|---|---|---|
| Place scraped | Each place saved to the dataset | $0.0044 |
| Filter applied | Each saved place when a filter is on (category words, minimum stars, closed places, website) | $0.0008 |
| Place details scraped | Each place whose detail page was opened and read (scrapePlaceDetailPage) | $0.0016 |
| Contact details scraped | Each place where scrapeContacts found an email or social link | $0.0024 |
| Social profile scraped | Each social profile returned with profile data | $0.008 |
| Lead scraped | Each lead record returned (maximumLeadsEnrichmentRecords) | $0.006 |
| Lead email verified | Each lead email checked (verifyLeadsEnrichmentEmails) | $0.0032 |
Add-ons are charged only when you turn the option on and only for places where it returned data.
Example: 1,000 places with no add-ons cost $4.40 in event fees plus platform usage. In our test runs platform usage for a plain search was about $0.08 to $0.16 per 1,000 places. The default 20-place search costs $0.088 in event fees.
If you set a maximum cost per run, the run saves as many places as that budget pays for and then stops cleanly.
Every field below can be set in the run form, passed through the API, or saved as a task. Unset fields fall back to the defaults shown.
searchStringsArrayarrayOptionalSearch terms
What you would type into the Google Maps search bar, e.g. English breakfast or pet shelter. Distinct terms are faster than near-synonyms (bar, restaurant, cafe). When looking for a specific business, also fill in Location. Putting the location into the search term (e.g. restaurant Pittsburgh) caps results at about 120 per term. Place IDs are accepted as place_id:ChIJ8_JBApXMDUcRDzXcYUPTGUY.
locationQuerystringOptionalLocation
One location per run, as free text. Simple formats work best, e.g. City, Country. A whole country or state is split into smaller areas automatically. City outlines can be smaller than the metro area; use Search area and language for an exact area.
startUrlsarrayOptionalGoogle Maps URLs
Optional. Google Maps search or place URLs (https://www.google.com/maps/...), also https://google.com/maps?cid=... and https://goo.gl/maps/... links. Up to 300 results per search URL.
maxCrawledPlacesPerSearchintegerOptionaldefault 20Max places per search term or URL
Number of places to return for each search term or URL. Higher numbers take longer. Leave empty to scrape every place found.
Narrow down which places are returned.
categoryFilterWordsarrayOptionalPlace categories
Only return places in these categories. Many places are miscategorised and Google Maps has over 4,000 categories, so list every category you want, including synonyms (e.g. divorce lawyer, divorce attorney, divorce service).
searchMatchingenumOptionaldefault "all"Get exact name matches (no similar results)
Filter places by how their name matches the search term, e.g. every place with chicken in its name vs. only places called Kentucky Fried Chicken.
placeMinimumStarsenumOptionaldefault ""Set a minimum star rating
Scrape only places rated at or above the selected stars. Places without reviews are skipped too, so fewer places are found.
websiteenumOptionaldefault "allPlaces"Scrape places with/without a website
Use this to exclude places without a website, or vice versa. This option is turned off by default.
skipClosedPlacesbooleanOptionaldefault falseSkip closed places
Skip places that are marked as temporary or permanently closed. Ideal for focusing on currently open places.
Results language, and an exact search area instead of Location.
languageenumOptionaldefault "en"Language
Results will be scraped in this language.
countryCodeenumOptionalCountry
Country, e.g. United States.
citystringOptionalCity
City only, e.g. Pittsburgh, without state or country. City outlines can be smaller than the metro area.
statestringOptionalState
State, e.g. Massachusetts (mainly for US addresses).
countystringOptionalCounty
County, e.g. New York County. Means different things in different countries: a US county, a Canadian regional district, a French département.
postalCodestringOptionalPostal code
One postal code, e.g. 10001. Combine it with Country only, not with City.
customGeolocationobjectOptionalCustom search area
A GeoJSON geometry, e.g. a Polygon or MultiPolygon, for an exact search area. Coordinates are [longitude, latitude].
Extra data from each place's page. Slower.
scrapePlaceDetailPagebooleanOptionaldefault falseScrape place detail page
Open each place's detail page for extra fields: review distribution and tags, image categories, popular times, opening hours, people also search, updates from customers, questions and answers, reservation links, owner updates and hotel fields. Slower, because every place needs another page load. Required by the other options in this section.
scrapeTableReservationProviderbooleanOptionaldefault falseScrape table reservation provider data
Scrape table reservation provider data like name, address, email or phone. This data is present only in restaurants that have a blue "RESERVE A TABLE" button.
scrapeOrderOnlinebooleanOptionaldefault falseScrape order online widget data
Scrape the 'Order online' section of restaurants to get pickup and delivery providers (Uber Eats, DoorDash, etc.), along with fees and estimated times.
includeWebResultsbooleanOptionaldefault falseInclude web results
Extract the "Web results" section located at the bottom of every place listing.
scrapeDirectoriesbooleanOptionaldefault falseScrape inside places (e.g. malls or shopping center)
Also scrape businesses listed inside a place, such as shops in a mall (the Directory or At this place list). Requires Scrape place detail page.
maxQuestionsintegerOptionaldefault 0Number of questions to extract
Reserved: Google Maps no longer shows questions and answers on place pages, so this field currently returns no data and does not affect the run. Kept for input compatibility.
scrapeContactsbooleanOptionaldefault falseCompany contacts enrichment
Visit each place's website for business emails and social media profiles (Facebook, Instagram, LinkedIn, X and more). Large chains such as McDonald's, Starbucks and KFC are skipped.
scrapeSocialMediaProfilesobjectOptionaldefault {"facebooks":false,"instagrams":false,"youtubes":false,"tik…Social media profile enrichment
For each selected platform, add a profile object for up to 3 links found on the place's website: URL and username always, plus what the public profile page shows without a login (name, description, image; Facebook followers and talking-about counts; Instagram followers, following and posts when the page shows them; TikTok followers, following, likes, videos and verification). X hides details from logged-out visitors, so it returns URL and username only, and some Instagram pages do the same. Turns on company contacts enrichment automatically, which also collects LinkedIn links. Never fails the run.
maximumLeadsEnrichmentRecordsintegerOptionaldefault 0Max business leads per place
Employee and company details per place with a website, taken from that website's home, contact, about and team pages: names, job titles, emails, phone numbers and LinkedIn profiles, plus company name, phone, LinkedIn and industry. People with a name come first, then company emails. 0 turns it off. Leads contain personal data, so only collect them with a legitimate reason. Large chains are skipped.
verifyLeadsEnrichmentEmailsbooleanOptionaldefault falseVerify lead emails
Check each lead's email address without contacting the mailbox: syntax, disposable-domain list and the domain's mail servers (MX record). Adds an emailVerification object with result (ok, invalid, disposable, unknown), quality (good, risky, bad, unknown), subResult, and free and role flags. ok means the domain accepts mail; the mailbox itself is not checked. Requires Max business leads per place above 0. Never fails the run.
Every run writes its results to a dataset. Preview it in the dashboard, then export it or read it through the API.
| Field | Type | Description |
|---|---|---|
| webResults | array | Web search results related to the place |
| userPlaceNote | string | User's personal note about the place |
| tableReservationLinks | array | Links for table reservations |
| tableReservationProviders | array | Table reservation providers available for the place. Populated only when `scrapeTableReservationProvider` is enabled. |
| bookingLinks | array | Links for booking (hotels, tours, etc.) |
| orderOnline | object | Online ordering options (pickup and delivery). Omitted unless `scrapeOrderOnline` is enabled. Note: Google may also omit this data in ~5% of cases. |
| questionsAndAnswers | any | Questions and answers from the Q&A section |
| ownerUpdates | array | Updates posted by the business owner |
| restaurantData | object | Restaurant-specific data |
| reviewsCount | number | null | Total number of reviews |
| title | string | Name/title of the place |
| placeId | string | Unique Google Place ID |
| address | string | null | Full address of the place |
| location | any | Geographic coordinates (latitude and longitude) |
| categories | array | Array of category names for the place |
| isAdvertisement | boolean | Whether this place is a paid advertisement |
| categoryName | string | null | Primary category name |
| totalScore | number | null | Average rating score (0-5) |
| permanentlyClosed | boolean | Whether the place is permanently closed |
| temporarilyClosed | boolean | Whether the place is temporarily closed |
| url | string | Google Maps URL for the place |
| price | string | null | Price level indicator (e.g., "$", "$$", "$$$") |
| cid | string | null | Google CID (Customer ID) - numeric identifier |
| fid | string | null | Feature ID - More info at https://dataforseo.com/help-center/what-is-cid-place-id-feature-id |
| imageUrl | string | null | URL of the main place image |
| hotelStars | string | null | Hotel star rating (for hotels) |
| scrapedAt | string | Timestamp when the data was scraped (ISO 8601) |
| searchPageUrl | string | URL of the search page where this place was found |
| searchString | string | Search query used to find this place |
| inputPlaceId | string | Place ID from the input if specified |
| inputStartUrl | string | Start URL from the input if specified |
| language | string | Language code used for scraping |
| rank | number | Position in search results (1-based) |
| kgmid | string | null | Google Knowledge Graph ID |
| businessProfileId | string | null | Business Profile ID |
| neighborhood | string | null | Neighborhood or district name |
| street | string | null | Street address including building number |
| city | string | null | City name |
| countryCode | string | null | Two-letter country code (ISO 3166-1 alpha-2) |
| postalCode | string | null | Postal or ZIP code |
| state | string | null | State or province name |
| emails | array | Array of email addresses found |
| phones | array | Array of phone numbers found |
| phonesUncertain | array | Array of uncertain/possible phone numbers |
| linkedIns | array | Array of LinkedIn profile URLs |
| twitters | array | Array of Twitter profile URLs |
| instagrams | array | Array of Instagram profile URLs |
| facebooks | array | Array of Facebook profile URLs |
| youtubes | array | Array of YouTube channel URLs |
| tiktoks | array | Array of TikTok profile URLs |
| pinterests | array | Array of Pinterest profile URLs |
| discords | array | Array of Discord server URLs |
| facebookProfiles | array | Array of Facebook profile data |
| instagramProfiles | array | Array of Instagram profile data |
| youtubeProfiles | array | Array of YouTube profile data |
| tiktokProfiles | array | Array of TikTok profile data |
| twitterProfiles | array | Array of Twitter profile data |
| description | string | null | Brief description of the place |
| phone | string | null | Formatted phone number |
| phoneUnformatted | string | null | Unformatted phone number with country code |
| imagesCount | number | Total number of images available |
| openingHours | any | Opening hours for each day of the week |
| additionalOpeningHours | object | Additional opening hours for specific services (e.g., delivery, pickup) |
| claimThisBusiness | boolean | Whether the business can be claimed by its owner |
| peopleAlsoSearch | array | Related places suggested by Google |
| additionalInfo | any | Additional information grouped by categories (accessibility, amenities, etc.) |
| reviewsTags | array | Tags extracted from reviews |
| placesTags | array | Tags related to the place itself |
| imageCategories | array | Categories available in the image gallery |
| gasPrices | array | Gas prices (for gas stations) |
| reserveTableUrl | string | null | URL for making table reservations |
| googleFoodUrl | string | null | Google Food ordering URL |
| website | string | Official website of the place |
| leadsEnrichment | array | Business leads found on the place's own public website |
| parentPlaceUrl | string | URL of the parent place (for places within other places) |
| hotelDescription | string | null | Description of the hotel |
| checkInDate | string | null | Check-in date for the search query |
| checkOutDate | string | null | Check-out date for the search query |
| similarHotelsNearby | array | Array of similar hotels in the area |
| hotelReviewSummary | any | Summary of hotel reviews by traveler type |
| hotelAds | array | Array of booking advertisements for the hotel |
| subTitle | string | null | Subtitle or secondary name |
| ownerDescription | string | null | Description written by the owner in Google Business Profile, shown in the "From the business" section. Unlike `description`, which is Google's own editorial summary. Kept in the language the owner wrote it in. |
| menu | string | null | URL to the menu (for restaurants) |
| servicesLink | string | null | URL to book services or appointments (for service businesses like barbershops, spas) |
| locatedIn | string | null | Name of the location where this place is situated |
| floor | string | null | Floor number or level |
| plusCode | string | null | Plus Code for the location |
| reviewsDistribution | any | Distribution of reviews across star ratings |
| reviewsRemovedNotice | any | Notice about reviews removed due to legal defamation complaints |
| updatesFromCustomers | any | Latest update posted by a customer |
| openingHoursBusinessConfirmationText | string | Confirmation text about opening hours from the business |
| wasOpenAtScrapeTime | boolean | null | Whether the place was open at the moment it was scraped, as reported by Google in the place's own time zone. Always `false` for permanently or temporarily closed places, and `null` when the status is missing or unrecognized, e.g. the place publishes no opening hours. |
| popularTimesLiveText | string | null | Human-readable text describing current occupancy level |
| popularTimesLivePercent | number | null | Current occupancy as a percentage (0-100) |
| popularTimesHistogram | object | Hourly occupancy data for each day of the week |
| isExternalServicePlace | boolean | Whether this place data comes from an external service |
| externalServiceProvider | string | null | Name of the external service provider |
| externalId | string | Identifier in the external service |
You pay for the events the actor reports, such as each result or page it produces. Platform usage is included in the event prices.
| Event | Charged when | Price |
|---|---|---|
| Contact details scraped | One place where contact scraping found an email or social link | $0.0024 |
| Filter applied | One saved place while a filter (category words, minimum stars, closed places, website) is on | $0.0008 |
| Lead email verified | One lead email checked for syntax, disposable domain and mail server | $0.0032 |
| Lead scraped | One business lead record returned | $0.006 |
| Place details scraped | One place whose detail page was opened and read | $0.0016 |
| Place scraped | One Google Maps place saved to the dataset | $0.0044 |
| Social profile scraped | One social media profile returned with profile data | $0.008 |
Run this actor from your own code. Swap in your API token and the examples work as-is.
import { ScrapelyClient } from 'scrapely-client'
const client = new ScrapelyClient({ token: process.env.SCRAPELY_TOKEN })
// Start the actor and wait for the run to finish.
const run = await client.actor('cornerledger/google-maps-extractor').call({
"searchStringsArray": [
"restaurant"
],
"locationQuery": "New York, USA",
"startUrls": [
{
"url": "https://www.google.com/maps/place/Yellowstone+National+Park/@44.5857951,-110.5140571,9z/data=!3m1!4b1!4m5!3m4!1s0x5351e55555555555:0xaca8f930348fe1bb!8m2!3d44.427963!4d-110.588455?hl=en-GB"
}
],
"maxCrawledPlacesPerSearch": 20,
"categoryFilterWords": [
"pizza",
"italian"
],
"searchMatching": "all",
"placeMinimumStars": "",
"website": "allPlaces",
"skipClosedPlaces": false,
"language": "en",
"countryCode": "US",
"city": "New York",
"state": "New York",
"county": "New York County",
"postalCode": "10001",
"customGeolocation": {
"type": "Point",
"coordinates": [
-73.9857,
40.7484
]
},
"scrapePlaceDetailPage": false,
"scrapeTableReservationProvider": false,
"scrapeOrderOnline": false,
"includeWebResults": false,
"scrapeDirectories": false,
"maxQuestions": 0,
"scrapeContacts": false,
"scrapeSocialMediaProfiles": {
"facebooks": false,
"instagrams": false,
"youtubes": false,
"tiktoks": false,
"twitters": false
},
"maximumLeadsEnrichmentRecords": 0
})
// Read the dataset the run produced.
const { items } = await client.dataset(run.defaultDatasetId).listItems()
console.log(items)Tokens live in your account settings. Keep them server-side: a token carries the full permissions of your account.
Connect the Scrape.ly MCP server once and your agent can run this actor as a tool. Pick your tool:
Fetch scrape.ly/agents.md and run cornerledger/google-maps-extractorPaste into Claude Code, Codex, Cursor or any AI agent. It signs you in once in the browser.
{
"mcpServers": {
"scrapely": {
"url": "https://api.scrape.ly/mcp?tools=actors,runs,storage,docs&actors=cornerledger/google-maps-extractor"
}
}
}No key in the file: your client opens the browser to sign you in the first time it connects.
Inputs the developer prepared for common jobs. Open one to see its input, then copy it to your account to run it as-is or adjust it.
Search Google Maps by keyword and city.
Places with German labels and local address format.
Open places with a website and at least 4 stars.
Scrape.ly gives you the infrastructure, the tooling, and the team to extract, process, and operationalize web data at any scale. Setup takes minutes. The advantage lasts forever.