Google Maps Extractor Listing Completeness Audit avatar

Google Maps Extractor Listing Completeness Audit

Pricing

from $5.99 / 1,000 results

Go to Apify Store
Google Maps Extractor Listing Completeness Audit

Google Maps Extractor Listing Completeness Audit

Google Maps Extractor: Extract business listings with names, categories, ratings, reviews, addresses, phone numbers, websites, hours, and Google Maps URLs. Audit listing completeness to identify missing business information, optimize local profiles, and uncover data quality gaps.

Pricing

from $5.99 / 1,000 results

Rating

0.0

(0)

Developer

Scrapier

Scrapier

Maintained by Community

Actor stats

0

Bookmarked

9

Total users

0

Monthly active users

10 days ago

Last modified

Share

Google Maps Extractor — Score Business Listing Completeness

Google Maps Extractor — Listing Completeness Audit scrapes Google Maps business listings and scores how complete each Google Business Profile is: a profile_completeness_score from 0 to 100, a missing_fields gap list, and the evidence behind both. Every field the standard Google Maps Extractor returns — address, phone, hours, rating, place ID — rides on the same row. Results come back as structured JSON, ready to filter into a prospect list, pass to an LLM, or load into a spreadsheet. No Google account, API key, or Places API billing required.


🧭 What is Google Maps Extractor — Listing Completeness Audit?

Google Maps Extractor — Listing Completeness Audit searches Google Maps by business category and area, then audits every distinct listing it finds against six Google Business Profile completeness components — with a seventh, Street View, if you turn on the extra lookup. It runs on the same tile-sweep collection engine as the base Google Maps Extractor, so every row still carries the base extractor's address, rating, phone and category columns. What sets this variant apart is the audit layer: a 0-100 profile_completeness_score, a missing_fields gap list naming exactly what each listing lacks, and weak-listing filters that keep only the profiles worth working. No Google account, API key, or Google Places API billing is required — the Actor connects through Apify Proxy like any other web request.

  • 📛 Business listing data — name, address, phone, website, category tags, coordinates and today's opening hours, identical to the base extractor
  • 🩺 Completeness score and gap listprofile_completeness_score plus a missing_fields array naming which of the 6 (or 7) components are absent
  • 🕳️ Weak-listing filtersmaxCompletenessScore and requiredGaps keep only the listings worth pitching, so the filtering happens in the run instead of in a spreadsheet afterward
  • 🛣️ Optional Street View & owner lookupauditMediaDepth adds has_street_view, street_view_thumbnail and owner_display_name for one extra request per listing
  • 🗺️ Query controlslocations + keywords swept across a geocoded tile grid, or urls to audit from a specific Google Maps link instead
  • 📐 Area confinementrestrictToSearchArea drops listings outside the requested bounding box, after first checking that the connection is honouring the requested coordinates at all

📦 What data can you get with Google Maps Extractor — Listing Completeness Audit?

Every audited listing returns three kinds of data on the same row: the base Google Maps listing fields, the completeness audit fields, and the evidence behind the score.

Result TypeExtracted FieldsPrimary Use Case
Business listingname, full_address, street_address, city, state, zip, country_code, website, phone, avg_rating, tags, place_id, lat, long, hours, rowTypeStandard NAP data and prospecting
Completeness auditprofile_completeness_score, missing_fields, present_field_count, scored_component_count, has_website, has_phone, has_hours, has_description, has_accessibility_info, has_booking_link, has_street_viewScoring, sorting and filtering weak listings
Evidencedescription, description_tagline, accessibility_features, neighborhood, thumbnail_url, street_view_thumbnail, owner_display_nameProof behind the score, outreach personalization

🩺 Listing completeness score and gap list

profile_completeness_score is a weighted percentage of the components that were actually evaluated for that listing — website (20), phone (20), today's opening hours (15), editorial description (15), accessibility info (10) and booking/ordering link (10), plus Street View (5) when the extra lookup is on. missing_fields lists exactly which of those failed, using the same values as the requiredGaps filter, so the two line up 1:1. With the Street View lookup off, that component is dropped from both sides of the ratio rather than counted as a gap — scored_component_count tells you whether a row was judged on 6 or 7 components. A photo/gallery-size component was deliberately not built: the place-detail lookup this Actor uses returns exactly one photo on every listing, so "has a photo" would be true 100% of the time and would only inflate every score by a fixed amount.

{
"profile_completeness_score": 55,
"missing_fields": ["description", "booking_link"],
"present_field_count": 5,
"scored_component_count": 7,
"has_website": true,
"has_phone": true,
"has_hours": true,
"has_description": false,
"has_accessibility_info": true,
"has_booking_link": false,
"has_street_view": true
}

