A Script That Checks Whether Email Sends Move Branded Search Volume

Published

A reader asked something I'd never actually checked: does an email send move branded search volume. Click rate tells you who clicked. It says nothing about who closed the email and searched the brand name in a new tab instead.

Why Click Data Alone Misses This

I wrote a small script to check for that second group using Search Console's own API, lined up against real send dates.

Open and click rates measure interaction with the email itself. Someone who reads a subject line, feels interested, and still doesn't click isn't necessarily uninterested.

Some portion of that group closes the email and searches the brand name directly before buying, especially on a higher-consideration purchase where verifying the brand feels worth the extra step.

That search happens entirely outside Klaviyo's own tracking. The only way to see it is to go looking in a completely different data source, which is what sent me toward the Search Console API instead of anything inside the email platform's own reporting.

The Search Console Query

Search Console's API exposes search analytics through a single query endpoint. The useful part for this check is filtering to branded queries and pulling daily volume, not aggregate totals, since a single total across the whole date range throws away exactly the day-by-day pattern this question depends on.

Filtering to branded terms means matching on the brand name itself plus common misspellings and the handful of query variants people actually type, like "brand reviews" or "brand login." Anything too broad pulls in unrelated search traffic and buries the signal.

The Script

The core of it is one API call with a date-range and a query filter, then matching the daily output against a list of send dates.

import requests

def get_branded_search_by_day(site_url, access_token, start_date, end_date, brand_terms):
    url = f"https://www.googleapis.com/webmasters/v3/sites/{site_url}/searchAnalytics/query"
    body = {
        "startDate": start_date,
        "endDate": end_date,
        "dimensions": ["date"],
        "dimensionFilterGroups": [{
            "filters": [
                {"dimension": "query", "operator": "contains", "expression": term}
                for term in brand_terms
            ]
        }],
        "rowLimit": 1000,
    }
    headers = {"Authorization": f"Bearer {access_token}"}
    resp = requests.post(url, json=body, headers=headers)
    rows = resp.json().get("rows", [])
    return {row["keys"][0]: row["impressions"] for row in rows}

That returns a plain dict of date to impression count for anything matching the brand terms. The second half is just aligning it against send dates:

def check_send_day_lift(daily_impressions, send_dates, window_days=1):
    send_day_values = []
    baseline_values = []
    for date, impressions in daily_impressions.items():
        is_near_send = any(
            abs((date_to_days(date) - date_to_days(send)) <= window_days)
            for send in send_dates
        )
        (send_day_values if is_near_send else baseline_values).append(impressions)
    return {
        "send_day_avg": sum(send_day_values) / len(send_day_values),
        "baseline_avg": sum(baseline_values) / len(baseline_values),
    }

date_to_days is a one-line helper converting a date string to a day-number for the subtraction, left out here since it's plumbing, not the actual technique.

What The Output Actually Showed

Running this against one client's last two months of sends and branded query data, send-day average impressions came in meaningfully above the non-send-day baseline.

That held up consistently enough across multiple sends that it wasn't just one lucky day skewing the average, which was the part I actually wanted to confirm before drawing any conclusion from it.

The effect was clearest on sends tied to a new product announcement. It was noticeably smaller on routine promotional sends the list had already seen the format of many times before.

That matches the intuition behind the whole check: a reason to verify the brand shows up more when there's something genuinely new to verify, and less when the send is just another instance of a familiar pattern.

The Limits Of What This Proves

A higher send-day average doesn't prove causation on its own, especially with a limited number of sends in the sample.

I've written before about why I don't trust an agent's own counting without checking it against real code. The same discipline applies here: a human reading a chart and wanting it to confirm a hypothesis is just as capable of seeing a pattern that isn't really there.

Before treating a bump like this as real, it's worth checking that no other campaign, press mention, or seasonal pattern explains the same window, and that the pattern holds across enough sends to rule out coincidence rather than taking the first positive result at face value.

Want help measuring what your sends actually do?

I build the scripts and direct API integrations that answer questions standard platform reporting can't.