SFOLN

Catalogue / Local & maps

Local SEO Audit – Google Business Profile

sfoln/local-business-audit · SEO tools, Lead generation, Automation On request

Audit a local business by name and city, place ID or Maps link: Google Business Profile completeness, review stats, local rank for up to 3 keywords with top competitors, website SEO, speed, mobile and NAP match, a 0–100 score and fixes. $35 per 1,000 audits.

About

What it does

Run a local SEO audit of any business on Google Maps: give a business name and city, a Google place ID, a CID or a Google Maps link, and the Actor reads its Google Business Profile, checks where it ranks in Google's local results for up to 3 keywords, audits the website linked from the profile and returns one scored JSON record: profile completeness (claimed, categories, hours, photos, description, attributes), review count, rating and star distribution, local rank with the 3 best-placed competitors, website SEO, speed, mobile setup, HTTPS, LocalBusiness structured data and NAP consistency (does the website show the profile's name, address and phone?), booking and chat widgets, ad pixels, domain age and e-mail authentication, plus an overall 0–100 score, a letter grade and a list of issues with a recommendation each. Built for local SEO and web-design agencies prospecting and reporting, franchise managers checking locations, and AI agents asked "how is this business doing on Google?".

Price

Price per delivered record

EventPricePer 1,000Charged for
Local business audit
local-audit
$0.035$35 / 1,000Charged once per business whose Google Business Profile was found: profile completeness, review stats, local rank for up to 3 keywords with competitors, website SEO / speed / mobile / NAP audit, domain and e-mail setup, scores and recommended fixes.

Apify free plan: up to 5 items per run. Paid plans have no per-run cap; set maxTotalChargeUsd to cap spend.

Run

Call it from code, an agent or the Console

This API is private on Apify for now. Email us and we will publish it for you, usually within two working days. The calls below work once it is public. contact@sfoln.com

curl · synchronous run, rows in the response
curl -X POST "https://api.apify.com/v2/actors/sfoln~local-business-audit/run-sync-get-dataset-items" \
  -H "Authorization: Bearer $APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"businesses":["cid:2946633002421908862"],"location":"New York,New York,United States"}'
mcp.json · hosted Apify MCP server
{
  "mcpServers": {
    "apify": {
      "type": "http",
      "url": "https://mcp.apify.com?tools=sfoln/local-business-audit"
    }
  }
}
asynchronous run · for large batches
POST https://api.apify.com/v2/actors/sfoln~local-business-audit/runs?maxTotalChargeUsd=1
GET  https://api.apify.com/v2/actor-runs/{runId}            # until status is SUCCEEDED
GET  https://api.apify.com/v2/datasets/{defaultDatasetId}/items?format=json

Authenticate with an Apify API token. MCP clients can instead sign in with OAuth when prompted. Example prompt for an agent: Audit the local SEO of Katz's Delicatessen in New York: how complete is its Google Business Profile, where does it rank for "deli", is its website consistent with the profile, and what should it fix first?

Input

Input fields

List businesses by name and city, Google place ID, CID or Google Maps link. Each business produces one audit record.

FieldTypeDefaultMeaning
businesses *arrayrequiredBusinesses
One per line: a business name ("Joe's Pizza Broadway"), a Google Maps link of the place, a place ID ("ChIJ…" or "place_id:…") or a CID ("cid:2946633002421908862" or the bare number of 15+ digits, always as a string: JSON numbers that large lose digits). Override the location per line with "name | location". Objects may also set per-business keywords: {"name": "…", "location": "…", "keywords": ["…"]}. Accounts on the Apify free plan can process up to 5 businesses per run.
locationstring—Location
Where to look up business names: a DataForSEO location name ("New York,New York,United States") or numeric location code (2840 = United States). For place IDs, CIDs and Maps links it only sets the regional context.
keywordsarray—Rank keywords
Up to 3 searches to check the business's local rank for, e.g. "emergency plumber". Each is searched as "<keyword> in <city of the business>" on Google's local results. Empty: the profile's primary category (one search).
checkRankingsbooleantrueCheck local rank
Search Google's local results for each keyword and report the business's position and the top 3 competitors.
checkWebsitebooleantrueAudit the website
Read the website linked from the profile (homepage and contact page): SEO, speed, mobile, HTTPS, LocalBusiness data, NAP consistency with the profile, booking and chat widgets, ad pixels, domain age and e-mail authentication.
languageCodestring"en"Language code
Language of the profile data and of the local-rank searches (ISO code such as en, de, es).
maxItemsinteger1000Max businesses
Safety cap on the number of businesses audited in this run. Range 1–10000.
concurrencyinteger3Concurrency
How many businesses are audited in parallel. Range 1–8.
includeRawbooleanfalseInclude raw upstream response
Attach the unmodified business profile and local results to each record (larger output).
Example input
{
  "businesses": [
    "cid:2946633002421908862"
  ],
  "location": "New York,New York,United States"
}

