Google Maps Competitor Change Tracker avatar

Google Maps Competitor Change Tracker

Pricing

$10.00 / 1,000 qualified competitor change results

Go to Apify Store
Google Maps Competitor Change Tracker

Google Maps Competitor Change Tracker

Compare structured Google Maps listing snapshots or persist a monitor baseline to detect explainable competitor changes, review velocity, closures, category moves, profile updates, and new or removed listings.

Pricing

$10.00 / 1,000 qualified competitor change results

Rating

0.0

(0)

Developer

Khoa Nguyen

Khoa Nguyen

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

16 days ago

Last modified

Categories

Share

Turn two structured Google Maps listing snapshots into explainable competitor alerts, or run in monitor mode and compare each new snapshot with a baseline stored in Apify Key-Value Store.

This Actor is designed for local SEO agencies, franchise operators, multi-location brands, and competitive-intelligence teams. It does not scrape Google Maps directly. It accepts listing data from an existing compliant data source, normalizes common scraper/export formats, safely matches entities, detects meaningful changes, scores impact, and publishes an agency-ready report.

What it detects

  • New and removed competitors
  • Business status changes, including temporary and permanent closure
  • Rating, review-count, and review-velocity movement
  • Primary and secondary category changes
  • Opening-hours changes
  • Name, address, phone, website, and coordinate changes
  • Price-level and claimed-profile changes
  • Photo-count, description, attributes, and service-option changes
  • Booking, ordering, and menu URL changes

Each alert contains the before/after evidence, matching method, confidence, field-level explanations, impact score, severity, and a recommended action.

Two-minute start

The default input runs fully offline and requires no credentials:

{
"runMode": "demo"
}

The demo uses realistic bundled fixtures but sends them through the same normalization, validation, deduplication, matching, diff, scoring, filtering, dataset, and report pipeline used by live inputs.

Expected default output:

  • Non-empty default dataset with qualified alert records
  • OUTPUT summary in the default Key-Value Store
  • REPORT.md human-readable report
  • NORMALIZED_CURRENT normalized snapshot
  • MATCH_DIAGNOSTICS data-quality and matching evidence

Input modes

Compare two snapshots

Provide two inline arrays:

{
"runMode": "compare",
"previousObservedAt": "2026-06-01T00:00:00Z",
"currentObservedAt": "2026-07-01T00:00:00Z",
"previousSnapshot": [
{
"placeId": "ChIJ-example-1",
"name": "Alpha Dental",
"address": "1 Main Street",
"rating": 4.4,
"reviewCount": 120
}
],
"currentSnapshot": [
{
"placeId": "ChIJ-example-1",
"name": "Alpha Dental",
"address": "1 Main Street",
"rating": 4.6,
"reviewCount": 146,
"bookingUrl": "https://alpha-dental.example/book"
}
]
}

You can instead provide public HTTPS URLs with previousSnapshotUrl and currentSnapshotUrl. URL sources can return JSON arrays, objects containing items, results, or data, JSONL/NDJSON, or CSV.

Monitor a recurring baseline

First run:

{
"runMode": "monitor",
"baselineKey": "COMPETITOR_DETROIT_DENTISTS",
"currentSnapshot": [
{
"placeId": "ChIJ-example-1",
"name": "Alpha Dental",
"rating": 4.6,
"reviewCount": 146
}
]
}

The default firstRunBehavior is store-and-exit, so the first run creates the baseline without generating false “new competitor” alerts. Later runs load that state, compare it with the new snapshot, and update the baseline only after processing and persistence complete successfully. When the SDK reports that the pay-per-event limit was reached, the baseline is not advanced, preventing silent loss of undelivered changes.

Use different baselineKey values to maintain independent markets, brands, or campaigns.

Supported source fields

The normalizer accepts common aliases rather than requiring one vendor-specific export. Examples include:

Canonical fieldAccepted examples
Place IDplaceId, place_id, googlePlaceId
CIDcid, googleCid
Namename, title, businessName, companyName
Addressaddress, fullAddress, formattedAddress, streetAddress
Ratingrating, totalScore, stars, averageRating
ReviewsreviewCount, reviewsCount, userRatingCount, numberOfReviews
Phonephone, phoneNumber, telephone, phoneUnformatted
Websitewebsite, websiteUrl, url, companyUrl
Coordinatestop-level latitude/longitude or nested location/coordinates
Hoursobject or array forms under hours, openingHours, regularOpeningHours, workingHours
TimestampobservedAt, scrapedAt, timestamp, capturedAt

Rows missing a business name or containing invalid ratings, review counts, or coordinates are isolated as row errors. The run fails only when the configured invalid-row ratio is exceeded.

Entity matching

Matching is one-to-one and evidence-based:

  1. Exact Google Place ID
  2. Exact Google CID
  3. Strong normalized keys such as phone, domain, and name/address fingerprint
  4. Candidate-blocked fuzzy matching using business-name tokens, address similarity, and geographic distance
  5. Ambiguity rejection when two candidates are too close in score

A listing with a different explicit Place ID is never fuzzy-matched to another explicit Place ID. A shared chain website alone cannot match distant branches. Exact IDs are strongly recommended for the most reliable monitoring.

Output record

A qualified dataset item contains:

{
"recordType": "alert",
"schemaVersion": 3,
"alertId": "alert:...",
"entityId": "place:ChIJ-example-1",
"changeType": "UPDATED",
"competitorName": "Alpha Dental",
"severity": "high",
"impactScore": 78,
"confidence": 0.71,
"headline": "Alpha Dental changed reviewCount, rating and bookingUrl (high)",
"recommendedAction": "Compare review acquisition velocity and investigate the movement.",
"match": {},
"changes": [],
"metrics": {},
"competitor": {
"previous": {},
"current": {}
},
"sourceUrl": null,
"detectedAt": "2026-07-01T00:00:00.000Z",
"provenance": {}
}

See sample-output/dataset.json for complete records generated from the bundled fixtures.

Pay-per-event behavior

The primary custom event is:

google-maps-competitor-change-tracker-result

One custom event is passed to Actor.pushData() for each alert that survives validation, matching, thresholds, severity filtering, and result limits. Invalid-row error items, when enabled, are pushed without the custom event. Pricing still has to be configured in Apify Console; pricing.example.json is deployment documentation, not proof that pricing is active.

Roadmap pricing:

  • Launch: $10 per 1,000 qualified results
  • Target: $15 per 1,000 qualified results

Local development

Requirements: Node.js 22 or newer.

npm ci
npm run typecheck
npm test
npm run samples
npm run validate
FORCE_LOCAL_RUNTIME=1 npm start

The local fallback writes Apify-compatible artifacts under storage/. On Apify, the Actor imports and uses the official apify SDK.

Safety and reliability

  • Strict TypeScript with noUncheckedIndexedAccess
  • Record and response-size limits
  • Bounded retry count with exponential backoff and jitter
  • Per-request timeout
  • HTTPS-only URL imports
  • Credential-bearing, localhost, private, and reserved targets rejected
  • Redirect destinations revalidated before follow-up
  • Deterministic deduplication, alert IDs, sorting, and matching
  • No browser automation, login bypass, CAPTCHA bypass, or anti-bot evasion
  • No API key required for default Store QA
  • Baseline state is not advanced after a reported billing limit

Limitations

  • The Actor analyzes structured snapshots; it does not collect Google Maps pages itself.
  • Review velocity requires meaningful snapshot timestamps.
  • Without Place ID or CID, matching is probabilistic and may reject ambiguous pairs rather than risk a false match.
  • A missing listing means “not present in this snapshot,” not guaranteed business closure. Verify important removals.
  • Scheduling, notifications, and webhooks are configured through Apify Platform around the Actor; they are not embedded notification providers.
  • URL import allows public data only. Private network sources should be imported inline through an authorized upstream workflow.

Project files

  • ARCHITECTURE.md — product and engineering design
  • STORE_LISTING.md — publish-ready Store copy
  • PRICING.md — event strategy and economics
  • DEPLOYMENT.md — cloud deployment checklist
  • sample-output/ — output generated by the real pipeline
  • BUILD_REPORT.md, VALIDATION.md — generated build evidence in the final package

License

Apache-2.0. See LICENSE.