RankSharkDocs

Create a campaign and read results

Find a business on Google Maps, track it on a grid, run a scan and read where it ranks, street by street.

A Track campaign holds a business, its keywords and a grid of points on the map. Each run makes one scan per keyword: at every point, RankShark searches the keyword and records where the business ranks.

This guide goes through the whole loop: find the business, create the campaign, run a scan, wait for it, read the results.

The examples use a fictional business, Harbor Dental in Austin, TX, and are the same as in the API reference. All requests need a Track key: see Getting started.

1. Find the business

Search Google Maps by name and city, or paste a Google Maps link to the place.

curl -X POST https://panel.rankshark.io/v1/track/find \
  -H "Authorization: Bearer $RANKSHARK_TRACK_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "query": "Harbor Dental Austin TX",
    "country": "us"
  }'
{
  "businesses": [
    {
      "name": "Harbor Dental",
      "address": "1200 S Congress Ave, Austin, TX 78704",
      "category": "Dentist",
      "lat": 30.2509,
      "lng": -97.7493,
      "fid": "0x8644b50a3e3c0b1d:0x6a8d1e2f3c4b5a69",
      "pid": "ChIJd8BlQ2BZwokRAFUEcm_qrcA",
      "cid": "7677826127960431209",
      "website": "https://harbordental.example",
      "phone": "+1 512-555-0142",
      "mapsUrl": "https://www.google.com/maps/place/?q=place_id:ChIJd8BlQ2BZwokRAFUEcm_qrcA",
      "rating": 4.8,
      "reviews": 312
    }
  ],
  "source": "maps"
}

A search can take up to two minutes. You can run 60 an hour. Needs track:write.

2. Create the campaign

Send the business you picked, the keywords and the grid. This one is a 7×7 grid reaching 3 miles (4,828 m) from the business, scanned every Monday at 9:00 Austin time, and filed under a business (clientId) in the folder Austin.

curl -X POST https://panel.rankshark.io/v1/track/campaigns \
  -H "Authorization: Bearer $RANKSHARK_TRACK_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Harbor Dental · Austin",
    "business": {
      "name": "Harbor Dental",
      "address": "1200 S Congress Ave, Austin, TX 78704",
      "category": "Dentist",
      "lat": 30.2509,
      "lng": -97.7493,
      "fid": "0x8644b50a3e3c0b1d:0x6a8d1e2f3c4b5a69",
      "pid": "ChIJd8BlQ2BZwokRAFUEcm_qrcA",
      "cid": "7677826127960431209",
      "website": "https://harbordental.example",
      "phone": "+1 512-555-0142",
      "mapsUrl": "https://www.google.com/maps/place/?q=place_id:ChIJd8BlQ2BZwokRAFUEcm_qrcA",
      "rating": 4.8,
      "reviews": 312
    },
    "keywords": [
      "dentist near me",
      "emergency dentist",
      "teeth whitening austin"
    ],
    "engines": [
      "maps"
    ],
    "grid": {
      "size": 7,
      "radiusM": 4828,
      "shape": "square",
      "unit": "mi"
    },
    "schedule": {
      "freq": "weekly",
      "hour": 9,
      "dow": 1,
      "tz": "America/Chicago"
    },
    "clientId": "tc_5Rw8Jn3Ys2QeLk",
    "folder": "Austin"
  }'

The answer is the new campaign (201 Created):

