GuideGuides/Retrieve GBP analytics
How do I retrieve Google Business Profile analytics through an API?

How to Retrieve Google Business Profile Analytics via API

Pull Google Business Profile analytics through an API, portfolio-wide and per location, with views, calls, direction requests and a maps-versus-search split.

At a glance

What this guide accomplishes
Read profile performance across a client and per location, with period comparisons and a per-location leaderboard.
APIs used
Reference
Profile Analytics
Prerequisites
  • An API key
  • A clientId, and a connected profile that is reporting data
Typical use cases
  • Building a performance dashboard for many locations
  • Reporting monthly numbers to a business owner
  • Ranking locations against each other

Reach for the portfolio call first and the per-location call second. That order matters here more than usual, because the wide call is solid and the narrow one has a rough edge you need to know about before you build a page on it.

The two analytics calls

GET /api/v1/analytics/summary rolls a client's locations into one response: the headline metrics, a period comparison, a maps-versus-search split, and a per-location leaderboard. It is enough for both the overview and the per-location table on a dashboard.

GET /api/v1/analytics is the single-location deep dive. It adds the Google search keywords that surfaced the profile, which the summary does not carry.

curl "https://ai.synup.com/api/v1/analytics/summary?clientId=CLIENT_ID&comparison=previous_period&granularity=weekly" \
  -H "Authorization: Bearer $SYNUP_API_KEY"
const API = "https://ai.synup.com/api/v1";
const headers = { Authorization: `Bearer ${process.env.SYNUP_API_KEY}` };

async function analyticsSummary(clientId, opts = {}) {
  const params = new URLSearchParams({ clientId, comparison: "previous_period", granularity: "weekly", ...opts });
  const res = await fetch(`${API}/analytics/summary?${params}`, { headers });
  if (!res.ok) throw new Error(`${res.status}: ${(await res.json()).error}`);
  const { data } = await res.json();
  return data;
}
import os
import requests

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

def analytics_summary(client_id, **opts):
    params = {"clientId": client_id, "comparison": "previous_period", "granularity": "weekly", **opts}
    r = requests.get(f"{API}/analytics/summary", headers=HEADERS, params=params, timeout=30)
    r.raise_for_status()
    return r.json()["data"]

What the summary gives you

Each headline metric is a block with the same shape, so one renderer handles all of them. The metrics are profileViews, allActions, websiteVisits, phoneCalls, directionRequests and buttonClicks, each carrying a total, a previousTotal for the delta, a labels array for the x-axis, and a bySource breakdown (Google today). Alongside them:

  • impressionSplit breaks impressions into maps versus search and desktop versus mobile. This is usually the most interesting chart to an owner, because it is the one they cannot see anywhere else.
  • coverage reports withData against total, so you can show "18 of 92 locations reporting" honestly rather than implying the silent ones are at zero.
  • leaderboard is a paged table, one row per location with its own metrics, a deltaPct and a spark series. This is your per-location table without a second call.
  • latestDataDay and syncedAt tell you how fresh the numbers are. Google reports on a lag, so surface the latest data day rather than implying the figures are from today.

Freshness and empty locations

Analytics only exist where a profile is connected and Google has started reporting, so a new location shows nothing for a while. That is why coverage and latestDataDay exist: build the page to say "not reporting yet" for a location in coverage.total but not in coverage.withData, rather than drawing a flat line at zero.

The neighbouring analytics reads

Two more analytics calls share the same client and location ids:

Next

Profile analytics tells you how the listing performs. To see why it ranks where it does, track local rankings covers the geo-grid. For a single quick pull rather than the whole surface, the retrieve profile analytics recipe is the short version.