SEO Analytics for a Multilingual Website via GSC API and BigQuery

SEO analytics for a multilingual website means collecting search data by language, country, URL, and query in a single warehouse. Teams use it to see organic traffic not only in the GSC interface, but also in custom reports where they can compare locales, pages, cannibalization, and long-tail queries. In real scenarios, account for API limits, data delays, URL normalization, and the fact that Search Console returns a limited set of aggregated data, not a complete raw log of every search event.
For a small website, it is enough to open Search Console and review queries manually. For a project with ten languages, hundreds of templates, and thousands of pages, that is no longer sufficient. You need to compare /uk/, /en/, /de/, countries, devices, pages, and queries in one place. This is where a custom pipeline through the GSC API, BigQuery, and scheduled loads helps. For a broader SEO infrastructure, it is useful to keep the material on SEO for AI Search and residential proxies nearby, because regional SERP checks and analytics often go together.
The main mistake in such a system is collecting data that is too aggregated. If countries, devices, or pages are mixed during export, it is impossible to restore the detail later. It is better to store raw slices in BigQuery and calculate final metrics in reports. This is especially visible for local directions: a page may perform well in one country and fail in another, as in local SEO for Google Maps tasks.
In multilingual SEO, data quickly starts to resemble price or catalog monitoring tasks: many URLs, many repeated templates, and many small changes. That is why the architecture should be closer to a data pipeline than to a manual CSV export. A similar approach to regular collection and normalization is described in the article on price monitoring at scale.
Which Metrics and Slices a Multilingual Website Needs
A multilingual website needs not only clicks and impressions, but also a connection between metrics and language, country, page, query, and device. Without this combination, the team sees overall growth but does not understand which locale produced it or where pages compete with each other.
The minimum set of metrics from the GSC API is clicks, impressions, ctr, and position. The minimum set of dimensions is date, page, query, country, and device. Discover, Google News, or video require a separate type, but for most SEO reports, web remains the baseline.
| Slice | Why it is needed | Example question |
|---|---|---|
| language | comparing locales | why /uk/ is growing while /en/ is flat |
| country | regional performance | where a page has impressions without clicks |
| page | template and cannibalization analysis | which URLs compete for one query |
| query | finding long-tail opportunities | which phrases have positions 5-20 |
| device | UX and SERP differences | where mobile CTR drops more sharply |
| date | seasonality and update effects | when the trend changed after a release |
Create a separate language field. Do not rely on analysts always recognizing language from the URL. For folder-based websites, this can be a regular expression with /uk/, /en/, or /fr/. For subdomains, you need a host -> language mapping table. For separate domains, such as example.de and example.fr, you also need market.
After defining the required slices, you can move on to API requests and segmentation rules.
How to Get Data via the Google Search Console API
The GSC API returns data for a selected date range, dimensions, and filters. A request to searchAnalytics.query should include startDate, endDate, a set of dimensions, optional filters, rowLimit, startRow, and the dataState parameter.
According to the official documentation, rowLimit supports up to 25,000 rows, and startRow is used for pagination. But on large websites, simple pagination does not fully solve the problem: the API may not return every possible combination if the number of unique dimension combinations is too high. That is why the export should be segmented. For example, request /uk/, /en/, and /de/ separately, then additionally split large locales by blog, categories, products, or URL templates.
The basic export process:
- create a list of language segments and GSC properties
- for each date, request data with the
page,query,country, anddevicedimensions - use a
page contains /uk/filter or another segment pattern - fetch pages in batches through
rowLimitandstartRow - write raw JSON or normalized rows to a staging table
- process duplicates and load the data into a production table
There is a separate nuance with the dataState parameter. final gives finalized data, but the last few days may still be unavailable. all lets you get fresh data, but it may change after Google processing. A good practice is to load fresh data daily for an operational dashboard and, in parallel, overwrite previous dates after they are finalized.

