GuideGuias/Track local rankings

Este guia está disponível em inglês.

How do I track local search rankings with an API?

How to Track Local Rankings with an API

Track local search rankings on a geo grid through an API, add keywords, read the grid and trend, and compare against competitors and citation coverage.

Em resumo

O que este guia realiza
Track keywords on a geo grid for a location, read ranks and trends, and compare against competitors.
APIs utilizadas
Referência
SEO
Pré-requisitos
  • An API key with write access to add keywords
  • The locationId to track, and its clientId for rollups
Casos de uso típicos
  • Adding a rank-tracking tab to a local marketing product
  • Monitoring how a location ranks block by block
  • Benchmarking a business against its local competitors

Local rank is not a single number. Where the searcher stands changes the result, so this API measures a keyword at many points on a grid around the business and reports the spread. You add keywords with a write, wait for the background scan, then read the grid, the trend and the competitor picture. Everything after the add is a read.

The shape of the problem

  1. Add keywordsPOST /api/v1/seo/keywordsUp to 25 per location, grid geometry set once
  2. ScanA background scan runs per keyword; results are not immediate
  3. Read the gridGET /api/v1/seo/keywordsPer-point ranks, trend, stat cards
  4. CompareGET /api/v1/seo/competitorsWho wins the points you lose
From a keyword to a grid of ranks

Add the keywords

POST /api/v1/seo/keywords starts tracking up to 25 keywords for a location. The grid geometry, how many points per side and how far apart, is fixed the first time a location is configured.

curl -X POST https://ai.synup.com/api/v1/seo/keywords \
  -H "Authorization: Bearer $SYNUP_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "locationId": "LOCATION_ID",
    "keywords": ["dentist san francisco", "teeth whitening near me"],
    "gridSize": 5,
    "distanceKm": 1.5
  }'
const API = "https://ai.synup.com/api/v1";
const headers = {
  Authorization: `Bearer ${process.env.SYNUP_API_KEY}`,
  "Content-Type": "application/json",
};

async function trackKeywords(locationId, keywords) {
  const res = await fetch(`${API}/seo/keywords`, {
    method: "POST",
    headers,
    body: JSON.stringify({ locationId, keywords, gridSize: 5, distanceKm: 1.5 }),
  });
  if (!res.ok) throw new Error(`${res.status}: ${(await res.json()).error}`);
  return (await res.json()).data; // { keywords: [{ id, keyword }] }; results land asynchronously
}
import os
import requests

API = "https://ai.synup.com/api/v1"
HEADERS = {"Authorization": f"Bearer {os.environ['SYNUP_API_KEY']}", "Content-Type": "application/json"}

def track_keywords(location_id, keywords):
    r = requests.post(f"{API}/seo/keywords", headers=HEADERS, timeout=30,
                      json={"locationId": location_id, "keywords": keywords, "gridSize": 5, "distanceKm": 1.5})
    r.raise_for_status()
    return r.json()["data"]  # keywords: [{ id, keyword }]

The response returns the keyword ids right away, but not their ranks. A scan runs in the background for each new keyword, so the first read after adding shows the keywords with no data yet. This is an asynchronous surface; treat rank data as something you poll on a schedule, never something you block a page render on.

Read the grid

GET /api/v1/seo/keywords is the location's rank view. It returns:

  • keywords, each with an avgRank and a monthly series.
  • months, the average rank trend across all tracked keywords, month by month.
  • grid, one entry per search point with its row, col, lat, lng and the rank at that point (plus excluded and water flags for points that do not count). This is the heatmap.
  • stats and statCards: the headline avgRank, top3Pct and top10Pct, plus the best and worst keyword, the biggest gainer and dropper, and how many keywords are not ranking or have no data yet.
  • center, the grid's origin, so you can draw it on a map.
curl "https://ai.synup.com/api/v1/seo/keywords?locationId=LOCATION_ID" \
  -H "Authorization: Bearer $SYNUP_API_KEY"

A location that ranks first at its own address but falls off a few points away has a real visibility problem, and the grid is the only place you see it. A single average would hide it.

Roll up a whole client

For a dashboard across locations, don't loop the overview. GET /api/v1/seo/rollup returns one row per location: avgRank, top3Pct, top10Pct, the bestKeyword and the location's tags, so you can sort a portfolio by who needs attention.

curl "https://ai.synup.com/api/v1/seo/rollup?clientId=CLIENT_ID" \
  -H "Authorization: Bearer $SYNUP_API_KEY"

Competitors, share of voice and citations

Three reads turn a rank into context:

  • GET /api/v1/seo/competitors lists the businesses winning the grid points around this location, each with its avgRank, top3Pct, gridPoints, rating and review count, and a per-keyword breakdown. The location itself is in the list flagged isYou: true, so you can show it in line.
  • GET /api/v1/seo/share-of-voice gives the comparison, per-keyword and performance-over-time views of how much of the local results a business owns versus its competitors.
  • GET /api/v1/seo/citation-index reports directory citation coverage as a percentage with listings and indexed counts, the listings foundation that ranking sits on.
  • GET /api/v1/seo/grid-point drills into a single grid point to see exactly who ranks there.

Managing the tracked set

DELETE /api/v1/seo/keywords stops tracking a keyword, and PATCH /api/v1/seo/config changes the grid configuration. Keep the tracked list to the keywords a business actually cares about. The 25-per-location ceiling is there because every keyword is a recurring scan, not a free lookup.

Next

Rank tracking is search visibility. The other half of being found now is AI: track AEO visibility measures how answer engines describe and recommend the business. For a single quick pull, the rankings recipe is the short version, and rank tracking is one of the features in the local marketing guide.