📛 Business listing fields

The base row carried over from the standard Google Maps Extractor: full_address is Google's own formatted one-line address, not a concatenation of parts, and hours is a list of {day, hours, opens_at, closes_at} objects with "HH:MM" start/end times. place_id is Google's stable identifier, used internally to deduplicate listings across areas and categories. This is the data set most Google Maps scrapers stop at — here it rides on the same row as the audit fields above, so a lead list and a completeness score never need to be joined manually.


🆚 How does this differ from the official Google Places API?

The Google Places API (New) returns place fields you request by SKU tier, but it does not compute a completeness score or a gap list — that logic has to be built by whoever calls it. As of 2026-08-04, its published documentation (Place Details (New)) shows phone number, opening hours, price level, rating and website gated behind the "Enterprise" SKU, and reviews/accessibility/amenities behind "Enterprise + Atmosphere" — each billed per request against a Google Cloud project.

FeatureGoogle Places API (New)Google Maps Extractor — Listing Completeness Audit
Search by keyword + areaYes, via Text Search / Nearby Search endpointsYes, via keywords + locations swept across a tile grid
Listing completeness scoreNot computed — raw fields onlyprofile_completeness_score and missing_fields, computed automatically
Setup requirementAPI key + billing-enabled Google Cloud projectAn Apify account and a run — no API key
Pricing modelPer-field SKU tiers, billed per requestOne pay-per-event charge per audited listing returned
Phone, hours, websiteGated behind the "Enterprise" SKUIncluded on every row
Accessibility, booking linkGated behind "Enterprise + Atmosphere"Included on every row, no tier gating
Area-wide sweep with confinement checkNot applicable — per-query results, no bounding-box verificationGeo-preflight check confirms the connection honours the requested area before saving

The official API is the right choice for a production app that needs a directly Google-operated endpoint and pays per field it actually uses. This Actor is the better fit for bulk prospecting runs across many areas and categories, where an automatic completeness score matters more than a direct Google support contract.


🔍 What is the difference between a Google Maps scraper and a Google Business Profile completeness audit?

A Google Maps scraper returns a list of places — name, address, phone, rating. A Google Business Profile completeness audit takes that same list and asks a second question for every row: what is this listing missing? The distinction matters because a raw place list treats every listing the same, while a local-SEO or lead-generation workflow only cares about the listings with a gap to sell against — a missing website, no booking link, no accessibility info.