{
  "id": "gt_8fK2mQ9xLp3s",
  "name": "Harbor Dental · Austin",
  "mode": "grid",
  "location": null,
  "business": {
    "name": "Harbor Dental",
    "address": "1200 S Congress Ave, Austin, TX 78704",
    "category": "Dentist",
    "lat": 30.2509,
    "lng": -97.7493,
    "fid": "0x8644b50a3e3c0b1d:0x6a8d1e2f3c4b5a69",
    "pid": "ChIJd8BlQ2BZwokRAFUEcm_qrcA",
    "cid": "7677826127960431209",
    "website": "https://harbordental.example",
    "phone": "+1 512-555-0142",
    "mapsUrl": "https://www.google.com/maps/place/?q=place_id:ChIJd8BlQ2BZwokRAFUEcm_qrcA",
    "rating": 4.8,
    "reviews": 312
  },
  "keywords": [
    "dentist near me",
    "emergency dentist",
    "teeth whitening austin"
  ],
  "engine": "maps",
  "engines": [
    "maps"
  ],
  "webGrid": false,
  "grid": {
    "size": 7,
    "radiusM": 4828,
    "shape": "square",
    "unit": "mi"
  },
  "center": {
    "lat": 30.2509,
    "lng": -97.7493
  },
  "points": 49,
  "aiEngines": null,
  "aliases": null,
  "aiGrid": null,
  "zoom": null,
  "language": "en",
  "country": "us",
  "ipLocation": {
    "country": "us",
    "region": "texas",
    "city": "austin"
  },
  "domain": "harbordental.example",
  "page": null,
  "schedule": {
    "freq": "weekly",
    "hour": 9,
    "dow": 1,
    "dom": 1,
    "tz": "America/Chicago"
  },
  "nextRunAt": "2026-11-09T15:00:00.000Z",
  "lastRunAt": null,
  "paused": false,
  "clientId": "tc_5Rw8Jn3Ys2QeLk",
  "folder": "Austin",
  "lastScheduleError": null,
  "latest": [],
  "createdAt": "2026-09-14T18:22:10.000Z",
  "updatedAt": "2026-11-02T15:06:12.000Z"
}

What you can set:

FieldWhat it is
keywordsWhat people search for. Your plan sets how many per campaign.
engine or enginesmaps, local_pack (Google's map pack in search), search (your website in Google's results), bing_maps, bing_local_pack, bing_search, or ai. With several engines, each keyword is scanned on each one.
gridsize is points per side, odd, 3 to 21. radiusM is the distance from the centre to the edge in metres. shape is square, circle or hexagon.
schedulefreq is off, daily, weekly or monthly, with hour, dow (0 = Sunday), dom and an IANA tz. Daily runs need Track Pro or higher.
clientId, folderThe business this campaign belongs to (tc_…) and a folder inside it, to group campaigns and reports.
runNowtrue also starts the first scan. Needs track:run as well.

GET /v1/track/engines lists the engines your plan includes and what each costs.

Points

A scan costs one point per grid point, so a 7×7 grid spends 49 points per keyword. Some engines cost more per point, and each AI answer costs more. Check GET /v1/track/usage before you run large grids.

3. Run a scan

Send the keywords to scan, or an empty body for all of them. Needs track:run.

curl -X POST https://panel.rankshark.io/v1/track/campaigns/gt_8fK2mQ9xLp3s/run \
  -H "Authorization: Bearer $RANKSHARK_TRACK_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "keywords": [
      "dentist near me"
    ]
  }'
{
  "scans": [
    {
      "id": "gs_4Pz8Qw2Er6TyUi",
      "keyword": "dentist near me",
      "engine": "maps",
      "tracks": [
        "maps"
      ]
    }
  ],
  "points": 49,
  "topupPoints": 0
}

points is what the run costs, and topupPoints how many of those came from bought points. The run is refused with 409 when:

  • quota: your points left this month (and any bought points) do not cover it. The answer tells you how many you need and when your points renew.
  • already_running: every keyword you asked for is being scanned already.

4. Wait for the scan

Scans take a few minutes. Your workspace runs its scans one after another, so a scan may wait for the ones before it. Check every 15 to 30 seconds:

curl "https://panel.rankshark.io/v1/track/campaigns/gt_8fK2mQ9xLp3s/scans?limit=2" \
  -H "Authorization: Bearer $RANKSHARK_TRACK_KEY"
