<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: orange</title>
    <description>The latest articles on DEV Community by orange (@orange_k).</description>
    <link>https://dev.to/orange_k</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4111284%2F0f25361a-e8da-4f8c-9f39-ddd03a53adb2.png</url>
      <title>DEV Community: orange</title>
      <link>https://dev.to/orange_k</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/orange_k"/>
    <language>en</language>
    <item>
      <title>Checking If a Business's Google Profile Actually Matches Its Own Website</title>
      <dc:creator>orange</dc:creator>
      <pubDate>Sun, 06 Sep 2026 03:50:33 +0000</pubDate>
      <link>https://dev.to/orange_k/checking-if-a-businesss-google-profile-actually-matches-its-own-website-413o</link>
      <guid>https://dev.to/orange_k/checking-if-a-businesss-google-profile-actually-matches-its-own-website-413o</guid>
      <description>&lt;p&gt;If you do local SEO work, you've run into this: a client's Google Business Profile says one phone number, their website footer says another, and nobody noticed until a customer called the wrong number. Or the postal code on the GBP listing is a leftover from an old office. This kind of drift is called a NAP (Name, Address, Phone) inconsistency, and it's widely cited as a local search ranking factor. But checking it by hand means opening every listing and every website side by side.&lt;/p&gt;

&lt;p&gt;I built &lt;strong&gt;&lt;a href="https://apify.com/reverenced_garnet/apify-gmaps-nap-checker" rel="noopener noreferrer"&gt;Google Maps NAP Consistency Checker&lt;/a&gt;&lt;/strong&gt;, an Apify Actor that takes Google Maps scraper output, fetches each business's own website (lightly: homepage plus one likely subpage), and checks whether the name, postal code, and phone number on the Google Business Profile actually show up on the site.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it does, and what it doesn't
&lt;/h2&gt;

&lt;p&gt;This Actor checks one thing: does a business's own website agree with its Google Business Profile on name, postal code, and phone number. It does not check third-party directories (Yelp, Facebook, etc.). That's a different problem with a different competitor landscape. It does not crawl an entire website; it fetches at most two pages per business (homepage, plus a subpage if one with a keyword like "contact" or "about" is linked from it). It does not use an LLM. It's regex and string matching against fetched text, which makes it fast, cheap, and predictable. There's no model that can hallucinate a match that isn't there.&lt;/p&gt;

&lt;p&gt;Businesses with no independent website (only a social profile, or nothing) are skipped entirely, because there's nothing to fetch and compare against.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it works
&lt;/h2&gt;

