ds@mcpanalytics.aiEmail your data and your question and get an answer about your own data, with the method shown.Email your data and question. Get an answer.

Cross-Platform Content Performance: Merge Search and Engagement Data

By MCP Analytics Team | Published , updated

Search Console tells you which pages attract clicks. GA4 tells you which pages keep visitors engaged. Neither one alone tells you which content is actually working. Cross-Platform Content Performance merges both data sources into a single quality matrix, so you can see which pages win on search AND engagement, and which ones need fixing. Upload two CSVs to view search and engagement data together.

Worked Example: Four Made-Up Pages

Question. Which pages have both strong search use and strong visitor use in a small set?

Inputs. Simulated data, seed 17. The script makes four made-up page rows with clicks, impressions, bounce rate and time on page in seconds. Click-through rate (CTR) is clicks divided by impressions. No real site data is used.

Method. Scale each measure from 0 to 1 using the lowest and highest value in this set. The search score is the mean of scaled clicks and scaled CTR. The visitor-use score is the mean of scaled (1 minus bounce rate) and scaled time on page. A score is high when it is at or above the median of that score. If a row lacks any input, the example leaves it out. This is one simple rule for the example. It is not the report's own score rule.

import numpy as np

SEED = 17
rng = np.random.default_rng(SEED)
names = ["Guide A", "Guide B", "Guide C", "Guide D"]
clicks = rng.integers(10, 101, size=4)
impressions = rng.integers(100, 1001, size=4)
bounce = rng.uniform(0.20, 0.81, size=4)
ctr = clicks / impressions
duration = rng.integers(30, 241, size=4)
inputs = np.column_stack((clicks, impressions, bounce, duration))
valid = np.isfinite(inputs).all(axis=1)
names = np.asarray(names)[valid].tolist()
clicks, impressions, bounce, duration = (
    column[valid] for column in (clicks, impressions, bounce, duration)
)
ctr = clicks / impressions

# Map each metric to 0..1 using (value - minimum) / (maximum - minimum).
# Reverse bounce first, so lower bounce gives a higher score.
def scale(values):
    values = np.asarray(values, dtype=float)
    span = values.max() - values.min()
    return np.zeros_like(values) if span == 0 else (values - values.min()) / span

search = (scale(clicks) + scale(ctr)) / 2
engagement = (scale(1 - bounce) + scale(duration)) / 2
search_cut = float(np.median(search))
engagement_cut = float(np.median(engagement))

print(f"Simulated data, seed {SEED}")
print("Rule: search=(scaled clicks + scaled CTR)/2; "
      "engagement=(scaled (1-bounce) + scaled duration)/2.")
print("Scale each measure to 0..1 by its min and max in this set. "
      "Cut each score at its median.")
print("All four rows have values; "
      "if a row lacks any input, this example leaves it out.")
print(f"Median cuts: search={search_cut:.3f}, engagement={engagement_cut:.3f}")
print("Page | clicks | impressions | CTR | bounce | duration_s | "
      "search | engagement | group | next step")
for i, name in enumerate(names):
    high_s = search[i] >= search_cut
    high_e = engagement[i] >= engagement_cut
    if high_s and high_e:
        group, action = "star", "Study its approach; keep an eye on it."
    elif not high_s and high_e:
        group = "SEO opportunity"
        action = "Review ways to improve search visibility."
    elif high_s and not high_e:
        group, action = "optimize", "Review whether the page meets visitor needs."
    else:
        group = "underperformer"
        action = "Review this page before choosing a change."
    print(f"{name} | {clicks[i]} | {impressions[i]} | {ctr[i]:.3f} | "
          f"{bounce[i]:.3f} | {duration[i]} | {search[i]:.3f} | "
          f"{engagement[i]:.3f} | {group} | {action}")
print("Limit: these scores use only four invented rows "
      "and do not show that a suggested change will work.")

Output of that script:

