---
title: "Hands-on: local performance data with Search Console, GBP…"
description: "What you are building A simple monthly local dashboard that combines three sources: 1. Search Console — organic clicks and impressions for location and…"
url: https://optimizeall.com/learn/local-seo-and-google-business-profile/local-performance-data-with-python-and-sheets
updated: 2026-10-05
---

Local SEO & Google Business Profile · Local rank tracking, reporting and audits · lesson 16 of 17 · 14 min

# Hands-on: local performance data with Search Console, GBP data, Python and Sheets

## What you are building

A simple monthly local dashboard that combines three sources:

1. **Search Console** — organic clicks and impressions for location and service pages, and "local" queries (city names, "near me").
2. **Business Profile performance** — calls, direction requests, website clicks and impressions from Search and Maps.
3. **GA4** — sessions and conversions from the UTM-tagged profile link.

You can do all of it by exporting CSVs into Google Sheets. Python makes it repeatable for multi-location businesses.

## Part 1: Search Console in Sheets (no code)

In Search Console → Performance → Search results, filter **Page** by "URLs containing" `/locations/` (or your pattern) and export. For local queries, use **Query** → "Custom (regex)" with a pattern such as:

```text
near me|nearby|lahore|gulberg|dha|karachi|clifton
```

Search Console filters use RE2 regular expression syntax. Export both views monthly into tabs `GSC_pages` and `GSC_queries`, then summarise with a pivot table or:

```text
=QUERY(GSC_pages!A:E, "select A, sum(B), sum(C) where A contains '/locations/' group by A order by sum(B) desc", 1)
```

## Part 2: Search Console API in Python (repeatable)

Create a Google Cloud service account, enable the Search Console API, and add the service account email as a user on the property. Keep the key file path in an environment variable.

```python
import os
import pandas as pd
from google.oauth2 import service_account
from googleapiclient.discovery import build
from googleapiclient.errors import HttpError

SITE = os.environ.get("GSC_SITE", "sc-domain:example.com")
creds = service_account.Credentials.from_service_account_file(
    os.environ["GSC_SERVICE_ACCOUNT_JSON"],
    scopes=["https://www.googleapis.com/auth/webmasters.readonly"])
gsc = build("searchconsole", "v1", credentials=creds)

def query(body: dict) -> pd.DataFrame:
    try:
        resp = gsc.searchanalytics().query(siteUrl=SITE, body=body).execute()
    except HttpError as e:
        raise SystemExit(f"Search Console API error: {e}")
    rows = resp.get("rows", [])
    dims = body["dimensions"]
    return pd.DataFrame([{**dict(zip(dims, r["keys"])), "clicks": r["clicks"],
                          "impressions": r["impressions"], "position": r["position"]} for r in rows])

location_pages = query({
    "startDate": "2026-08-01", "endDate": "2026-08-31",
    "dimensions": ["page"],
    "dimensionFilterGroups": [{"filters": [
        {"dimension": "page", "operator": "contains", "expression": "/locations/"}]}],
    "rowLimit": 25000,
})
local_queries = query({
    "startDate": "2026-08-01", "endDate": "2026-08-31",
    "dimensions": ["query", "page"],
    "dimensionFilterGroups": [{"filters": [
        {"dimension": "query", "operator": "includingRegex", "expression": "near me|lahore|karachi"}]}],
    "rowLimit": 25000,
})
location_pages.to_csv("gsc_location_pages_2026-08.csv", index=False)
local_queries.to_csv("gsc_local_queries_2026-08.csv", index=False)
print(location_pages.sort_values("clicks", ascending=False).head(10))
```

Note: Search Console anonymises some rare queries, so query-level totals are lower than page-level totals. Compare like with like.

## Part 3: Business Profile performance data

**Without code:** in the Business Profile, open **Performance**, choose the date range and export or record the monthly figures (calls, directions, website clicks, bookings, views on Search/Maps). For many locations, the Business Profile manager supports multi-location performance downloads.

**With the API (approved developers):** Google's **Business Profile Performance API** returns daily metrics per location. Access requires applying for Business Profile API access, OAuth consent by an account that manages the location, and the `https://www.googleapis.com/auth/business.manage` scope. A minimal request using an authorised session:

```python
from google.auth.transport.requests import AuthorizedSession
from google_auth_oauthlib.flow import InstalledAppFlow

flow = InstalledAppFlow.from_client_secrets_file(
    "client_secret.json", scopes=["https://www.googleapis.com/auth/business.manage"])
creds = flow.run_local_server(port=0)          # the location's owner/manager signs in
session = AuthorizedSession(creds)

LOCATION_ID = "1234567890"                      # from the Business Profile / API
url = (f"https://businessprofileperformance.googleapis.com/v1/locations/{LOCATION_ID}"
       ":fetchMultiDailyMetricsTimeSeries")
params = [("dailyMetrics", m) for m in ("CALL_CLICKS", "WEBSITE_CLICKS", "BUSINESS_DIRECTION_REQUESTS")]
params += [("dailyRange.startDate.year", 2026), ("dailyRange.startDate.month", 8), ("dailyRange.startDate.day", 1),
           ("dailyRange.endDate.year", 2026), ("dailyRange.endDate.month", 8), ("dailyRange.endDate.day", 31)]
resp = session.get(url, params=params, timeout=30)
resp.raise_for_status()
print(resp.json())                               # nested time series per metric
```

Metric names and response shapes can change — check the current Business Profile Performance API reference before relying on this in production, and store OAuth tokens securely.

## Part 4: GA4

In GA4, build an exploration with **Session campaign** = `gbp-listing` (from your UTM convention) and dimensions *Landing page* and *Session source/medium*, metrics *Sessions*, *Key events* (conversions). Export monthly into a `GA4_gbp` tab.

## Part 5: The monthly dashboard tab

```text
Location | GSC clicks (location page) | GSC impressions | Local-query clicks | GBP calls | GBP directions
GBP website clicks | GA4 gbp sessions | GA4 key events | Reviews (new) | Avg rating | Notes
```

Fill it with `SUMIF`/`XLOOKUP` from the source tabs, add month-on-month and year-on-year columns, and chart the three numbers the owner cares about most (usually calls, directions and conversions). If you use Looker Studio, connect it to the Sheet rather than rebuilding logic in two places.

## Worked example (illustrative)

A 4-branch optician chain in Lahore and Karachi runs the Python script monthly and pastes GBP performance exports into the Sheet. The dashboard shows Gulberg's location page gaining impressions but its GBP calls flat, while Clifton's calls rise after a category fix. Investigation shows Gulberg's profile links to the home page rather than its location page, and the phone number on the page is the head office. Fixing both aligns the page, the profile and the calls — and the next month's dashboard shows it.

## Common mistakes

- Adding query-level and page-level Search Console totals together.
- Hard-coding API keys or OAuth secrets in scripts.
- Reporting impressions without the actions and conversions that follow.
- Building the same logic separately in Sheets, Looker Studio and slides.

## Video lecture: Hands-on: local performance data with Search Console, GBP data, Python and Sheets

Lecture coming soon · 13 chapters · about 8 minutes. Read the full transcript below.

1. Hands-on: local performance data
2. Why combine
3. Start simple
4. Search Console API
5. GBP performance
6. GBP Performance API call
7. Example 1 (simple, illustrative)
8. Example 2 (illustrative)
9. GA4 + the dashboard tab
10. Design for the owner
11. Watch me do it: the first local dashboard
12. Common mistakes
13. Recap

## Lecture transcript

### Hands-on: local performance data

Here's a common monthly meeting. The SEO agency shows rankings. The profile shows calls. Analytics shows sessions. And nobody can say whether the business is actually doing better. In this lecture you'll build one simple local dashboard that combines Search Console, Business Profile performance and GA4, first with spreadsheet exports, then with a little Python so it runs the same way every month.

### Why combine

Why combine the data? Because each source shows only part of the journey. Search Console shows how your location and service pages perform in organic results. Business Profile performance shows calls, direction requests and website clicks from your profile. GA4, with UTM tagged links, shows what those visitors did on your site. Together, they tell a story an owner can act on.

### Start simple

Here's the key idea: start simple, then automate. Think of it like cooking. You make a recipe by hand a few times before you write it down for others. For one location, exports into a Google Sheet are enough. In Search Console, filter pages containing slash locations, and filter queries with a custom regex, like near me or your city and neighbourhood names. Export both monthly into tabs, and summarise with a pivot table or a QUERY formula.

### Search Console API

Then automate with the Search Console API. The script in the lesson uses a service account whose key path lives in an environment variable, builds the Search Console service, and defines one helper that runs a query and returns a table. It runs two queries: location pages with a page contains filter, and local queries with an including regex filter on the query dimension. It saves both as CSVs, ready for the dashboard. One caution: Search Console anonymises some rare queries, so query level totals are lower than page totals.

### GBP performance

Next, Business Profile performance. Without code, open Performance in your profile, choose the month, and record calls, direction requests, website clicks, bookings and views on Search and Maps. For many locations, the Business Profile manager supports multi location downloads. With code, Google's Business Profile Performance API returns daily metrics per location. But access requires an application and approval, OAuth sign in by someone who manages the location, and the business dot manage scope.

### GBP Performance API call

