Pre-Black Friday: 20% off, for as long as you stayEARLYBFon Solo and AgencyEnds Nov 28Start free

How-to guide

How to measure AI's effect on organic traffic and lead gen (Search Console plus a people API)

Pull your own query data, separate AI referrals from search, and turn the accounts you can see into reachable buyers.

Get your free API key →Free to start. No credit card. 1,000 records to spend whenever you like.

TL;DR

  • You measure your own numbers: impressions versus clicks per query, plus sessions whose referrer is an AI assistant. No third-party study needed.
  • It takes about 45 minutes and spends about 25 LeadOcean records. Sizing the audience is free.
  • Output: a table of queries losing clicks, a count of AI-referred sessions, and a contact roster for the companies that still reach you.

Prerequisites

  • Search Console access to your site and a Google Cloud OAuth token with the read-only Search Console scope.
  • Python 3.10 or newer with requests.
  • Web analytics that exports sessions with a referrer hostname and a company domain (a form fill or a reverse-IP field).
  • A LeadOcean key in $LEADOCEAN_API_KEY. The Free plan covers this: 1,000 records for life, no card, 5 requests a second.

Step 1: Find the queries where clicks fell but impressions did not

A query that keeps its impressions and loses clicks is the signal that an answer box or an assistant is satisfying the reader before the click. Search Console reports both. The Search Analytics endpoint takes a date range, dimensions and a row limit of 1 to 25,000, and needs the webmasters.readonly scope (Google, October 2026).

python
import os, requests
from urllib.parse import quote
SITE = quote("sc-domain:example.com", safe="")  # the site URL is URL-encoded in the path
TOKEN = os.environ["GSC_TOKEN"]  # OAuth token, read-only scope

def pull(start, end):
    r = requests.post(
        f"https://www.googleapis.com/webmasters/v3/sites/{SITE}/searchAnalytics/query",
        headers={"Authorization": f"Bearer {TOKEN}"},
        json={"startDate": start, "endDate": end,
              "dimensions": ["query"], "rowLimit": 1000},
        timeout=30)
    r.raise_for_status()
    return {x["keys"][0]: x for x in r.json().get("rows", [])}

before = pull("2025-09-01", "2025-09-30")
after  = pull("2026-09-01", "2026-09-30")

Use the same month a year apart so seasonality cancels out.

Step 2: Rank queries by click loss at stable impressions

Keep queries whose impressions moved less than 15 percent and sort by clicks lost. That threshold is a choice, not a rule. Tighten it if your list is long.

python
rows = []
for q, a in after.items():
    b = before.get(q)
    if not b or b["impressions"] < 200:
        continue
    drift = abs(a["impressions"] - b["impressions"]) / b["impressions"]
    if drift <= 0.15:
        rows.append((q, b["clicks"], a["clicks"], b["clicks"] - a["clicks"]))
rows.sort(key=lambda r: -r[3])
for r in rows[:10]:
    print(*r, sep="\t")

Read the top ten by hand. Informational queries ("what is", "how to") tend to lose first. Navigational and comparison queries tend to hold. Check that against your own rows before you believe it.

Step 3: Count the sessions that arrive from AI assistants

Assistants send some visitors, and your analytics tags them by referrer. Match hostnames in a list you own and update.

python
import csv, re
AI_HOSTS = re.compile(
    r"(^|\.)(chatgpt\.com|perplexity\.ai|gemini\.google\.com|"
    r"copilot\.microsoft\.com|claude\.ai)$")

ai, total = [], 0
with open("sessions.csv") as f:  # columns: referrer_host, company_domain
    for s in csv.DictReader(f):
        total += 1
        if AI_HOSTS.search(s["referrer_host"]):
            ai.append(s)
print(f"{len(ai)} of {total} sessions from AI hosts")

A visit that arrives with no referrer cannot be matched, so this is a floor. Treat it as one.

Step 4: Size the buyers you want before you spend a record

Sizing is free. Count the people in the role that reads your content. This example is VPs of marketing in the US.