Simulated data, seed 17
Rule: search=(scaled clicks + scaled CTR)/2; engagement=(scaled (1-bounce) + scaled duration)/2.
Scale each measure to 0..1 by its min and max in this set. Cut each score at its median.
All four rows have values; if a row lacks any input, this example leaves it out.
Median cuts: search=0.547, engagement=0.403
Page | clicks | impressions | CTR | bounce | duration_s | search | engagement | group | next step
Guide A | 77 | 512 | 0.150 | 0.331 | 49 | 0.933 | 0.553 | star | Study its approach; keep an eye on it.
Guide B | 86 | 602 | 0.143 | 0.435 | 185 | 0.970 | 0.784 | star | Study its approach; keep an eye on it.
Guide C | 19 | 786 | 0.024 | 0.461 | 40 | 0.000 | 0.254 | underperformer | Review this page before choosing a change.
Guide D | 24 | 431 | 0.056 | 0.573 | 33 | 0.162 | 0.000 | underperformer | Review this page before choosing a change.
Limit: these scores use only four invented rows and do not show that a suggested change will work.

Result. Guides A and B are stars. Guides C and D are underperformers. No page lands in SEO opportunity or optimize in this run. Guide A has 77 clicks on 512 impressions, so its CTR is 0.150.

What the data builds in. Every number is drawn at random, so no page is built to be better than another. With four rows and a median cut, and no ties, exactly two rows are high on each score, so the labels carry no information here. They show how the grouping works, not which page is better. Scaling by the lowest and highest row on four rows is also fragile, because the largest and smallest rows always score 1 and 0. A different seed can give a different mix of labels.

Limit. Four invented rows do not show that a suggested change will work. The next steps in the output are ideas to review.

What Is Cross-Platform Content Performance Analysis?

Most content teams look at search data and engagement data separately. They check Search Console for impressions and clicks, then flip to GA4 for bounce rates and time on page. The problem is that these two views tell completely different stories. A page with a high bounce rate may not meet what some visitors sought. The meaning depends on the page goal. Conversely, a deep guide might have strong engagement metrics (a long average read time, a low bounce rate) but appear on page three of Google where few people find it.

Cross-Platform Content Performance analysis solves this by joining your Google Search Console export with your GA4 engagement export on a shared URL field. The result is a unified view where every page has both its search metrics (clicks, impressions, CTR, average position) and its engagement metrics (pageviews, bounce rate, session duration, engagement rate). The report groups pages by search and engagement scores. The worked example above shows one way to set the scores and groups.

This is the analysis that answers the question every content strategist asks: "Which of my pages are actually working end-to-end?" Not just attracting clicks, not just keeping visitors, but doing both. The quality matrix puts both views on one chart, which helps you decide where to look first.

When to Use Cross-Platform Content Analysis

Run this analysis whenever you need to prioritize content work. If you have a library of blog posts, landing pages, or documentation and limited time to improve them, this report can help you sort pages for review. Its groups depend on the data and the set cut points. The stars quadrant shows your best content, protect and replicate it. The SEO opportunities quadrant reveals hidden gems that engage visitors but lack search visibility. These are your fastest wins because the content is already good, it just needs better distribution. The optimization targets quadrant flags pages pulling search traffic that bounces. These need content improvements before the search traffic is wasted.

Content audits are the most common trigger. When a marketing team reviews their content portfolio quarterly, this analysis replaces manual spreadsheet work with a single report. Instead of cross-referencing GSC and GA4 tabs by hand, you upload two exports and get the matrix. The report even identifies content types (articles vs. landing pages vs. docs) and compares performance across types, so you can see if your blog strategy is outperforming your product pages or vice versa.

This analysis is also valuable after major site changes: a redesign, a URL migration, or a content reorganization. By running the cross-platform analysis before and after, you can see which pages maintained their combined search-plus-engagement performance and which ones broke.

What Data Do You Need?

This is a multi-dataset module. You need two separate CSV files:

Search dataset (from Google Search Console): Export your page-level performance data with columns for page URL, clicks, impressions, CTR, and average position. Export a CSV from each tool. The key columns are page, clicks, impressions, ctr, and position.

Engagement dataset (from GA4): Export your page-level engagement data with columns for page path, screen page views, total users, sessions, bounce rate, average session duration, and engagement rate. Export a CSV from each tool. The key columns are pagePath, screenPageViews, totalUsers, sessions, bounceRate, averageSessionDuration, and engagementRate.

The module matches pages across the two datasets using URL matching. You can set a domain parameter to strip the domain prefix from GSC URLs (which include the full URL) so they match GA4 paths (which are typically just the path). Additional parameters let you set minimum thresholds: min_clicks (default 5), min_impressions (default 50), and min_pageviews (default 10) to filter out low-traffic pages that would add noise to the analysis. A content_type_regex parameter lets you classify pages into content types based on URL patterns.

How to Read the Report

The report is organized into sections, each revealing a different facet of your content performance:

Quality Matrix. This is the centerpiece. Pages are plotted on a two-dimensional chart with search performance on one axis and engagement performance on the other. Each dot is a page, and pages cluster into four quadrants. Stars (top-right) are your best performers, high search traffic and high engagement. SEO opportunities (bottom-right) engage visitors well but lack search visibility. Optimization targets (top-left) attract search clicks but fail to engage. Underperformers (bottom-left) need either a complete rework or deprecation.

Stars. A detailed table of your top-performing content: the pages that score well on both search and engagement. These are your templates for success. Study what they have in common: topic depth, format, keyword targeting, page structure. Then replicate those patterns across your weaker content.

SEO Opportunities. Pages with strong engagement signals but poor search visibility. These are often long-form guides, case studies, or niche content that visitors love once they find it, but Google is not sending traffic yet. Action items: improve title tags, add internal links, build backlinks, and target related keywords.

Optimize Content. Pages that attract search clicks but fail to retain visitors. High bounce rate, low session duration, or low engagement rate despite decent search rankings. The content is not meeting the promise of the search snippet. Action items: improve content quality, match search intent better, fix page speed issues, add internal navigation.

Content Types. If you set the content_type_regex parameter, this section breaks down performance by content category, articles vs. tutorials vs. product pages, for example. You can compare how each content format does on search and engagement together.

Unmatched Pages. Pages that appear in one dataset but not the other. GSC-only pages might have URL mismatches or might not be tracked in GA4. GA4-only pages might not be indexed in Google. This section helps you catch data hygiene issues.

TL;DR. AI-generated executive summary with the key findings and top action items.

When to Use Something Else

If you only have GA4 data and want to analyze page engagement without the search dimension, use the GA4 Page Engagement module instead. It goes deeper on engagement metrics alone (bounce analysis, duration patterns, content type breakdowns) without requiring a GSC export.

If you only have Search Console data and want to find SEO opportunities without the engagement overlay, use the GSC Quick Wins module. It identifies pages ranking positions 4-20 with high impressions but low CTR: the classic SEO quick wins that do not require engagement data.

If you want to track how search rankings change over time rather than analyze content quality, use the Ranking Changes module. And if you are running SEO title experiments and need to compare control vs. treatment periods, use the Title A/B Test module.

The R Code Behind the Analysis

Every report names its method and includes the R code that produced it, so you can reproduce, audit and cite it. The method code is fixed, not rewritten for each run, so the same data gives the same analysis every time.

The analysis uses dplyr for joining the two datasets on URL, computing composite quality scores, and classifying pages into quadrants. The quality matrix is rendered with ggplot2. Content type classification uses regex pattern matching on URLs. All thresholds and parameters are visible in the code tab of your report.

Your turn

Bring your own data and the question you actually need answered.

MCP AnalyticsData science by email Send us your data and your question, and we will reply. ds@mcpanalytics.ai