Output

One record per input item

Records land in the run's dataset; a synchronous call returns them directly. Each has a status (ok is charged; everything else is free) and a charged flag. The run's OUTPUT record totals items and charges.

FieldTypeMeaning
inputobjectInput
{ lookup, location?, keywords? }: the business as given (name, cid:…, place_id:… or Google Maps link).
statusstringStatus
ok | not_found | error | skipped_invalid | skipped_budget | skipped_free_plan | skipped_unmetered
chargedbooleanCharged
Whether this record incurred a local-audit charge.
chargedEventstringCharged event
local-audit (present only when charged).
chargedCountintegerEvents charged
Events actually charged when the charge came back short (present only then).
sourcestringSource
Upstream data sources.
fetchedAtstringFetched at
ISO timestamp.
errorobjectError
{ code, message, provider?, status? } when status is not ok: not_found (no Google Business Profile for the name / id), invalid_input (unreadable Maps link), plus the runtime codes (budget_exhausted, free_plan_limit, unmetered, aborted, charge_failed, record_too_large, invalid_record, dataset_push_failed) and upstream codes (auth, insufficient_credits, rate_limit, timeout, network, server, client, invalid_response).
warningsarrayWarnings
Parts that failed or were skipped: rank searches, a website that refused the request, RDAP.
lookupKindstringLookup kind
cid, place_id or keyword (business name).
locationstringLocation
Location used for the lookup.
foundbooleanFound
A Google Business Profile was found.
businessNamestring | nullBusiness name
Name on the Google Business Profile.
categorystring | nullCategory
Primary category.
additionalCategoriesarraySecondary categories
Further categories of the profile.
addressstring | nullAddress
Formatted address.
citystring | nullCity
City of the address.
postalCodestring | nullPostal code
Postal code of the address.
regionstring | nullRegion
State or region.
countryCodestring | nullCountry
ISO 3166-1 alpha-2 country code.
phonestring | nullPhone
Phone number as shown on the profile.
phoneE164string | nullPhone (E.164)
The profile's phone in E.164 (+12175550142).
websitestring | nullWebsite
Website linked from the profile.
domainstring | nullDomain
Host of the website (without www.).
cidstring | nullCID
Google customer id of the place.
placeIdstring | nullPlace ID
Google place id.
googleMapsUrlstring | nullGoogle Maps
Link to the place on Google Maps.
latitudenumber | nullLatitude
Latitude of the place.
longitudenumber | nullLongitude
Longitude of the place.
isClaimedboolean | nullClaimed
The owner has claimed the profile.
currentStatusstring | nullStatus on Google
open, closed (right now), temporarily_closed or closed_forever.
ratingnumber | nullRating
Average star rating.
reviewCountintegerReviews
Number of Google reviews.
auditScoreintegerAudit score
Overall score 0–100: 35% profile completeness, 30% website, 20% reviews, 15% local rank (sections not assessed are left out).
gradestringGrade
A (90+), B (75+), C (60+), D (45+), E (25+), F.
scoresobjectScores
{ overall, profile, reviews, visibility, website } 0–100 (visibility / website null when not assessed; website 0 without a website or with a social / directory page as the website).
profileobjectProfile
{ completeness, checks[{ check, label, ok, points, maxPoints }], description, workHours, workHoursSummary, totalPhotos, logo, mainImage, attributes[], services, bookOnlineUrl, priceLevel, peopleAlsoSearch[] }.
reviewsobjectReviews
{ rating, count, distribution { 1…5 }, positiveShare, negativeShare, topics, score }: counts and distribution only, no review texts.
visibilityobject | nullLocal rank
{ keywords[{ keyword, query, rank, resultsCount, competitors[{ rank, name, rating, reviewCount, category, domain, cid }], error }], bestRank, score }.
websiteAuditobject | nullWebsite audit
{ url, finalUrl, status, https, sslValid, sslIssuer, sslDaysLeft, mobileFriendly, title, metaDescription, h1, cms, technologies[], bookingWidgets[], chatWidgets[], adPixels[], analytics[], runsAds, localBusinessSchema, phonesOnSite[], hasClickToCall, emailsOnSite[], socials, copyrightYear, staleSite, lastModified, performance, scores { seo, performance, local }, pagesRead[] }.
napobject | nullNAP consistency
{ name { gbp, site, score, match }, phone { gbp, site[], match }, address { gbp, postalCodeFound, streetFound, cityFound, match }, consistent, score }: the profile's name, address and phone compared with the website.
presenceobject | nullDomain and e-mail
{ domain, domainCreatedAt, domainAgeDays, registrar, mailProvider, hasMailServer, spf, dmarcPolicy, spfDmarcLookup }: spfDmarcLookup failed = the SPF / DMARC lookup failed (spf null, dmarcPolicy unknown); null for a website on a shared platform domain (wixsite.com, myshopify.com …) or without a website of its own.
issuesarrayIssues
{ severity (high | medium | low), area (profile | reviews | visibility | nap | website | presence), code, finding, recommendation }, most severe first.
issueCountsobjectIssue counts
{ high, medium, low }.
sectionsobjectSections
State of each part: profile ok; visibility ok | partial | error | skipped | off; website ok | none | social (a social or directory page as the website, not audited) | blocked | error | off; presence ok | partial | skipped | off.
Example record
Example record
{
  "input": {
    "lookup": "cid:1234567890123456789",
    "location": "Springfield,Illinois,United States",
    "keywords": [
      "dentist",
      "emergency dentist"
    ]
  },
  "lookupKind": "cid",
  "location": "Springfield,Illinois,United States",
  "found": true,
  "businessName": "Maple Street Dental",
  "category": "Dentist",
  "additionalCategories": [
    "Cosmetic dentist"
  ],
  "address": "1200 S Maple St, Springfield, IL 62704",
  "city": "Springfield",
  "postalCode": "62704",
  "region": "Illinois",
  "countryCode": "US",
  "phone": "+1217-555-0142",
  "phoneE164": "+12175550142",
  "website": "https://www.maplestreetdental.example/",
  "domain": "maplestreetdental.example",
  "cid": "1234567890123456789",
  "placeId": "ChIJmapleStreetDental0001",
  "googleMapsUrl": "https://www.google.com/maps?cid=1234567890123456789",
  "latitude": 39.7817,
  "longitude": -89.6501,
  "isClaimed": false,
  "currentStatus": "open",
  "rating": 4.1,
  "reviewCount": 23,
  "auditScore": 69,
  "grade": "C",
  "scores": {
    "overall": 69,
    "profile": 60,
    "reviews": 57,
    "visibility": 50,
    "website": 96
  },
  "profile": {
    "completeness": 60,
    "checks": [
      {
        "check": "claimed",
        "label": "Profile claimed by the owner",
        "ok": false,
        "points": 0,
        "maxPoints": 15
      },
      {
        "check": "primaryCategory",
        "label": "Primary category",
        "ok": true,
        "points": 10,
        "maxPoints": 10
      },
      {
        "check": "photos",
        "label": "10 or more photos",
        "ok": false,
        "points": 5,
        "maxPoints": 10
      }
    ],
    "description": "Family dentist in Springfield.",
    "workHours": {
      "monday": [
        "08:00-17:00"
      ],
      "tuesday": [
        "08:00-17:00"
      ]
    },
    "workHoursSummary": "Mon 08:00-17:00; Tue 08:00-17:00; Wed 08:00-17:00; Thu 08:00-17:00; Fri 08:00-13:00; Sat closed; Sun closed",
    "totalPhotos": 6,
    "logo": null,
    "mainImage": "https://lh5.googleusercontent.com/p/maple-street-dental-main=w648-h240-k-no",
    "attributes": [
      "has_wheelchair_accessible_entrance",
      "accepts_new_patients"
    ],
    "services": 0,
    "bookOnlineUrl": null,
    "priceLevel": null,
    "peopleAlsoSearch": [
      "Lincoln Park Dental Care"
    ]
  },
  "reviews": {
    "rating": 4.1,
    "count": 23,
    "distribution": {
      "1": 3,
      "2": 1,
      "3": 1,
      "4": 4,
      "5": 14
    },
    "positiveShare": 0.78,
    "negativeShare": 0.17,
    "topics": {
      "cleaning": 12,
      "front desk": 5
    },
    "score": 57
  },
  "visibility": {
    "keywords": [
      {
        "keyword": "dentist",
        "query": "dentist in Springfield, Illinois",
        "rank": 2,
        "resultsCount": 20,
        "competitors": [
          {
            "rank": 1,
            "name": "Lincoln Park Dental Care",
            "rating": 4.8,
            "reviewCount": 412,
            "category": "Dentist",
            "domain": "lincolnparkdental.example",
            "cid": "9876543210987654321"
          },
          {
            "rank": 3,
            "name": "Capitol City Smiles",
            "rating": 4.6,
            "reviewCount": 158,
            "category": "Cosmetic dentist",
            "domain": "capitolcitysmiles.example",
            "cid": "1111111111111111111"
          },
          {
            "rank": 4,
            "name": "Prairie Family Dentistry",
            "rating": 4.9,
            "reviewCount": 87,
            "category": "Dentist",
            "domain": null,
            "cid": "2222222222222222222"
          }
        ],
        "error": null
      },
      {
        "keyword": "emergency dentist",
        "query": "emergency dentist in Springfield, Illinois",
        "rank": null,
        "resultsCount": 20,
        "competitors": [
          {
            "rank": 1,
            "name": "Capitol City Smiles",
            "rating": 4.6,
            "reviewCount": 158,
            "category": "Cosmetic dentist",
            "domain": "capitolcitysmiles.example",
            "cid": "1111111111111111111"
          },
          {
            "rank": 2,
            "name": "Springfield Emergency Dental",
            "rating": 4.3,
            "reviewCount": 64,
            "category": "Emergency dental service",
            "domain": "springfieldemergencydental.example",
            "cid": "3333333333333333333"
          }
        ],
        "error": null
      }
    ],
    "bestRank": 2,
    "score": 50
  },
  "websiteAudit": {
    "url": "https://www.maplestreetdental.example/",
    "finalUrl": "https://www.maplestreetdental.example/",
    "status": 200,
    "https": true,
    "sslValid": true,
    "sslIssuer": "Let's Encrypt",
    "sslDaysLeft": 75,
    "mobileFriendly": true,
    "title": "Maple Street Dental | Family Dentist in Springfield, IL",
    "metaDescription": "Gentle family and cosmetic dentistry in Springfield, Illinois. New patients welcome, same-week appointments and evening hours. Book online today.",
    "h1": "Family dentistry in Springfield",
    "cms": null,
    "technologies": [
      "Facebook Pixel",
      "Google Tag Manager",
      "Let's Encrypt",
      "Open Graph"
    ],
    "bookingWidgets": [
      "Zocdoc"
    ],
    "chatWidgets": [],
    "adPixels": [
      "Meta Pixel"
    ],
    "analytics": [
      "Google Tag Manager"
    ],
    "runsAds": true,
    "localBusinessSchema": {
      "present": true,
      "types": [
        "Dentist"
      ],
      "name": "Maple Street Dental",
      "telephone": "+1-217-555-0142",
      "address": "1200 South Maple Street, 62704 Springfield, IL, US",
      "hasOpeningHours": true,
      "hasGeo": false
    },
    "phonesOnSite": [
      "+12175550142"
    ],
    "hasClickToCall": true,
    "emailsOnSite": [
      "maplestreetdental.frontdesk@gmail.com"
    ],
    "socials": {
      "facebook": "https://www.facebook.com/maplestreetdental",
      "instagram": "https://www.instagram.com/maplestreetdental"
    },
    "copyrightYear": 2021,
    "staleSite": true,
    "lastModified": null,
    "performance": {
      "ttfbMs": 420,
      "htmlKb": 38.2,
      "compressed": true,
      "requestEstimate": 31,
      "renderBlockingScripts": 0
    },
    "scores": {
      "seo": 97,
      "performance": 100,
      "local": 90
    },
    "pagesRead": [
      "https://www.maplestreetdental.example/",
      "https://www.maplestreetdental.example/contact/"
    ]
  },
  "nap": {
    "name": {
      "gbp": "Maple Street Dental",
      "site": "Maple Street Dental",
      "score": 1,
      "match": true
    },
    "phone": {
      "gbp": "+12175550142",
      "site": [
        "+12175550142"
      ],
      "match": true
    },
    "address": {
      "gbp": "1200 S Maple St, Springfield, IL 62704",
      "postalCodeFound": true,
      "streetFound": true,
      "cityFound": true,
      "match": true
    },
    "consistent": true,
    "score": 100
  },
  "presence": {
    "domain": "maplestreetdental.example",
    "domainCreatedAt": "2014-03-02T10:00:00Z",
    "domainAgeDays": 4609,
    "registrar": "Example Registrar LLC",
    "mailProvider": "Google Workspace",
    "hasMailServer": true,
    "spf": true,
    "dmarcPolicy": null,
    "spfDmarcLookup": "ok"
  },
  "issues": [
    {
      "severity": "high",
      "area": "profile",
      "code": "profile_unclaimed",
      "finding": "The profile is not claimed by the business (anyone can suggest edits and the owner cannot reply to reviews).",
      "recommendation": "Claim and verify the profile in Google Business Profile Manager so the owner controls its details, posts and review replies."
    },
    {
      "severity": "high",
      "area": "visibility",
      "code": "visibility_not_ranking",
      "finding": "Not among Google's top 20 local results for \"emergency dentist in Springfield, Illinois\".",
      "recommendation": "Build relevance for \"emergency dentist\": use it in the categories, description and services of the profile and in the website's homepage title and text, and collect reviews that mention it."
    },
    {
      "severity": "medium",
      "area": "profile",
      "code": "profile_short_description",
      "finding": "The business description is only 30 characters.",
      "recommendation": "Add a 250–750 character description naming the main services and the area served."
    },
    {
      "severity": "medium",
      "area": "profile",
      "code": "profile_few_photos",
      "finding": "The profile has 6 photos; profiles with 10+ get more clicks.",
      "recommendation": "Upload at least 10 recent photos (outside, inside, team, work) and add new ones every month."
    }
  ],
  "issueCounts": {
    "high": 2,
    "medium": 3,
    "low": 5
  },
  "sections": {
    "profile": "ok",
    "visibility": "ok",
    "website": "ok",
    "presence": "ok"
  },
  "status": "ok",
  "charged": true,
  "chargedEvent": "local-audit",
  "source": "dataforseo+serper+website",
  "fetchedAt": "2026-10-15T12:00:00.000Z"
}

FAQ

Questions

What does one audit check?
The Google Business Profile (claimed status, categories, phone, website, hours, description, photos, attributes, services), review counts and the star distribution, the business's position in Google's local results for up to 3 keywords with the 3 best-placed competitors, and the website linked from the profile: HTTPS and certificate, mobile viewport, title, description and H1, speed proxies, LocalBusiness structured data, tap-to-call, freshness, booking and chat widgets, ad pixels, and whether its name, address and phone match the profile. Every gap becomes an issue with a recommendation.
How are the scores calculated?
Profile completeness is a 100-point checklist; reviews combine the rating (60%) and the review volume (40%); local rank gives 100 for the top 3, 80 for 4–5, 60 for 6–10 and 40 below; the website score is 45% SEO, 25% speed and 30% local signals (HTTPS, LocalBusiness data, tap-to-call, freshness, NAP). The overall score weighs the profile 35%, the website 30%, reviews 20% and local rank 15%, leaving out sections that could not be assessed.
What if the business is not found?
Nothing is charged. Names work best with the city ("Joe's Pizza Broadway New York") or a location; place IDs, CIDs and Google Maps links of the place are exact.

Machine-readable

This page for agents