Appearance
Workflow: AI Search Visibility Fresh
AI-generated answers (Google AI Overviews, Perplexity, ChatGPT) are now a significant traffic factor. This workflow tracks whether your client is mentioned in AI responses, which sources the AI cites, and how AI results differ from traditional organic rankings.
How AI Search Works vs. Traditional SERP
flowchart TD
A[Search Query] --> B{Result Type}
B --> C[Traditional Organic\n10 blue links]
B --> D[AI Overview\nGenerated answer + citations]
B --> E[AI Mode SERP\nFull AI-first layout]
C --> F[Track with SERP API\n/serp/google/organic]
D --> G[Track with SERP API\n/serp/google/ai_overview or\nparse ai_overview items from organic]
E --> H[Track with AI Mode SERP\n/serp/google/ai_mode]
F --> I[Traditional rank position]
G --> J[AI Overview citation presence]
H --> K[AI Mode visibility score]
I --> L[Combined Visibility Report]
J --> L
K --> LStep 1: Check for AI Overviews in Standard SERP
When fetching standard organic SERP results, AI Overviews appear as items with type: "ai_overview". Check for them in every SERP response.
python
import os
import time
import json
import requests
from requests.auth import HTTPBasicAuth
auth = HTTPBasicAuth(
os.environ["DATAFORSEO_LOGIN"],
os.environ["DATAFORSEO_PASSWORD"]
)
CLIENT_DOMAIN = "example.com"
CLIENT_BRAND = "Acme Roofing"
TRACKED_KEYWORDS = [
"best roofing contractor miami",
"who is the best roofer in miami",
"top rated roofing companies miami",
"roofing contractor miami reviews",
"how to choose a roofer miami"
]
# Post standard SERP tasks
serp_payload = [
{
"keyword": kw,
"location_code": 1015116,
"language_code": "en",
"device": "desktop",
"depth": 30 # fetch enough results to capture AI overview block
}
for kw in TRACKED_KEYWORDS
]
response = requests.post(
"https://api.dataforseo.com/v3/serp/google/organic/tasks_post",
auth=auth,
json=serp_payload
)
task_ids = [t["id"] for t in response.json()["tasks"] if t["status_code"] == 20100]
print(f"SERP tasks posted: {len(task_ids)}")Step 2: Parse AI Overview Presence
After fetching results, check each SERP for AI Overview blocks and whether your client is cited.
python
def check_ai_overview_presence(items, client_domain, client_brand):
"""
Check if an AI Overview exists and if the client is mentioned.
Returns dict with: has_ai_overview, client_cited, citations, ai_text
"""
ai_result = {
"has_ai_overview": False,
"client_cited": False,
"citations": [],
"ai_text_snippet": None,
"client_position_in_citations": None
}
for item in items:
if item.get("type") == "ai_overview":
ai_result["has_ai_overview"] = True
ai_result["ai_text_snippet"] = (item.get("text") or "")[:300]
# Check references/citations
references = item.get("references") or item.get("items") or []
for i, ref in enumerate(references):
ref_url = ref.get("url") or ref.get("source", {}).get("url", "")
ref_title = ref.get("title", "")
ai_result["citations"].append({
"position": i + 1,
"url": ref_url,
"title": ref_title
})
if client_domain in ref_url or client_brand.lower() in ref_title.lower():
ai_result["client_cited"] = True
ai_result["client_position_in_citations"] = i + 1
break # only one AI Overview per SERP
return ai_result
def wait_and_fetch_serp(auth, task_ids, max_wait=300):
"""Poll and fetch SERP results."""
ready = set()
deadline = time.time() + max_wait
while time.time() < deadline and len(ready) < len(task_ids):
data = requests.get(
"https://api.dataforseo.com/v3/serp/google/organic/tasks_ready",
auth=auth
).json()
for task in data["tasks"]:
for r in task.get("result", []):
if r["id"] in task_ids:
ready.add(r["id"])
if len(ready) < len(task_ids):
time.sleep(20)
results = {}
for tid in ready:
data = requests.get(
f"https://api.dataforseo.com/v3/serp/google/organic/task_get/advanced/{tid}",
auth=auth
).json()
task = data["tasks"][0]
keyword = task["data"]["keyword"]
items = task["result"][0].get("items", []) if task.get("result") else []
results[keyword] = items
return results
serp_data = wait_and_fetch_serp(auth, set(task_ids))
# Analyze AI Overview presence for each keyword
ai_overview_report = {}
for keyword, items in serp_data.items():
# Also find traditional organic position
organic_position = None
for item in items:
if item.get("type") == "organic" and CLIENT_DOMAIN in (item.get("url") or ""):
organic_position = item["rank_absolute"]
break
ai_data = check_ai_overview_presence(items, CLIENT_DOMAIN, CLIENT_BRAND)
ai_data["organic_position"] = organic_position
ai_overview_report[keyword] = ai_data
# Summary
print(f"\nAI OVERVIEW TRACKING — {CLIENT_DOMAIN}")
print(f"{'Keyword':<50} {'AI OV':>6} {'Cited':>6} {'Cit. Pos':>9} {'Organic':>8}")
print("-" * 83)
for kw, data in ai_overview_report.items():
has_ov = "Yes" if data["has_ai_overview"] else "No"
cited = "Yes" if data["client_cited"] else "No"
cit_pos = str(data["client_position_in_citations"]) if data["client_position_in_citations"] else "-"
org_pos = str(data["organic_position"]) if data["organic_position"] else "Not ranked"
print(f"{kw:<50} {has_ov:>6} {cited:>6} {cit_pos:>9} {org_pos:>8}")Step 3: Track AI Mode SERP
AI Mode is Google's full AI-generated results layout. Track it separately for a different view of visibility.
python
def get_ai_mode_results(auth, keywords, location_code, language_code="en"):
"""
Fetch AI Mode SERP results for a list of keywords.
AI Mode returns AI-generated responses with cited sources.
"""
payload = [
{
"keyword": kw,
"location_code": location_code,
"language_code": language_code,
"device": "desktop",
"depth": 10
}
for kw in keywords
]
# Task-based for cost efficiency
response = requests.post(
"https://api.dataforseo.com/v3/serp/google/ai_mode/tasks_post",
auth=auth,
json=payload
)
data = response.json()
task_ids = [t["id"] for t in data["tasks"] if t["status_code"] == 20100]
return task_ids
def parse_ai_mode_citations(result_items, client_domain, client_brand):
"""Extract citation data from AI Mode results."""
citations_found = []
for item in result_items:
# AI Mode results typically have type "ai_mode" or similar
if "citation" in item.get("type", "") or item.get("type") == "ai_mode":
sources = item.get("items") or item.get("sources") or []
for source in sources:
url = source.get("url", "")
title = source.get("title", "")
if client_domain in url or client_brand.lower() in title.lower():
citations_found.append({
"url": url,
"title": title,
"position": source.get("rank_absolute")
})
return citations_found
# Run AI Mode tracking
ai_mode_task_ids = get_ai_mode_results(auth, TRACKED_KEYWORDS, 1015116)
print(f"\nAI Mode tasks posted: {len(ai_mode_task_ids)}")Step 4: Combine AI + Traditional Visibility Score
Calculate a composite visibility score that weights AI citations alongside traditional rankings.
python
def calculate_visibility_score(keyword_data_list):
"""
Calculate a composite visibility score per keyword.
Components:
- Traditional organic rank (0-100 scale, lower rank = higher score)
- AI Overview citation (bonus points for being cited, position-weighted)
- AI Mode citation (additional bonus)
Returns score 0-100 for each keyword.
"""
scores = []
for entry in keyword_data_list:
score = 0
# Traditional organic: rank 1 = 100 pts, rank 10 = 10 pts, not ranked = 0
organic_pos = entry.get("organic_position")
if organic_pos:
organic_score = max(0, 100 - (organic_pos - 1) * 10)
score += organic_score * 0.6 # 60% weight
# AI Overview citation: being cited at position 1 = 40 pts, position 3 = 20 pts
if entry.get("client_cited"):
cit_pos = entry.get("client_position_in_citations", 5)
ai_score = max(10, 40 - (cit_pos - 1) * 10)
score += ai_score * 0.4 # 40% weight
scores.append({
"keyword": entry["keyword"],
"visibility_score": round(score, 1),
"organic_position": organic_pos,
"ai_cited": entry.get("client_cited", False),
"citation_position": entry.get("client_position_in_citations")
})
return sorted(scores, key=lambda x: x["visibility_score"], reverse=True)
# Build input list
keyword_data_for_scoring = []
for kw, data in ai_overview_report.items():
keyword_data_for_scoring.append({
"keyword": kw,
**data
})
scores = calculate_visibility_score(keyword_data_for_scoring)
print("\nCOMPOSITE AI VISIBILITY SCORES")
print(f"{'Keyword':<50} {'Score':>6} {'Organic':>8} {'AI Cited':>9}")
print("-" * 76)
for entry in scores:
org = str(entry["organic_position"]) if entry["organic_position"] else "-"
ai_cited = "Yes" if entry["ai_cited"] else "No"
print(f"{entry['keyword']:<50} {entry['visibility_score']:>6.1f} {org:>8} {ai_cited:>9}")Step 5: Track Over Time
Save results with timestamps to track visibility trends.
python
import csv
from datetime import datetime
def append_visibility_to_csv(scores, client_domain, filepath="ai-visibility-history.csv"):
"""Append scored results to history CSV."""
timestamp = datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%SZ")
fieldnames = ["timestamp", "client", "keyword", "visibility_score",
"organic_position", "ai_cited", "citation_position"]
import os
write_header = not os.path.exists(filepath)
with open(filepath, "a", newline="", encoding="utf-8") as f:
writer = csv.DictWriter(f, fieldnames=fieldnames)
if write_header:
writer.writeheader()
for entry in scores:
writer.writerow({
"timestamp": timestamp,
"client": client_domain,
"keyword": entry["keyword"],
"visibility_score": entry["visibility_score"],
"organic_position": entry.get("organic_position"),
"ai_cited": entry["ai_cited"],
"citation_position": entry.get("citation_position")
})
print(f"Saved {len(scores)} records to {filepath}")
append_visibility_to_csv(scores, CLIENT_DOMAIN)Key Concepts
| Term | Definition |
|---|---|
| AI Overview | Google's AI-generated answer box at the top of SERP. Cites 3–8 sources. |
| AI Mode | Full AI-first layout that replaces traditional results for some queries. |
| AI Citation | When your domain/brand is sourced in an AI-generated answer. |
| Traditional Rank | Standard organic position (position 1–100). |
| Visibility Score | Composite metric combining traditional rank + AI citation presence. |
What to Do If a Client Is Not Being Cited
When a competitor is consistently cited in AI Overviews but your client isn't:
- Check what content the cited sources have — often they have dedicated FAQ pages, structured data markup, or more comprehensive coverage of the query topic
- Add structured FAQ schema (
FAQPage) to the client's relevant pages - Ensure content directly answers the question being asked (not just mentions the topic)
- Build citations from authoritative sources — AI tends to cite sources that are themselves cited frequently
- Check if the client appears in position 1–3 traditionally — AI Overview sources usually come from the top organic results