[
  {
    "id": "gs_4Pz8Qw2Er6TyUi",
    "keyword": "emergency dentist",
    "engine": "maps",
    "mode": "grid",
    "status": "running",
    "trigger": "manual",
    "createdAt": "2026-11-05T10:12:40.000Z",
    "startedAt": "2026-11-05T10:13:02.000Z",
    "finishedAt": null,
    "progress": {
      "done": 18,
      "failed": 0,
      "total": 49
    },
    "metrics": null,
    "organicMetrics": null,
    "error": null,
    "note": null,
    "grid": {
      "size": 7,
      "radiusM": 4828,
      "shape": "square",
      "unit": "mi"
    },
    "suspect": false,
    "wait": {
      "ahead": 0,
      "startsInMin": 0,
      "minutesLeft": 4
    }
  },
  {
    "id": "gs_3Hq7Vt2NwR8cYp",
    "keyword": "dentist near me",
    "engine": "maps",
    "mode": "grid",
    "status": "done",
    "trigger": "schedule",
    "createdAt": "2026-11-02T15:00:04.000Z",
    "startedAt": "2026-11-02T15:00:31.000Z",
    "finishedAt": "2026-11-02T15:06:12.000Z",
    "progress": {
      "done": 49,
      "failed": 0,
      "total": 49
    },
    "metrics": {
      "points": 49,
      "total": 49,
      "found": 44,
      "foundPct": 89.8,
      "top3": 27,
      "top10": 39,
      "arp": 4.18,
      "atrp": 5.9,
      "solv": 55.1
    },
    "organicMetrics": null,
    "error": null,
    "note": null,
    "grid": {
      "size": 7,
      "radiusM": 4828,
      "shape": "square",
      "unit": "mi"
    },
    "suspect": false
  }
]

A scan is finished when status is done, failed or cancelled. Stop polling then. While it waits or runs, wait says how many scans are ahead and about how many minutes are left.

poll.js
const KEY = process.env.RANKSHARK_TRACK_KEY;
const BASE = 'https://panel.rankshark.io/v1/track';
const FINISHED = ['done', 'failed', 'cancelled'];

async function waitForScan(scanId) {
  for (;;) {
    const res = await fetch(`${BASE}/scans/${scanId}`, { headers: { Authorization: `Bearer ${KEY}` } });
    if (res.status === 429) {
      await new Promise((r) => setTimeout(r, Number(res.headers.get('Retry-After') || 60) * 1000));
      continue;
    }
    const scan = await res.json();
    if (FINISHED.includes(scan.status)) return scan;
    await new Promise((r) => setTimeout(r, 20_000));
  }
}

To cancel a scan, POST /v1/track/scans/{id}/cancel. If no point was scanned yet, its points come back.

5. Read the results

curl https://panel.rankshark.io/v1/track/scans/gs_3Hq7Vt2NwR8cYp \
  -H "Authorization: Bearer $RANKSHARK_TRACK_KEY"

The scan has the metrics, every grid point, the listings seen (places) and the competitors. Shortened here to a few points and competitors:

{
  "id": "gs_3Hq7Vt2NwR8cYp",
  "keyword": "dentist near me",
  "engine": "maps",
  "mode": "grid",
  "status": "done",
  "trigger": "schedule",
  "createdAt": "2026-11-02T15:00:04.000Z",
  "startedAt": "2026-11-02T15:00:31.000Z",
  "finishedAt": "2026-11-02T15:06:12.000Z",
  "progress": {
    "done": 49,
    "failed": 0,
    "total": 49
  },
  "metrics": {
    "points": 49,
    "total": 49,
    "found": 44,
    "foundPct": 89.8,
    "top3": 27,
    "top10": 39,
    "arp": 4.18,
    "atrp": 5.9,
    "solv": 55.1
  },
  "organicMetrics": null,
  "error": null,
  "note": null,
  "grid": {
    "size": 7,
    "radiusM": 4828,
    "shape": "square",
    "unit": "mi"
  },
  "suspect": false,
  "campaignId": "gt_8fK2mQ9xLp3s",
  "trackerId": "gt_8fK2mQ9xLp3s",
  "center": {
    "lat": 30.2509,
    "lng": -97.7493
  },
  "zoom": 13,
  "language": "en",
  "country": "us",
  "ipArea": "austin, texas, US",
  "target": {
    "name": "Harbor Dental",
    "domain": "harbordental.example",
    "fid": "0x8644b50a3e3c0b1d:0x6a8d1e2f3c4b5a69",
    "pid": "ChIJd8BlQ2BZwokRAFUEcm_qrcA",
    "key": "0x6a8d1e2f3c4b5a69",
    "phone": "+1 512-555-0142"
  },
  "points": [
    {
      "i": 0,
      "row": 0,
      "col": 0,
      "lat": 30.2943,
      "lng": -97.7995,
      "st": "ok",
      "rank": 3,
      "ids": [
        "0x3b8a1f0c2d9e4a17",
        "0x1c2d3e4f5a6b7c8d",
        "0x6a8d1e2f3c4b5a69"
      ],
      "depth": 20
    },
    {
      "i": 1,
      "row": 0,
      "col": 1,
      "lat": 30.2943,
      "lng": -97.7828,
      "st": "ok",
      "rank": 2,
      "ids": [
        "0x3b8a1f0c2d9e4a17",
        "0x6a8d1e2f3c4b5a69",
        "0x1c2d3e4f5a6b7c8d"
      ],
      "depth": 20
    },
    {
      "i": 24,
      "row": 3,
      "col": 3,
      "lat": 30.2509,
      "lng": -97.7493,
      "st": "ok",
      "rank": 1,
      "ids": [
        "0x6a8d1e2f3c4b5a69",
        "0x3b8a1f0c2d9e4a17",
        "0x1c2d3e4f5a6b7c8d"
      ],
      "depth": 20
    }
  ],
  "places": {
    "0x6a8d1e2f3c4b5a69": {
      "name": "Harbor Dental",
      "category": "Dentist",
      "rating": 4.8,
      "reviews": 312,
      "address": "1200 S Congress Ave, Austin, TX 78704",
      "lat": 30.2509,
      "lng": -97.7493,
      "fid": "0x8644b50a3e3c0b1d:0x6a8d1e2f3c4b5a69",
      "pid": "ChIJd8BlQ2BZwokRAFUEcm_qrcA"
    },
    "0x3b8a1f0c2d9e4a17": {
      "name": "Bluebonnet Family Dentistry",
      "category": "Dentist",
      "rating": 4.6,
      "reviews": 198,
      "address": "815 W 6th St, Austin, TX 78703",
      "lat": 30.2716,
      "lng": -97.7529,
      "fid": "0x8644b5a1c2d3e4f5:0x3b8a1f0c2d9e4a17",
      "pid": null
    },
    "0x1c2d3e4f5a6b7c8d": {
      "name": "Lakeline Smiles",
      "category": "Cosmetic dentist",
      "rating": 4.9,
      "reviews": 87,
      "address": "2300 S Lamar Blvd, Austin, TX 78704",
      "lat": 30.2468,
      "lng": -97.7801,
      "fid": "0x8644b4c3d2e1f0a9:0x1c2d3e4f5a6b7c8d",
      "pid": null
    }
  },
  "competitors": [
    {
      "key": "0x6a8d1e2f3c4b5a69",
      "name": "Harbor Dental",
      "category": "Dentist",
      "rating": 4.8,
      "reviews": 312,
      "address": "1200 S Congress Ave, Austin, TX 78704",
      "lat": 30.2509,
      "lng": -97.7493,
      "fid": "0x8644b50a3e3c0b1d:0x6a8d1e2f3c4b5a69",
      "pid": "ChIJd8BlQ2BZwokRAFUEcm_qrcA",
      "appearances": 44,
      "arp": 4.18,
      "atrp": 5.9,
      "solv": 55.1,
      "top3": 27,
      "best": 1,
      "isTarget": true
    },
    {
      "key": "0x3b8a1f0c2d9e4a17",
      "name": "Bluebonnet Family Dentistry",
      "category": "Dentist",
      "rating": 4.6,
      "reviews": 198,
      "address": "815 W 6th St, Austin, TX 78703",
      "lat": 30.2716,
      "lng": -97.7529,
      "fid": "0x8644b5a1c2d3e4f5:0x3b8a1f0c2d9e4a17",
      "pid": null,
      "appearances": 46,
      "arp": 3.61,
      "atrp": 4.67,
      "solv": 61.22,
      "top3": 30,
      "best": 1,
      "isTarget": false
    }
  ]
}