bash
curl -s -H "x-api-key: $LEADOCEAN_API_KEY" \
  "https://api.leadocean.io/v1/people/search?count=true&limit=1" \
  -X POST -H "content-type: application/json" \
  -d '{"jobLevel":["VP"],"jobFunction":["Advertising & Marketing"],"country":["US"]}'

The count=true flag goes in the query string, not the body. The response has meta.total and an empty data array.

Step 5: Enrich the AI-referred company domains

Take the distinct company_domain values from Step 3 and look each one up. A company enrichment costs one record, and a miss is a free 404.

python
import os, requests
H = {"x-api-key": os.environ["LEADOCEAN_API_KEY"]}
domains = sorted({s["company_domain"] for s in ai if s["company_domain"]})[:5]
for d in domains:
    r = requests.post("https://api.leadocean.io/v1/companies/enrich",
                      headers=H, json={"domain": d}, timeout=30)
    print(d, r.status_code)

Step 6: Pull the people at those companies

GET /v1/companies/{domain}/people returns thin records, one record per person, up to 100 a page. Narrow it in your own code to the roles you sell to.

python
r = requests.get("https://api.leadocean.io/v1/companies/acme.com/people",
                 headers=H, params={"limit": 20}, timeout=30)
for p in r.json()["data"]:
    role = (p["contact_data"].get("contact_current_experiences") or [{}])[0]
    print(p["profile_data"].get("profile_full_name"), role.get("job_title"),
          p["meta"]["source_ids"]["person_id"])

Contact points are not in the roster. Enrich only the people you will write to, with POST /v1/people/enrich by person_id.

What you get

Free sizing call from the MCP tool leadocean_count_leads, run 2026-10-21, filters jobLevel VP, jobFunction Advertising & Marketing, country US (counted by mailable email status):

json
{
  "total": 49523,
  "totalIsExact": true,
  "totalAll": 125091,
  "distinctCompanies": 24142,
  "credits": 0
}

The 49,523 are people with a verified, catch_all_valid or catch_all email. The 125,091 is everyone matching with no email condition.

Free company sizing from leadocean_count_companies, run 2026-10-21, industry Computer Software, 51-200 employees, HQ US: 460 companies (136 in California, 36 in New York, 26 in Texas).

Roster row, response shape from the API reference, placeholder values:

json
{
  "success": true,
  "data": [{
    "profile_data": {"profile_full_name": "Jane Doe"},
    "contact_data": {"contact_current_experiences": [{"job_title": "VP Marketing", "company_name": "Acme", "company_domain": "acme.com"}]},
    "meta": {"source_ids": {"person_id": "1234567890"}}
  }],
  "meta": {"count": 1, "limit": 20, "credits": 1, "nextCursor": null}
}

Troubleshooting

ErrorCauseFix
401Missing or wrong x-api-keyRe-export $LEADOCEAN_API_KEY and resend
429Above the plan rate: 5 a second on Free, 200 on AgencyWait for Retry-After, then slow the loop
402Records spent (Free is 1,000 for life)Check GET /v1/usage, then move to a paid plan
Empty resultsA filter value is not an exact enum stringUse leadocean_list_enum_values or GET /v1/enums
404 on enrichNo match for that domainFree. Skip it
Capped page, no nextCursorOne walk pages to 10,000 rowsNarrow the filters and run smaller searches

FAQ

Is this proof that AI caused the click loss?

No. It shows clicks fell where impressions held. Layout changes, seasonality and ranking moves produce the same shape. Compare against a control set of queries you expect to be unaffected.

Why start from a year-ago month?

Same-month comparison removes seasonality. A month-on-month view mixes AI effects with the calendar.

Do I need Search Console data at all for the LeadOcean part?

No. Steps 4 to 6 work from any list of domains. Search Console tells you which content to defend. The API tells you who is behind the visits you still get.

How many records does the full run spend?

About 25: five company enrichments and twenty roster rows. Counts and misses are free.

Can I do the sizing step from an assistant?

Yes. The hosted MCP server has 13 tools, and leadocean_count_leads and leadocean_count_companies are free.

Related reading: AI lead gen tools, lead gen providers with strong support and platforms known for integrations. Plans are on pricing.

Turn the visits you can still see into contacts

Free to start. No credit card. 1,000 records to spend whenever you like.

Get your free API key →