In practice, keep the loader small and predictable. Store the request parameters next to every batch: property, segment, date range, dimensions, filters, rowLimit, startRow, and load time. When a report changes, this metadata helps separate a real ranking shift from a changed extraction rule or a repeated import.
How to Store and Normalize Data in BigQuery
BigQuery is better used not as one large table "for everything", but as several layers: raw, staging, and marts. The raw layer stores the API response or a structure as close to it as possible. Staging brings types, URLs, and language to a unified format. Marts contains ready-made tables for Looker Studio, BI, or internal reports.
A practical schema may look like this:
| Table | Purpose | Key fields |
|---|---|---|
gsc_raw_daily | raw API export | export_date, property, payload, loaded_at |
gsc_rows_staging | expanded rows | date, page, query, country, device, clicks |
dim_url | normalized URLs | page, normalized_path, language, template |
gsc_daily_fact | final fact table | date, url_id, query, country, device, metrics |
seo_language_mart | language-level reports | language, clicks, impressions, ctr, position |
For URL normalization, create separate fields: host, path, language, canonical_group, and template_type. Do not parse the URL in the BI report every time. It is slow, unstable, and increases the risk of different rules being used in different dashboards.
To work with fresh and finalized data, use MERGE instead of simply appending rows. The key can be a combination of date, page, query, country, device, and search_type. If the row already exists, update the metrics and data_state. If it does not exist, insert a new one. This way, history does not swell with duplicates after repeated loads.

Once the data has been brought into a unified model, you can look for differences between locales and overlaps in queries.
How to Compare Language Versions and Find Cannibalization
Language versions should be compared not only by total traffic. It is important to see which pages perform the same role in different locales and to check separately for cannibalization within one language and cases where the wrong language version appears in search results.
For this, the proposed data model uses an internal canonical_group field: a conditional group of pages of the same type. For example, /uk/blog/proxy-guide, /en/blog/proxy-guide, and /de/blog/proxy-guide can belong to one group if they are localizations of the same article. Then the report shows which language has impressions, where CTR is lower, where the position dropped after an update, and where title or intent work is needed.
Cannibalization and related localization issues should be checked separately:
- cannibalization between pages in one language, when two URLs rank for one query
- localization issues in search results, when users from one country see the wrong language version
- cannibalization between templates, when a category and an article compete for one informational query
- cannibalization between an old and a new URL version after a migration or structure change
A separate useful query_gap report shows query gaps where a page has many impressions, a position of 5-20, and low CTR. These are often the queries worth adding to H2s, FAQs, or paragraph updates. For a multilingual website, this report should be built separately by language, because Ukrainian long-tail does not translate mechanically into English.

After that, technical automation works with priorities rather than scattered URLs without context.
How to Automate Hreflang Checks and Reports
SEO analytics for a multilingual website should combine GSC data with a technical hreflang check. Search Console will show clicks and impressions, but it will not explain every reason why users see the wrong locale. For that, you need a separate crawler or a regular audit based on the logic of the Afina API.
A minimum hreflang check should find:
- missing reciprocal links between language versions
- URLs that return 3xx, 4xx, or a canonical to another page
- language codes that are not present in the locale map
- pages without self-referencing hreflang
- different
canonical_groupvalues for localizations of the same article
After that, the report should be joined with GSC. If a page has a technical hreflang error and also receives impressions in the wrong country, it is a priority task. If the error is on a page without impressions, it can be placed lower in the task list.
Afina can complement such a system during regional manual SERP checks, work with controlled browser profiles, and automated QA scenarios when the team needs to check the SERP in a specific environment. The core of SEO analytics still remains the GSC API, BigQuery, and a clear data model.
For the workflow, a simple schedule is enough: daily GSC export, overwriting finalized data with a delay, weekly hreflang audit, and a report on queries with positions 5-20. This approach gives the SEO team not just a dashboard, but a specific list of optimization tasks.
DownloadFAQ — Frequently Asked Questions
What is SEO analytics for a multilingual website?
SEO analytics for a multilingual website involves collecting and comparing search data by language, country, URL, and query. It shows which locales are growing and where pages are losing visibility.
What data can be obtained through the GSC API?
The GSC API returns clicks, impressions, CTR, and average position for selected dimensions. Date, page, query, country, and device are used most often.
Why store GSC data in BigQuery?
BigQuery provides long history, a custom data model, and flexible reports. It removes dependence on manual exports and Search Console interface limitations.
How should URLs be normalized for different languages?
Split the URL into host, path, language, template, and canonical group. For language folders, language can be extracted from patterns such as /uk/ or /en/.
Why can fresh GSC data differ from finalized data?
Fresh data has not yet gone through full Google processing. After a few days, clicks, impressions, and positions may change, so finalized data should be overwritten separately.
How do you find SEO cannibalization between language versions?
Group localized pages into a canonical group and compare queries, countries, and URLs. Cannibalization is visible when several pages compete for one intent.
Is one report enough for all languages?
No, one general report hides local problems. Each language needs separate slices by country, query, page, and device.
