# 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
- **Stats:** 1 total users, 0 monthly users, 100.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 and usage. You are charged both the fixed price for specific events and for Apify platform usage.

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

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## 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 DataForSEO 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](https://apify.com/khadinakbar/google-maps-leads-scraper). 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

Set `DATAFORSEO_LOGIN` and `DATAFORSEO_PASSWORD` as Actor environment variables using the credentials from [DataForSEO API access](https://app.dataforseo.com/api-access). Credentials stay in the environment rather than in visible Actor input.

`providerMode` defaults to `standard`, which submits a queued DataForSEO 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" }],
  "provider": "dataforseo",
  "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

The Actor uses Pay per event + usage with Apify platform usage passed through. 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 DataForSEO provider cost is reported in each row and in `RUN_SUMMARY` and is billed to the supplied DataForSEO 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 plus Apify platform usage; 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 DataForSEO 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 DataForSEO 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.

# 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`):

DataForSEO 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 DataForSEO 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/cKDivfuuFrKojdLbg/openapi.json