The lesson's API example uses an authorised session. A profile manager signs in once through an OAuth flow. Then the script requests the fetch multi daily metrics time series endpoint for a location ID, asking for call clicks, website clicks and direction requests over a date range. It returns a nested time series per metric. Metric names and response shapes can change, so check the current API reference before relying on it in production, and store tokens securely.

### Example 1 (simple, illustrative)

Worked example one, simple. A single location physio clinic records, each month, Search Console clicks to its location page, profile calls and direction requests, and GA4 sessions from the gbp listing campaign with booking conversions. After three months the sheet shows calls rising, but website bookings from the profile flat. They discover the profile's booking link goes to a generic contact form. Changing it to the booking page lifts profile driven bookings the next month.

### Example 2 (illustrative)

Worked example two, realistic and illustrative. An optician chain with four branches in Lahore and Karachi runs the Python script monthly and pastes profile exports into the sheet. The dashboard shows Gulberg's location page gaining impressions, but its profile calls flat, while Clifton's calls rise after a category fix. Investigating, they find Gulberg's profile links to the home page instead of its location page, and the page lists the head office phone. Fixing both aligns page, profile and calls, and the next month's dashboard shows it.

### GA4 + the dashboard tab

GA4 completes the picture. Build an exploration where session campaign equals gbp listing, from your UTM convention, with landing page and source dimensions, and sessions and key events as metrics. Export monthly. Then the dashboard tab: one row per location with Search Console clicks and impressions, local query clicks, profile calls, directions and website clicks, GA4 sessions and key events, new reviews and average rating, and notes. Add month on month and year on year columns, and chart the three numbers the owner cares about most.

### Design for the owner

Let's pause and design the dashboard for a real owner. A restaurant owner in Lahore doesn't want forty columns. Ask them which three numbers would change what they do next week. Usually it's calls or reservations, direction requests, and online orders or bookings from the profile. Put those three at the top as simple charts with last month and the same month last year. Put everything else, impressions, positions, query tables, on a second tab for you. A dashboard is for decisions, not for proving how much data you can collect.

### Watch me do it: the first local dashboard

Watch me do it. I'll build the first monthly dashboard for a four branch optician chain. First, I set the Search Console property and the service account key path as environment variables. Next, I edit the script's two queries. Location pages, with the page filter contains slash locations. Local queries, with an including regex filter: near me, lahore, karachi, gulberg, clifton. Then I run it. Two CSV files appear, and the script prints the top ten location pages by clicks. I import both into the Sheet, into the GSC tabs. Now the Business Profile data. This chain doesn't have API access, so I use the multi location performance download in the Business Profile manager for last month, and paste it into a GBP tab. Then GA4. I open my saved exploration filtered to the gbp listing campaign, with landing page and key events, and export it. Now the dashboard tab. One row per branch. SUMIF pulls location page clicks, the local query clicks, profile calls and directions, GA4 sessions and key events. I add month on month columns. Immediately, Gulberg stands out: impressions up, calls flat. That's the first observation. The second: Clifton's calls rose after last month's category fix. Finally, I chart calls, directions and bookings for the owner at the top.

### Common mistakes

Common mistakes. Adding query level and page level Search Console totals together. Hard coding API keys or OAuth secrets in scripts. Reporting impressions without the actions and conversions that follow. And building the same logic separately in Sheets, Looker Studio and slides. If you use Looker Studio, connect it to the sheet, so the logic lives in one place.

### Recap

Recap. Combine Search Console, Business Profile performance and GA4 for a complete local picture. Start with exports and regex filters in Sheets, then automate with the Search Console API. Use the Business Profile Performance API only with approved access and proper OAuth. Keep secrets out of code, compare like with like, and chart what owners care about. Try this now. Export last month's location page data from Search Console and your profile's calls and directions, and build the first row of your dashboard.

## Key takeaways

- Combine Search Console, Business Profile performance and GA4 (UTM-tagged) data for a complete local picture.
- Sheets exports and regex filters are enough for one location; the Search Console API makes it repeatable at scale.
- The Business Profile Performance API needs approved access, OAuth by a profile manager and the business.manage scope.
- Keep secrets in environment variables, compare like with like, and chart the actions owners care about.

## Try it

Build the monthly dashboard for one business: export location-page and local-query data from Search Console, record GBP performance and GA4 gbp-listing sessions, and write two observations with actions.

- [Previous: Local rank tracking and reporting](https://optimizeall.com/learn/local-seo-and-google-business-profile/local-rank-tracking-and-reporting)
- [Next: Local SEO audit and 90-day action plan](https://optimizeall.com/learn/local-seo-and-google-business-profile/local-seo-audit-and-action-plan)
- [All lessons of Local SEO & Google Business Profile](https://optimizeall.com/learn/local-seo-and-google-business-profile)