&lt;p&gt;For each place with a real website, the Actor:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Checks &lt;code&gt;robots.txt&lt;/code&gt; for that domain before fetching anything, and skips the business if the checker's user agent isn't allowed.&lt;/li&gt;
&lt;li&gt;Fetches the homepage HTML (up to 3 MB), strips &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt;, and comments before converting to text, so JavaScript variables and tracking IDs don't get misread as phone numbers.&lt;/li&gt;
&lt;li&gt;Looks for an internal link containing a keyword like "contact," "about," "access," or "info" (also handles Japanese equivalents), and fetches that one extra page if found. Mail and tel links and JS pseudo-links are excluded from that search. An early version tried to fetch &lt;code&gt;mailto:info@...&lt;/code&gt; as if it were a page URL, which is exactly the kind of bug you only find on real sites.&lt;/li&gt;
&lt;li&gt;Pulls out &lt;code&gt;href="tel:..."&lt;/code&gt; numbers directly (a strong signal), plus any phone-shaped strings in the visible text, covering Japanese formats (&lt;code&gt;03-6434-9090&lt;/code&gt;) and US formats (&lt;code&gt;(512) 828-3835&lt;/code&gt;, &lt;code&gt;+1 512-828-3835&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Compares postal code and phone number found on the site against what the Google Business Profile has, using the scraper's own &lt;code&gt;postalCode&lt;/code&gt; field when available, with digit-only and hyphenated variants both checked so formatting differences don't cause false mismatches.&lt;/li&gt;
&lt;li&gt;Checks whether a meaningful fraction of the business name's word tokens appear anywhere on the page, as a rough "is this even the right site" sanity check.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Every result also gets a &lt;code&gt;changeStatus&lt;/code&gt;: run it again later on the same list and matches that were previously flagged as mismatched but are now fine get tagged &lt;code&gt;FIXED&lt;/code&gt;. Useful if you're the one who told the client to fix it and want proof it happened.&lt;/p&gt;

&lt;h2&gt;
  
  
  Usage
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Apify Console
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Run a Google Maps scraper (e.g. &lt;a href="https://apify.com/compass/crawler-google-places" rel="noopener noreferrer"&gt;Google Maps Scraper&lt;/a&gt;) for the businesses you want to audit.&lt;/li&gt;
&lt;li&gt;Open &lt;a href="https://apify.com/reverenced_garnet/apify-gmaps-nap-checker" rel="noopener noreferrer"&gt;Google Maps NAP Consistency Checker&lt;/a&gt;, paste that dataset array into the &lt;strong&gt;Google Maps places&lt;/strong&gt; field.&lt;/li&gt;
&lt;li&gt;Click Start. You're billed per business actually checked. Places with no independent website are skipped for free.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Chaining two Actors with apify-client (Python)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;apify_client&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ApifyClient&lt;/span&gt;

&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ApifyClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;YOUR_APIFY_TOKEN&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# 1. Scrape a set of businesses
&lt;/span&gt;&lt;span class="n"&gt;scrape_run&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;actor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;compass/crawler-google-places&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;run_input&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;searchStringsArray&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;plumber in Austin, TX&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;maxCrawledPlacesPerSearch&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;places&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scrape_run&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;defaultDatasetId&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="nf"&gt;iterate_items&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;

&lt;span class="c1"&gt;# 2. Check NAP consistency for each business's own website
&lt;/span&gt;&lt;span class="n"&gt;nap_run&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;actor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;reverenced_garnet/apify-gmaps-nap-checker&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;run_input&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;places&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;places&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nap_run&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;defaultDatasetId&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="nf"&gt;iterate_items&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;

&lt;span class="n"&gt;mismatches&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;reasons&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mismatches&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; of &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; checked businesses have a NAP issue&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Input
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Field&lt;/th&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;places&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;array (required)&lt;/td&gt;
&lt;td&gt;Place objects from a Google Maps scraper's dataset output. Only places with their own (non-social-media) website are checked. The rest are skipped.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Output
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Ultimate Plumber"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"placeId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ChIJv79rmEfzGGARhXlSEd8ceiQ"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"fetchedOk"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"postalMatch"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"phoneStatus"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"match"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"nameFoundOnSite"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"changeStatus"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"FIXED"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"reasons"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Field&lt;/th&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;title&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;string&lt;/td&gt;
&lt;td&gt;Business name&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;placeId&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;string&lt;/td&gt;
&lt;td&gt;Google Maps place ID&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;fetchedOk&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;boolean&lt;/td&gt;
&lt;td&gt;Whether the website could be fetched at all (&lt;code&gt;false&lt;/code&gt; means every other field is unknown, not "matching")&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;postalMatch&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;boolean or null&lt;/td&gt;
&lt;td&gt;Whether the GBP postal code appears on the site; &lt;code&gt;null&lt;/code&gt; if there wasn't enough data on either side to compare&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;phoneStatus&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;string&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;match&lt;/code&gt;, &lt;code&gt;mismatch&lt;/code&gt;, &lt;code&gt;gbp_missing_on_site_found&lt;/code&gt;, &lt;code&gt;site_missing_gbp_has&lt;/code&gt;, &lt;code&gt;both_missing&lt;/code&gt;, or &lt;code&gt;unknown&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nameFoundOnSite&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;boolean or null&lt;/td&gt;
&lt;td&gt;Whether most of the business name's word tokens appear on the page&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;changeStatus&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;string&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;NEW&lt;/code&gt;, &lt;code&gt;UPDATED&lt;/code&gt;, &lt;code&gt;UNCHANGED&lt;/code&gt;, or &lt;code&gt;FIXED&lt;/code&gt; versus the previous run on this Actor&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;reasons&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;array&lt;/td&gt;
&lt;td&gt;Plain-English list of what didn't match&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Note that &lt;code&gt;fetchedOk: false&lt;/code&gt; and &lt;code&gt;postalMatch: null&lt;/code&gt; are deliberately kept separate from a plain "no mismatch found." If the site couldn't be fetched, or there wasn't a postal code to compare on one side, the Actor says so instead of quietly reporting a false match.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use case: auditing NAP consistency before a local SEO engagement
&lt;/h2&gt;

&lt;p&gt;A common first step for a local SEO agency taking on a new client, or a freelancer building a pitch:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Scrape the client's own listing (or every location, for a multi-location business) with a Google Maps scraper.&lt;/li&gt;
&lt;li&gt;Run it through this Actor to see whether the phone number and address on the GBP listing actually match the website.&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;phoneStatus: gbp_missing_on_site_found&lt;/code&gt; as a quick, low-effort recommendation: "your GBP is missing a phone number your own site already lists, this is a five-minute fix."&lt;/li&gt;
&lt;li&gt;Schedule it monthly with Apify's Scheduler for an existing client, and use &lt;code&gt;changeStatus: FIXED&lt;/code&gt; as evidence the fixes you recommended actually got made.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The same pattern applies to a directory or citation-management tool that wants a quick pre-check on whether a business's own site agrees with itself before flagging bigger discrepancies elsewhere.&lt;/p&gt;

&lt;h2&gt;
  
  
  A note on scope and responsible use
&lt;/h2&gt;

&lt;p&gt;This Actor fetches at most two pages per business, respects &lt;code&gt;robots.txt&lt;/code&gt; for the domain being checked, and identifies itself with its own user agent string. It's not a general-purpose crawler. It only looks at data that's already public on the business's own Google Business Profile and website. It's still worth being explicit that finding a phone number or address on a public page doesn't by itself give you consent to use it for outreach; if you use anything found here for contacting a business, you're responsible for complying with applicable law in your jurisdiction.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pricing
&lt;/h2&gt;

&lt;p&gt;Pay-per-event: $0.01 when a run starts, plus $0.01 per business actually checked (only businesses that had a website to fetch). Auditing 100 locations costs about $1.01. Places with no independent website are skipped and not charged. Current pricing is always on the Store page.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;The Actor is live on the Apify Store: &lt;a href="https://apify.com/reverenced_garnet/apify-gmaps-nap-checker" rel="noopener noreferrer"&gt;Google Maps NAP Consistency Checker&lt;/a&gt;. I built this as part of a small set of post-processing Actors for Google Maps scraper output. If you're already running a scraper for local SEO or lead work, this is a cheap add-on step to catch a class of client-facing errors that's otherwise easy to miss.&lt;/p&gt;

</description>
      <category>apify</category>
      <category>python</category>
      <category>seo</category>
      <category>automation</category>
    </item>
    <item>
      <title>Turning Google Maps Scraper Output into a 0-100 Lead Priority Score (No Scraping, No LLM)</title>
      <dc:creator>orange</dc:creator>
      <pubDate>Sat, 05 Sep 2026 23:56:11 +0000</pubDate>
      <link>https://dev.to/orange_k/turning-google-maps-scraper-output-into-a-0-100-lead-priority-score-no-scraping-no-llm-e3p</link>
      <guid>https://dev.to/orange_k/turning-google-maps-scraper-output-into-a-0-100-lead-priority-score-no-scraping-no-llm-e3p</guid>
      <description>&lt;p&gt;If you've ever built a lead-gen pipeline for a local SEO or web design agency, you know the pattern: scrape a bunch of businesses off Google Maps, then manually eyeball which ones are worth calling. Missing website? Good lead. Unclaimed Google Business Profile? Also good. Both? Great lead. But sorting hundreds or thousands of rows by hand on multiple criteria doesn't scale.&lt;/p&gt;

&lt;p&gt;I built &lt;strong&gt;&lt;a href="https://apify.com/reverenced_garnet/apify-gmaps-lead-priority" rel="noopener noreferrer"&gt;Google Maps Lead Priority Scorer&lt;/a&gt;&lt;/strong&gt;, an Apify Actor that takes the JSON output you already have from a Google Maps scraper and turns it into a single sortable &lt;code&gt;priorityScore&lt;/code&gt; (0-100) per business, plus a plain-English list of reasons. It does no scraping and makes no LLM or external API calls — it's pure post-processing of data you already paid to collect.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem with single-signal lead lists
&lt;/h2&gt;

&lt;p&gt;Most "find leads with no website" tools give you exactly one signal. But a business that has no website &lt;strong&gt;and&lt;/strong&gt; an unclaimed GBP listing &lt;strong&gt;and&lt;/strong&gt; fewer reviews than its neighbors is a categorically hotter lead than one that's just missing a website. If you're running four separate scrapers/filters and merging spreadsheets by hand, you're doing manual work a scoring function should do for you.&lt;/p&gt;

&lt;p&gt;This Actor combines four signals into one number:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Signal&lt;/th&gt;
&lt;th&gt;Max points&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;No independent website (a bare social-media link doesn't count)&lt;/td&gt;
&lt;td&gt;35&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Unclaimed Google Business Profile&lt;/td&gt;
&lt;td&gt;25&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Other missing profile fields (phone, description, hours, photos, categories)&lt;/td&gt;
&lt;td&gt;25&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Review count below the median for similar nearby businesses&lt;/td&gt;
&lt;td&gt;15&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Scores map to tiers: &lt;strong&gt;Hot&lt;/strong&gt; (≥70), &lt;strong&gt;Warm&lt;/strong&gt; (40-69), &lt;strong&gt;Cold&lt;/strong&gt; (&amp;lt;40). Businesses marked permanently closed on Google Maps are dropped automatically before scoring, so you never waste an outreach call on a place that no longer exists.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it works: no scraping, no LLM
&lt;/h2&gt;

&lt;p&gt;This is the detail that matters most for a technical audience: this Actor is not a Maps scraper. It expects you to already have place objects (from something like &lt;a href="https://apify.com/compass/crawler-google-places" rel="noopener noreferrer"&gt;Google Maps Scraper&lt;/a&gt;) and does pure computation on top of them — string checks for real vs. social-media-only URLs, null checks across profile fields, and a percentile calculation against other places in the same input batch. No headless browser, no OpenAI call, nothing that can time out or hallucinate. That's also why it's cheap: pay-per-event pricing is $0.005 per run start plus $0.0015 per scored lead returned. Scoring 1,000 already-scraped places costs about $1.51 on top of whatever the scraper itself charged.&lt;/p&gt;

&lt;h2&gt;
  
  
  Usage: Apify Console or the API
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Option 1 — Apify Console
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Run a Google Maps scraper for your target city/category and grab its dataset output (as JSON).&lt;/li&gt;
&lt;li&gt;Open &lt;a href="https://apify.com/reverenced_garnet/apify-gmaps-lead-priority" rel="noopener noreferrer"&gt;Google Maps Lead Priority Scorer&lt;/a&gt; in Apify Console, paste that array into the &lt;strong&gt;Google Maps places&lt;/strong&gt; input field.&lt;/li&gt;
&lt;li&gt;Optionally set &lt;strong&gt;Minimum priority tier&lt;/strong&gt; to &lt;code&gt;Warm&lt;/code&gt; or &lt;code&gt;Hot&lt;/code&gt; so you're only ever billed for the leads worth calling.&lt;/li&gt;
&lt;li&gt;Click Start. Results come back sorted highest-priority first, downloadable as JSON/CSV/Excel.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Option 2 — &lt;code&gt;apify call&lt;/code&gt; from the CLI
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;apify call reverenced_garnet/apify-gmaps-lead-priority &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--input&lt;/span&gt; &lt;span class="s1"&gt;'{
    "places": [
      { "title": "Lemari Coffee", "placeId": "ChIJv79rmEfzGGARhXlSEd8ceiQ" }
    ],
    "minTier": "Warm"
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Option 3 — Apify API / SDK (chaining two Actors)
&lt;/h3&gt;

&lt;p&gt;The realistic production setup is: scrape → score → dataset, all in one script. Here's a minimal Node example using the Apify client that chains a Maps scraper run into this Actor:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ApifyClient&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;apify-client&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ApifyClient&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;APIFY_TOKEN&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// 1. Run a Google Maps scraper for a city/category&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;scrapeRun&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;actor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;compass/crawler-google-places&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;searchStringsArray&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;coffee shop in Austin, TX&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;maxCrawledPlacesPerSearch&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;places&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scrapeRun&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;defaultDatasetId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listItems&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// 2. Score those places for lead priority&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;scoreRun&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;actor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;reverenced_garnet/apify-gmaps-lead-priority&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="nx"&gt;places&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;minTier&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Warm&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;webhookUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://your-n8n-instance.example.com/webhook/new-leads&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;leads&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scoreRun&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;defaultDatasetId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listItems&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Got &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;leads&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; Warm+ leads, top one:`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;leads&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The same works in Python with the &lt;code&gt;apify-client&lt;/code&gt; package (&lt;code&gt;ApifyClient(token=...)&lt;/code&gt;, &lt;code&gt;.actor(...).call(run_input=...)&lt;/code&gt;, &lt;code&gt;.dataset(...).list_items()&lt;/code&gt;).&lt;/p&gt;

&lt;h2&gt;
  
  
  Input schema
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Field&lt;/th&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;places&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;array&lt;/td&gt;
&lt;td&gt;Place objects from a Google Maps scraper's dataset output&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;minTier&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;string&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;Hot&lt;/code&gt;, &lt;code&gt;Warm&lt;/code&gt;, or &lt;code&gt;Cold&lt;/code&gt; (default) — minimum tier to include in the output&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;slackWebhookUrl&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;string (optional)&lt;/td&gt;
&lt;td&gt;Slack Incoming Webhook URL — posts a run summary&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;discordWebhookUrl&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;string (optional)&lt;/td&gt;
&lt;td&gt;Discord Channel Webhook URL — posts a run summary&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;webhookUrl&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;string (optional)&lt;/td&gt;
&lt;td&gt;Any URL — receives a structured JSON summary (e.g. for n8n/Make)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;notifyOnlyNewOrUpdated&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;boolean&lt;/td&gt;
&lt;td&gt;Default &lt;code&gt;true&lt;/code&gt; — only include &lt;code&gt;NEW&lt;/code&gt;/&lt;code&gt;UPDATED&lt;/code&gt; leads in notifications&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Output fields
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Lemari Coffee"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"placeId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ChIJv79rmEfzGGARhXlSEd8ceiQ"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"priorityScore"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"tier"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Warm"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"changeStatus"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"NEW"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"reasons"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"No independent website (social-media link or none at all)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"No business description"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"hasRealWebsite"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"isUnclaimed"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"completenessScore"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;70&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"reviewPercentileInGroup"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;25.0&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Field&lt;/th&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;title&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;string&lt;/td&gt;
&lt;td&gt;Business name&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;placeId&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;string&lt;/td&gt;
&lt;td&gt;Google Maps place ID&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;priorityScore&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;number&lt;/td&gt;
&lt;td&gt;0-100 combined lead-priority score&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;tier&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;string&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;Hot&lt;/code&gt;, &lt;code&gt;Warm&lt;/code&gt;, or &lt;code&gt;Cold&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;changeStatus&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;string&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;NEW&lt;/code&gt;, &lt;code&gt;UPDATED&lt;/code&gt;, or &lt;code&gt;UNCHANGED&lt;/code&gt; vs. the previous run on this Actor&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;reasons&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;array&lt;/td&gt;
&lt;td&gt;Plain-English reasons contributing to the score&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;hasRealWebsite&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;boolean&lt;/td&gt;
&lt;td&gt;False if no site, or only a social-media link&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;isUnclaimed&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;boolean&lt;/td&gt;
&lt;td&gt;True if the Google Business Profile is unclaimed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;completenessScore&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;number&lt;/td&gt;
&lt;td&gt;0-100 GBP profile completeness&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;reviewPercentileInGroup&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;number&lt;/td&gt;
&lt;td&gt;Review-count percentile vs. same city+category peers&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;One useful, unusual feature for anyone running this on a schedule: &lt;code&gt;changeStatus&lt;/code&gt;. Run the Actor again later on the same area and every lead gets tagged &lt;code&gt;NEW&lt;/code&gt;, &lt;code&gt;UPDATED&lt;/code&gt;, or &lt;code&gt;UNCHANGED&lt;/code&gt; compared to the previous run, so your sales team only re-reviews what actually moved instead of re-triaging the whole list every week.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use case: building an outbound list for a local SEO agency
&lt;/h2&gt;

&lt;p&gt;A typical workflow for an agency doing outbound prospecting for, say, dentists in a metro area:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Run Google Maps Scraper for &lt;code&gt;"dentist in Denver, CO"&lt;/code&gt;, get back a few hundred place objects.&lt;/li&gt;
&lt;li&gt;Feed that array into this Actor with &lt;code&gt;minTier: "Hot"&lt;/code&gt; — you now only pay for and receive the businesses with the strongest combination of no website, unclaimed profile, and thin review count.&lt;/li&gt;
&lt;li&gt;Set &lt;code&gt;slackWebhookUrl&lt;/code&gt; so the agency's sales channel gets pinged with a summary whenever a run finds new or updated leads.&lt;/li&gt;
&lt;li&gt;Schedule the whole pipeline (scraper → scorer) weekly with Apify's built-in Scheduler. Because of &lt;code&gt;changeStatus&lt;/code&gt;, week-over-week runs only surface leads that changed, so the team isn't re-triaging the same 300 businesses every Monday.&lt;/li&gt;
&lt;li&gt;Export the dataset as CSV and import into whatever CRM or cold-email tool the agency uses.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The same pattern works for directory sites deciding which unclaimed listings to email about claiming their profile, or freelancers building a target list for a single vertical + city combo before a cold-call sprint.&lt;/p&gt;

&lt;h2&gt;
  
  
  A word on scope and legality
&lt;/h2&gt;

&lt;p&gt;This Actor deliberately does one thing: turn place data you already have into a priority score. It doesn't scrape anything, doesn't call any external API, and doesn't infer anything with an LLM — which is also why it's fast and cheap. It's also worth being explicit that the presence of a phone number or address on a public Google Business Profile doesn't by itself mean you have consent to cold-call or cold-email that business. If you're using this for outreach, you're responsible for complying with applicable law (GDPR, CAN-SPAM, TCPA, etc. depending on where your leads are).&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;The Actor is live on the Apify Store: &lt;a href="https://apify.com/reverenced_garnet/apify-gmaps-lead-priority" rel="noopener noreferrer"&gt;Google Maps Lead Priority Scorer&lt;/a&gt;. If you're already running a Google Maps scraper as part of a lead-gen pipeline, this is a five-minute addition that replaces a manual sorting/filtering step with a single number your sales team can trust.&lt;/p&gt;

</description>
      <category>apify</category>
      <category>webdev</category>
      <category>automation</category>
      <category>python</category>
    </item>
  </channel>
</rss>