How to read it:

MetricMeaning
rankWhere the business ranks at that point. 1 is first. null means not in the top 20.
idsThe listings seen at that point, in order. Look them up in places.
solvShare of local voice: the percentage of points where the business is in the top 3.
arpAverage rank at the points where it was found.
atrpAverage rank over all points, counting "not found" as 21. Lower is better.
foundPctThe percentage of points where it was found at all.
suspecttrue when the results may be unreliable. Such scans are left out of trends, comparisons and reports.

Competitors

For the competitors alone, best first:

curl "https://panel.rankshark.io/v1/track/scans/gs_3Hq7Vt2NwR8cYp/competitors?limit=3" \
  -H "Authorization: Bearer $RANKSHARK_TRACK_KEY"
{
  "scanId": "gs_3Hq7Vt2NwR8cYp",
  "keyword": "dentist near me",
  "engine": "maps",
  "status": "done",
  "competitors": [
    {
      "key": "0x6a8d1e2f3c4b5a69",
      "name": "Harbor Dental",
      "category": "Dentist",
      "rating": 4.8,
      "reviews": 312,
      "address": "1200 S Congress Ave, Austin, TX 78704",
      "lat": 30.2509,
      "lng": -97.7493,
      "fid": "0x8644b50a3e3c0b1d:0x6a8d1e2f3c4b5a69",
      "pid": "ChIJd8BlQ2BZwokRAFUEcm_qrcA",
      "appearances": 44,
      "arp": 4.18,
      "atrp": 5.9,
      "solv": 55.1,
      "top3": 27,
      "best": 1,
      "isTarget": true
    },
    {
      "key": "0x3b8a1f0c2d9e4a17",
      "name": "Bluebonnet Family Dentistry",
      "category": "Dentist",
      "rating": 4.6,
      "reviews": 198,
      "address": "815 W 6th St, Austin, TX 78703",
      "lat": 30.2716,
      "lng": -97.7529,
      "fid": "0x8644b5a1c2d3e4f5:0x3b8a1f0c2d9e4a17",
      "pid": null,
      "appearances": 46,
      "arp": 3.61,
      "atrp": 4.67,
      "solv": 61.22,
      "top3": 30,
      "best": 1,
      "isTarget": false
    },
    {
      "key": "0x1c2d3e4f5a6b7c8d",
      "name": "Lakeline Smiles",
      "category": "Cosmetic dentist",
      "rating": 4.9,
      "reviews": 87,
      "address": "2300 S Lamar Blvd, Austin, TX 78704",
      "lat": 30.2468,
      "lng": -97.7801,
      "fid": "0x8644b4c3d2e1f0a9:0x1c2d3e4f5a6b7c8d",
      "pid": null,
      "appearances": 31,
      "arp": 6.4,
      "atrp": 11.78,
      "solv": 18.37,
      "top3": 9,
      "best": 2,
      "isTarget": false
    }
  ]
}

For a spreadsheet, GET /v1/track/scans/{id}/csv.

Next

On this page