# Google Maps Business Profile Rank Checker (`khadinakbar/local-rank-tracker`) Actor

Check Google Maps local-pack rank by keyword, device, and geographic point. Returns one auditable rank row per keyword/location snapshot with match evidence, competitors, verification URL, timestamp, and provider cost.

- **URL**: https://apify.com/khadinakbar/local-rank-tracker.md
- **Developed by:** [Khadin Akbar](https://apify.com/khadinakbar) (community)
- **Categories:** SEO tools, Automation, MCP servers
- **Features:** Limited permissions
- **Stats:** 8 total users, 1 monthly users, 75.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $60.00 / 1,000 local rank checks

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

### What is Google Maps Business Profile Rank Checker?

Check where a business appears in Google Maps local results for a specific keyword, language, device, and geographic point. The Actor defaults to the SEO data provider Google Maps Standard priority for lower provider cost, with Live available when realtime latency matters. It writes one auditable dataset record per keyword/location check with `localPackRank`, `rankStatus`, competitor positions, a verification URL, and `checkedAt`.

### Fit for this Actor

Use this Actor for local SEO agencies, multi-location operators, franchise reporting, and coordinate-based Google Business Profile visibility checks. Provide the business name plus a domain, Google place ID, or CID when available; those stable signals make target matching clearer than a name alone.

For business discovery and contact enrichment, continue with the public Google Maps Scraper — Emails, Phones & B2B Leads. This Actor is the focused rank-evidence step after the business and search intent are known.

Each run is a dated Maps snapshot. Schedule runs and retain the datasets when you need a time series. Interpret `not_found` as “the target was outside the provider's returned Maps results for this snapshot”; use the location, device, depth, and `checkUrl` fields to keep the conclusion scoped.

### Scope and interpretation

The Actor reports the returned Maps provider snapshot for the requested keyword, location, language, device, and depth. It does not claim a separate organic SERP position. Use the recorded `checkUrl`, `provider`, and `checkedAt` values when comparing observations across scheduled executions.

### How one workflow works

An agency starts with a client's profile name, website domain, three service keywords, and a small coordinate grid around the service area. Each keyword/location pair becomes one row. The agency compares `localPackRank` by `keyword` + `locationLabel`, reviews `localPackResults` to see which businesses appeared nearby, and uses `targetMatchMethod` plus `checkUrl` when explaining a visibility change to the client.

### Input

Credentials stay in the environment rather than in visible Actor input.

`providerMode` defaults to `standard`, which submits a queued SEO data provider Maps task at normal priority. Use `live` for a realtime request. The Actor records the selected mode and provider cost, and fails closed before writing a billable row if a provider response exceeds `$0.006` per check. At the current `$0.06` event price and Apify's 80% share, that is the guardrail for at least a 70% contribution margin before fixed maintenance costs.

Each location sets exactly one of `locationCode`, `locationName`, or `locationCoordinate`. Coordinates provide a direct path for a local geo-grid. The matrix is bounded at 20 keywords × 25 locations (500 checks).

```json
{
  "businessName": "Acme Dental",
  "targetDomain": "acmedental.com",
  "keywords": ["dentist near me", "emergency dentist"],
  "locations": [
    { "label": "Downtown", "locationCoordinate": "40.7128,-74.0060,500" },
    { "label": "Northwest", "locationCoordinate": "40.7306,-73.9866,500" }
  ],
  "languageCode": "en",
  "device": "desktop",
  "depth": 100,
  "providerMode": "standard",
  "competitorNames": ["Rival Dental"]
}
```

Use `placeId` or `cid` when known. Otherwise, `targetDomain` is a stable matching signal alongside business name. `device` accepts `desktop` or `mobile`; `depth` accepts 20–100 Maps results.

### Output

One dataset record represents one completed keyword/location snapshot.

| Field | Meaning |
| --- | --- |
| `localPackRank` | Target position in the returned local pack, or `null` |
| `rankStatus` | `local_pack` when matched, otherwise `not_found` for this Maps-only check |
| `targetMatchMethod` | `place_id`, `cid`, `domain`, or normalized business-name match |
| `localPackResults` | Compact businesses returned in the local pack |
| `competitorRanks` | Requested competitor names found in the snapshot |
| `checkUrl` | Provider verification URL when available |
| `checkedAt` | ISO timestamp for the snapshot |

Illustrative schema-valid item:

```json
{
  "runId": "example-run-id",
  "checkedAt": "ISO_8601_TIMESTAMP",
  "keyword": "dentist near me",
  "locationLabel": "Downtown",
  "locationCode": null,
  "locationName": null,
  "locationCoordinate": "40.7128,-74.0060,500",
  "languageCode": "en",
  "device": "desktop",
  "depth": 100,
  "businessName": "Acme Dental",
  "targetDomain": "acmedental.com",
  "placeId": null,
  "cid": null,
  "localPackRank": 2,
  "organicRank": null,
  "localPackFound": true,
  "organicFound": false,
  "rankStatus": "local_pack",
  "targetMatchMethod": "domain",
  "localPackSize": 3,
  "localPackResults": [{ "name": "Acme Dental", "domain": "acmedental.com", "rank": 2 }],
  "competitorRanks": [{ "name": "Rival Dental", "rank": 1, "rankType": "local_pack" }],
  "providerMode": "standard",
  "providerCostUsd": 0.01,
  "checkUrl": "https://www.google.com/search?q=dentist+near+me&hl=en",
  "serpItemTypes": ["maps_search"],
  "warnings": []
}
```

The provider snapshot is the provenance boundary: Google results vary by location, device, language, personalization, and time. `OUTPUT`, `RUN_SUMMARY`, and `LAST_RUN_SUMMARY` are written for every terminal outcome. `COMPLETE` means every requested check persisted; `PARTIAL` preserves useful rows when some checks fail; `VALID_EMPTY` represents a functioning provider response with no persisted rows; `INVALID_INPUT`, `CONFIG_ERROR`, and `UPSTREAM_FAILED` contain actionable diagnostics.

### Pricing

#### Startup billing and empty runs

The `apify-actor-start` event costs **$0.00005 per startup unit**. Apify charges one startup unit per GB of allocated memory, with a minimum of one unit, so a 2 GB run incurs two units. This startup charge still applies when input is invalid, no results are found, or an upstream request fails. Statements about free, unbilled, or skipped results refer only to the corresponding result event; they do not waive the startup charge. Apify platform usage is included in the event prices and is not passed through as an additional customer charge. Check the live [Pricing tab](https://apify.com/khadinakbar/local-rank-tracker/pricing) for current events, tiers, and rates. Result limits cap result events, not the entire run cost.

The Actor uses Pay per event with Apify platform usage included. One `local-rank-check` event represents one persisted keyword/location snapshot; the Actor prints the maximum event-charge cap before work begins. The default Standard provider mode targets about `$0.0006` per Maps task, while Live targets about `$0.002`; both are below the `$0.006` guardrail at the current `$0.06` event price. The SEO data provider cost is reported in each row and in `RUN_SUMMARY` and is billed to the supplied the SEO data provider account. The live Pricing tab is the source of truth for current event prices and platform usage.

### API and dataset readback

```bash
curl "https://api.apify.com/v2/acts/khadinakbar~local-rank-tracker/runs?token=$APIFY_TOKEN" \
  -H 'content-type: application/json' \
  -d '{"businessName":"Acme Dental","targetDomain":"acmedental.com","keywords":["dentist near me"],"locations":[{"label":"Downtown","locationCoordinate":"40.7128,-74.0060,500"}]}'
```

Read the completed execution's default dataset from the API result or `https://api.apify.com/v2/datasets/{DATASET_ID}/items`. Read `OUTPUT` and `RUN_SUMMARY` from the execution's default key-value store. For large matrices, page the dataset and join records by `keyword` + `locationLabel`; each record is already bounded to a compact local-pack preview.

### MCP / agent prompt

> Check Acme Dental for `dentist near me` at the supplied coordinate. Return the Google Maps local-pack rank, rank status, match method, top local competitors, check URL, and timestamp. Keep the exact location and device in the explanation, and report partial or empty outcomes clearly.

For lowest cost, leave `providerMode` as `standard`; request `live` only when the workflow needs a realtime provider response.

MCP clients can use this read-only, no-side-effect snapshot tool when they have a business name, one keyword, and one location; a domain, place ID, or CID improves matching. The result is one row per check, with provider provenance, terminal outcome records, bounded output, and dataset readback through `get-actor-output` or the Apify dataset API. Cost is the per-check event with Apify platform usage included; provider usage is separate, and a matrix can be paged by dataset item ranges. Inspect the terminal outcome before summarizing rows.

### Results guidance

- Keep the same coordinate grid and keyword spelling across scheduled runs so comparisons remain meaningful.
- Prefer `placeId` or `cid`, then `targetDomain`, before relying on normalized business-name matching.
- Use `mobile` when the reporting question is specifically mobile local visibility.
- Treat `not_found` as “not found in this returned snapshot and depth,” then inspect `checkUrl` and location settings before drawing a conclusion.
- Keep batches bounded so provider spend and dataset readback remain predictable.

### Builder's note

I built the implementation with local-pack parsing and rank matching separate from the provider request layer. I found that this makes the dataset useful even when the SEO data provider changes whether local-pack businesses arrive as direct items or nested `items`, while `targetMatchMethod` tells an operator how much confidence to place in each match.

### Boundary and responsible use

Use public search-result evidence and credentials you are authorized to operate. Follow the SEO data provider and Google terms, applicable law, and privacy obligations. Present the output as a dated provider snapshot, preserve the independent Google affiliation boundary, and support profile recommendations with the recorded evidence.

### Continue the workflow

- Then use [📩 Google Maps Email Extractor — Business Emails by City](https://apify.com/khadinakbar/google-maps-email-extractor) to extend Google Maps Business Profile Rank Checker research with a complementary enrichment contract.
- Then use [Naver Map Scraper — Korean Places, Phone & Menus](https://apify.com/khadinakbar/naver-map-scraper) to extend Google Maps Business Profile Rank Checker with a neighboring local-business research source when the brief calls for Naver Maps data.

### Managed research allowance

Free Apify callers share a $0.10 supplier-cost allowance per rolling 30 days across these managed research tools, within a $1 daily and $10 rolling family free pool. Results and supported search options stay unchanged within that allowance. Once exhausted, no further owner-funded research is requested; the existing start fee can still apply. Paid callers keep the existing prices and platform-usage payer. Identical supplier work inside one run can reuse its completed response, without sharing data between users or runs. Existing result-event billing remains unchanged; no new research-credit fee is added.

Paid routes priced per upstream request retain one supplier task per billed request. Completed same-run reuse applies where it preserves the existing billable unit.

# Changelog

This Actor's version history is a separate document: https://apify.com/khadinakbar/local-rank-tracker/changelog.md

# Actor input Schema

## `businessName` (type: `string`):

The business name to find in Google Maps local results (e.g. 'Acme Dental'). Use the name shown on the Business Profile. This is a matching hint, not a Google Business Profile ID.

## `providerMode` (type: `string`):

Cost-optimized Standard priority is the default and typically costs about $0.0006 per SERP task; choose Live only when a realtime result is worth about $0.002. The Actor rejects provider responses above $0.006 per check so the $0.06 event retains at least a 70% contribution-margin floor after Apify's 80% share.

## `targetDomain` (type: `string`):

Optional website domain used as a stronger match signal (e.g. 'acmedental.com'). Enter a domain only, without https:// or a path. This does not crawl the website or verify ownership.

## `placeId` (type: `string`):

Optional stable Google place identifier for exact matching when the provider returns it. Example: 'ChIJ...'. This is not an Apify Actor ID and cannot be discovered by this run.

## `cid` (type: `string`):

Optional Google client ID for a local establishment. Example: '1234567890123456789'. Use this when your business already has a known CID; it is stronger than name-only matching.

## `keywords` (type: `array`):

Search terms to check, such as \['dentist near me', 'emergency dentist']. Each keyword is tested at every location. Provide 1–20 terms; this Actor does not generate keyword ideas.

## `locations` (type: `array`):

Geographic points where each keyword is checked. Each object must set exactly one of locationCode, locationName, or locationCoordinate. Coordinates make a geo-grid possible; use 'latitude,longitude' or 'latitude,longitude,radius'. Maximum 25 locations.

## `languageCode` (type: `string`):

the keyword-data API Google language code for the snapshot, such as 'en' or 'es'. Defaults to 'en'. This controls result language and is not a country selector.

## `device` (type: `string`):

Device profile used for the Google snapshot. 'desktop' returns up to the requested depth; 'mobile' follows Google's mobile result layout. Defaults to desktop.

## `depth` (type: `integer`):

How many Google Maps businesses the keyword-data API should inspect per check (e.g. 100). Defaults to 100; higher depth can increase provider charges, while this Actor's Apify event remains one check row. This is Maps result depth, not an organic SERP depth.

## `competitorNames` (type: `array`):

Optional business names to locate in the same returned Google Maps rows. Example: \['Rival Dental']. Name matching is exact after normalization; competitor rows are not separate checks or separate charges.

## Actor input object example

```json
{
  "businessName": "Acme Dental",
  "providerMode": "standard",
  "targetDomain": "acmedental.com",
  "keywords": [
    "dentist near me",
    "emergency dentist"
  ],
  "locations": [
    {
      "label": "Downtown",
      "locationCoordinate": "40.7128,-74.0060,500"
    }
  ],
  "languageCode": "en",
  "device": "desktop",
  "depth": 100,
  "competitorNames": []
}
```

# Actor output Schema

## `rankChecks` (type: `string`):

One row per successful keyword/location Google Maps snapshot, including local rank positions.

## `runSummary` (type: `string`):

Detailed terminal run summary with provider failures, counts, and provider cost.

## `compactOutput` (type: `string`):

Stable terminal outcome for automations and agents.

## `lastRunSummary` (type: `string`):

Compatibility alias containing the latest detailed terminal summary.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "businessName": "Acme Dental",
    "providerMode": "standard",
    "targetDomain": "acmedental.com",
    "keywords": [
        "dentist near me",
        "emergency dentist"
    ],
    "locations": [
        {
            "label": "Downtown",
            "locationCoordinate": "40.7128,-74.0060,500"
        }
    ],
    "languageCode": "en",
    "device": "desktop",
    "depth": 100,
    "competitorNames": []
};

// Run the Actor and wait for it to finish
const run = await client.actor("khadinakbar/local-rank-tracker").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "businessName": "Acme Dental",
    "providerMode": "standard",
    "targetDomain": "acmedental.com",
    "keywords": [
        "dentist near me",
        "emergency dentist",
    ],
    "locations": [{
            "label": "Downtown",
            "locationCoordinate": "40.7128,-74.0060,500",
        }],
    "languageCode": "en",
    "device": "desktop",
    "depth": 100,
    "competitorNames": [],
}

# Run the Actor and wait for it to finish
run = client.actor("khadinakbar/local-rank-tracker").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "businessName": "Acme Dental",
  "providerMode": "standard",
  "targetDomain": "acmedental.com",
  "keywords": [
    "dentist near me",
    "emergency dentist"
  ],
  "locations": [
    {
      "label": "Downtown",
      "locationCoordinate": "40.7128,-74.0060,500"
    }
  ],
  "languageCode": "en",
  "device": "desktop",
  "depth": 100,
  "competitorNames": []
}' |
apify call khadinakbar/local-rank-tracker --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,khadinakbar/local-rank-tracker"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/150Bt0Oaw0KmwnodN/builds/Kdd9EmbyeFGU1rVpA/openapi.json