Google Maps Extractor — Listing Completeness Audit returns both in one run: the plain listing data (the JSON object's full_address, phone, website, hours and related keys) and the audit data (profile_completeness_score, missing_fields, and the has_* booleans) on the same row, plus filters (maxCompletenessScore, requiredGaps) that turn the place list into a prospect list directly, without a separate scoring step afterward.


🛠️ How to scrape Google Maps business listings with Google Maps Extractor?

  1. Open Google Maps Extractor — Listing Completeness Audit on the Apify Store and click Try for free (or Run, if you already have it saved).
  2. Enter at least one business category in keywords — this is the Actor's only required field, e.g. ["dentists"].
  3. Add one or more areas in locations (e.g. ["Austin", "Brooklyn"]), or paste a Google Maps link into urls instead.
  4. Set the real query controls: maxCompletenessScore and requiredGaps to define what counts as a weak listing, auditMediaDepth to turn the Street View lookup on or off, and maxResults for how many audited listings you want back.
  5. Click Start, then download the results as JSON, CSV, Excel or the Apify API once the run finishes.
{
"locations": ["Austin"],
"keywords": ["dentists"],
"maxCompletenessScore": 70,
"requiredGaps": ["website"]
}

📚 How to run multiple areas and categories in one job

locations and keywords are both arrays: every category is searched in every area, so two areas and three categories run as six searches in the same job, sharing one maxResults budget and deduplicating by Google place ID across all of them. urls runs alongside them as additional targets rather than replacing them. There is no separate concurrency setting to configure — the Actor manages its own tile-sweep parallelism internally.


⬇️ Input

Every field below comes straight from the Actor's input schema (.actor/actor.json) — same names, same types, same defaults.

ParameterRequiredTypeDescriptionExample Value
locationsNoarray of stringsCity, district or region names, e.g. Austin, Brooklyn, Manchester. One area per line — several areas run one after another. Default: ["New York"].["Austin", "Brooklyn"]
keywordsYesarray of stringsWhat the listings should be, e.g. dentists, coffee shops, plumbers, hair salons. Each category is searched in every area above. Default: ["coffee shops"].["dentists"]
maxCompletenessScoreNointeger (0–100)A listing is kept only when its profile_completeness_score is at or below this number. Lower values return fewer, more broken listings. Default: 100 (keep everything).60
requiredGapsNoarray (enum, multi-select)A listing is kept only when it is missing every item ticked. Enum values: website, phone, hours, description, accessibility_info, booking_link, street_view. Default: [].["website"]
auditMediaDepthNostring (enum)fetch = one extra lookup per listing, adding has_street_view, street_view_thumbnail and owner_display_name, scoring 7 components instead of 6. skip = listing payload only. Default: "fetch"."fetch"
maxResultsNointeger (1–10000)Total unique listings saved across all areas, categories and links. Duplicates are merged by Google place ID. Default: 20.50
restrictToSearchAreaNobooleanDrops every listing whose coordinates fall outside the geocoded bounding box of the requested area, after verifying the connection honours the requested coordinates. Default: true.true
urlsNoarray of stringsFull Google Maps links, e.g. https://www.google.com/maps/search/dentists/@30.2672,-97.7431,13z — the search term and coordinates are both read from the link. Default: [].["https://www.google.com/maps/search/dentists/@30.2672,-97.7431,13z"]
proxyConfigurationNoobjectYour proxy groups and country, passed through unchanged. Default: {"useApifyProxy": true}.{"useApifyProxy": true}

Example JSON input:

{
"locations": ["Austin"],
"keywords": ["dentists"],
"maxCompletenessScore": 70,
"requiredGaps": ["website"],
"auditMediaDepth": "fetch",
"maxResults": 50,
"restrictToSearchArea": true,
"urls": [],
"proxyConfiguration": { "useApifyProxy": true }
}

⚠️ Pitfall: requiredGaps only accepts the seven enum values above — anything else is silently dropped rather than erroring. If you tick street_view in requiredGaps while auditMediaDepth is set to skip, the Actor turns the Street View lookup back on automatically for that run, because that gap cannot be judged without it. keywords is the only field the schema marks as required, but the run itself also needs at least one of locations or urls — an empty run with neither fails immediately rather than silently returning nothing.


⬆️ Output

Each run produces one dataset row per audited listing — typed, normalized JSON with a consistent schema across runs. Results can be downloaded as JSON, CSV, Excel, XML or HTML directly from the Apify Console, or pulled through the Apify API in the same formats. Three dataset views are provided on the Actor's page: Audit score & gaps (the default, 19 columns), Evidence behind the score, and All Google Maps place fields — all three read from the same underlying rows shown below.

📄 Scraped results

[
{
"rowType": "place",
"name": "Katz's Delicatessen",
"place_id": "ChIJCar0f49ZwokR6ozLV-dHNTE",
"street_address": "205 E Houston St",
"city": "New York",
"state": "NY",
"zip": "10002",
"country_code": "US",
"full_address": "205 E Houston St, New York, NY 10002",
"tags": ["Delicatessen", "Sandwich shop"],
"website": "https://katzsdelicatessen.com/",
"phone": "(212) 254-2246",
"avg_rating": 4.6,
"lat": 40.7223274,
"long": -73.9873733,
"hours": [
{ "day": "Sunday", "hours": "8 AM-10:45 PM", "opens_at": "08:00", "closes_at": "22:45" }
],
"profile_completeness_score": 100,
"missing_fields": [],
"present_field_count": 7,
"scored_component_count": 7,
"has_website": true,
"has_phone": true,
"has_hours": true,
"has_description": true,
"has_accessibility_info": true,
"has_booking_link": true,
"has_street_view": true,
"accessibility_features": ["Wheelchair accessible entrance", "Wheelchair accessible restroom"],
"description": "Iconic Jewish deli slinging hand-carved pastrami since 1888.",
"description_tagline": "Legendary NYC deli",
"neighborhood": "Manhattan",
"thumbnail_url": "https://lh5.googleusercontent.com/p/example1=w152-h86",
"street_view_thumbnail": "https://streetviewpixels-pa.googleapis.com/v1/thumbnail?example1",
"owner_display_name": null
},
{
"rowType": "place",
"name": "Sunrise Coffee Bar",
"place_id": "ChIJexampleplace002",
"street_address": "48 Nostrand Ave",
"city": "Brooklyn",
"state": "NY",
"zip": "11206",
"country_code": "US",
"full_address": "48 Nostrand Ave, Brooklyn, NY 11206",
"tags": ["Coffee shop"],
"website": null,
"phone": "(718) 555-0142",
"avg_rating": 4.1,
"lat": 40.6987,
"long": -73.9498,
"hours": [
{ "day": "Monday", "hours": "7 AM-4 PM", "opens_at": "07:00", "closes_at": "16:00" }
],
"profile_completeness_score": 55,
"missing_fields": ["website", "booking_link"],
"present_field_count": 5,
"scored_component_count": 7,
"has_website": false,
"has_phone": true,
"has_hours": true,
"has_description": true,
"has_accessibility_info": true,
"has_booking_link": false,
"has_street_view": true,
"accessibility_features": ["Wheelchair accessible entrance"],
"description": "Neighborhood coffee bar with in-house roasted beans.",
"description_tagline": null,
"neighborhood": "Bedford-Stuyvesant",
"thumbnail_url": "https://lh5.googleusercontent.com/p/example2=w152-h86",
"street_view_thumbnail": "https://streetviewpixels-pa.googleapis.com/v1/thumbnail?example2",
"owner_display_name": "Sunrise Coffee Bar (Owner)"
},
{
"rowType": "place",
"name": "QuickFix Plumbing Co",
"place_id": "ChIJexampleplace003",
"street_address": "900 Industrial Blvd",
"city": "Austin",
"state": "TX",
"zip": "78744",
"country_code": "US",
"full_address": "900 Industrial Blvd, Austin, TX 78744",
"tags": ["Plumber"],
"website": null,
"phone": null,
"avg_rating": 3.4,
"lat": 30.2015,
"long": -97.7392,
"hours": [],
"profile_completeness_score": 17,
"missing_fields": ["website", "phone", "hours", "description", "booking_link"],
"present_field_count": 1,
"scored_component_count": 6,
"has_website": false,
"has_phone": false,
"has_hours": false,
"has_description": false,
"has_accessibility_info": true,
"has_booking_link": false,
"has_street_view": null,
"accessibility_features": ["Wheelchair accessible entrance"],
"description": null,
"description_tagline": null,
"neighborhood": null,
"thumbnail_url": null,
"street_view_thumbnail": null,
"owner_display_name": null
}
]

⚠️ hours carries only the current day's opening hours — Google's map-search payload does not expose the full week on this surface, so a listing with published hours for every other day but not today still scores has_hours: false for that run. has_street_view, street_view_thumbnail and owner_display_name are null (not false) whenever the extra lookup was skipped or failed, so a null never gets counted as a scored gap.


💡 How can I use the data extracted with Google Maps Extractor — Listing Completeness Audit?

  • 🏢 Local SEO agencies and GBP consultants: filter requiredGaps to ["website"] or ["booking_link"] to build an outreach list of businesses with a specific, sellable gap, using missing_fields as the talking point in the first email.
  • 📞 Lead-generation and sales teams: run maxCompletenessScore at 50–60 across a metro area and category to surface the weakest profiles first, then use phone and website to reach out directly.
  • 🤖 AI engineers and LLM developers: point an agent at this Actor's JSON output — profile_completeness_score and missing_fields give a model grounded, structured evidence to generate an audit report or outreach draft without hallucinating what's missing.
  • 📊 Market researchers: aggregate profile_completeness_score by city or neighborhood across a category to compare how well businesses in different areas maintain their Google Business Profile.

📈 How do you monitor listing completeness over time?

Listing completeness is not static — a business adds a website, fixes its hours, or lets its profile lapse, and profile_completeness_score moves accordingly between runs. The discipline is simple: run the same locations and keywords on a schedule, keep each run's dataset, and diff on place_id — the one field guaranteed to stay stable for the same physical business across runs.

The fields worth diffing are profile_completeness_score (did it go up or down), missing_fields (which specific gap opened or closed), and has_website / has_phone / has_booking_link individually if you only care about one component. A dropped website between two runs of the same place_id is a much stronger signal than a raw score change, because it names the exact thing that happened.

A practical workflow: schedule a weekly run across your target category and area, export both the current and previous run's datasets, join them on place_id, and alert only on rows where missing_fields gained an entry it didn't have last time — that is a business whose profile got worse, and therefore a fresher lead than one that has been broken for months.

This Actor has no built-in diff or alerting feature of its own — schedule repeated runs through the Apify Console's Schedules tab (a native Apify platform feature, not something this Actor implements itself) and do the comparison in whatever tool consumes the dataset afterward.


🔌 Integrate Google Maps Extractor — Listing Completeness Audit and automate your workflow

Google Maps Extractor — Listing Completeness Audit works with any language or tool that can send an HTTP request, through the Apify API and the apify-client SDKs.

🐍 REST API with Python

from apify_client import ApifyClient
client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("<store-username>/google-maps-extractor-listing-completeness-audit").call(
run_input={
"locations": ["Austin"],
"keywords": ["dentists"],
"maxCompletenessScore": 60,
}
)
for row in client.dataset(run["defaultDatasetId"]).iterate_items():
print(row["name"], row["profile_completeness_score"], row["missing_fields"])

⏰ Scheduled monitoring and delivery

The Actor has no built-in webhook or delivery feature of its own, but it runs on the standard Apify platform: set up a recurring run under Schedules in the Apify Console, and attach an Apify webhook to the run to notify an external endpoint (Slack, a pipeline, your own API) once each run finishes, so a completeness sweep can run unattended on a cadence you choose.


Yes — this Actor collects only publicly visible Google Business Profile listing data: business name, address, phone, website, category, hours and similar fields that any visitor sees on Google Maps without logging in. This is business and public-record data, not personal data about a private individual, so GDPR and CCPA — which govern personal data — generally do not attach to it; the relevant framing instead is Google's own Terms of Service and database-rights law around bulk collection and reuse of a compiled dataset. In hiQ Labs, Inc. v. LinkedIn Corp. (9th Cir., 2019), the court held that scraping publicly accessible profile data does not, on its own, violate the U.S. Computer Fraud and Abuse Act — a precedent that has been widely applied to public-data scraping generally, though it does not override a platform's own Terms of Service. Scraping for internal monitoring or lead qualification carries a different risk profile than scraping at scale for AI training or resale. Consult your legal team for commercial use cases involving bulk data storage.


❓ Frequently asked questions

Can I audit listings outside the United States?

Yes. locations accepts any city, district or region name that OpenStreetMap's Nominatim geocoder can resolve — the Actor uses the resolved area's own country code to query Google in the matching region, so ["Manchester"] or ["Brooklyn"] both work without any separate country setting.

Can I control which listings get filtered out?

Yes, through two independent filters: maxCompletenessScore keeps only listings scoring at or below the number you set, and requiredGaps keeps only listings missing every gap you tick from the seven available components. Both default to keeping everything (100 and []).

How does this Actor handle Google's anti-bot measures?

It runs through Apify Proxy by default and escalates automatically: if a connection tier gets refused or blocked repeatedly, the Actor steps up from your configured proxy to a residential tier on its own. Before saving any results for an area, it also probes the requested coordinates and switches connection if the results come back from somewhere else entirely, rather than saving wrong-area rows.

Does this Actor compute the listing completeness score?

Yes — every row carries profile_completeness_score (0-100) and a missing_fields array. The score is a weighted percentage of the 6 listing-payload components (website, phone, today's hours, description, accessibility info, booking link) plus a 7th, Street View, when auditMediaDepth is set to fetch. scored_component_count on each row tells you whether it was judged on 6 or 7.

How many results does this Actor return per query?

Up to maxResults, which accepts 1–10,000 and defaults to 20. This is a total across every area, category and link in the run, deduplicated by Google place ID. ⚠️ When auditMediaDepth is fetch, the Street View/owner lookup is capped internally at max(100, 5 × maxResults) requests per run — once that budget is used up, remaining listings in that run are scored on the 6 listing components only, and a warning is logged.

How do I monitor a business category's completeness over time?

Schedule the same locations and keywords on a recurring cadence through the Apify Console's Schedules tab, keep each run's dataset, and join consecutive runs on place_id to see which listings' missing_fields grew or shrank between runs.

Does this Actor work with Claude, ChatGPT, and AI agent frameworks?

There is no dedicated MCP server for this Actor. It is callable as a standard HTTP endpoint through the Apify API by any agent framework that can issue an HTTP request, which lets an agent trigger a run, retrieve the resulting JSON, and ground its answer in the returned profile_completeness_score and missing_fields.

How does this Actor compare to other Google Maps scrapers?

Competing Google Maps scrapers' documented capabilities were not available for direct comparison at the time of writing. What this Actor's own source code confirms is that it returns the standard Google Maps listing row (name, address, phone, hours, rating) plus an automatically computed completeness score and gap list on the same row — a scoring layer most general-purpose Maps scrapers leave for the user to build themselves.

Can I use this Actor without managing proxies or Google credentials?

Yes. Apify Proxy is enabled by default (proxyConfiguration) and the Actor manages its own connection escalation. No Google account, API key or Google Places API billing is required — you only need an Apify account to run it.


💬 Your feedback

Found a bug, or a listing field that should be on the row but isn't? Open the Issues tab on this Actor's Apify Store page to report it. Bug reports and field requests are what keep this Actor's scoring accurate as Google's own listing payload changes.