Skip to content

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 --> L

Step 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

TermDefinition
AI OverviewGoogle's AI-generated answer box at the top of SERP. Cites 3–8 sources.
AI ModeFull AI-first layout that replaces traditional results for some queries.
AI CitationWhen your domain/brand is sourced in an AI-generated answer.
Traditional RankStandard organic position (position 1–100).
Visibility ScoreComposite 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:

  1. Check what content the cited sources have — often they have dedicated FAQ pages, structured data markup, or more comprehensive coverage of the query topic
  2. Add structured FAQ schema (FAQPage) to the client's relevant pages
  3. Ensure content directly answers the question being asked (not just mentions the topic)
  4. Build citations from authoritative sources — AI tends to cite sources that are themselves cited frequently
  5. Check if the client appears in position 1–3 traditionally — AI Overview sources usually come from the top organic results

Internal SOP reference — not affiliated with DataForSEO.