Tutorial

How to Measure GEO Performance with SERP Data

Track your GEO performance by monitoring AI Overview appearances, Knowledge Graph presence, and featured snippet capture rates across target queries.

Generative Engine Optimization performance cannot be measured by traditional rank tracking alone. In 2026, you need to track three surfaces: AI Overview citations, Knowledge Graph entity presence, and featured snippet capture. This tutorial builds a GEO performance dashboard that checks all three surfaces for a list of target queries, calculates a composite GEO score, and tracks changes over time. Each query check costs one Scavio credit at $0.005.

Prerequisites

  • Python 3.9+ installed
  • requests library installed
  • A Scavio API key from scavio.dev
  • A list of target queries to track

Walkthrough

Step 1: Define the GEO performance metrics

Set up the three surfaces to check for each query: AI Overview citation, Knowledge Graph presence, and featured snippet capture.

Python
import os, requests, json, time
from datetime import datetime

SCAVIO_KEY = os.environ['SCAVIO_API_KEY']
H = {'x-api-key': SCAVIO_KEY, 'Content-Type': 'application/json'}
URL = 'https://api.scavio.dev/api/v1/search'

TARGET_DOMAIN = 'scavio.dev'
QUERIES = [
    'search api for ai agents',
    'web search mcp server',
    'serp api pricing comparison',
    'tiktok data api',
    'google search api alternative',
]

Step 2: Check all three GEO surfaces per query

For each query, check AI Overview citation, Knowledge Graph mention, and featured snippet ownership. Return a structured result.

Python
def measure_geo(query: str, domain: str) -> dict:
    resp = requests.post(URL, headers=H,
        json={'query': query, 'country_code': 'us', 'include_ai_overview': True})
    data = resp.json()
    # AI Overview
    ai = data.get('ai_overview', {})
    ai_sources = ai.get('sources', [])
    ai_cited = any(domain in s.get('link', '') for s in ai_sources)
    # Knowledge Graph
    kg = data.get('knowledge_graph', {})
    kg_present = domain in json.dumps(kg) if kg else False
    # Featured Snippet
    snippet = data.get('featured_snippet', {})
    snippet_owned = domain in snippet.get('link', '') if snippet else False
    # Organic position
    organic = data.get('organic_results', [])
    org_pos = next((r['position'] for r in organic if domain in r.get('link', '')), None)
    # GEO score: 0-100
    score = 0
    if ai_cited: score += 40
    if kg_present: score += 25
    if snippet_owned: score += 25
    if org_pos and org_pos <= 3: score += 10
    return {
        'query': query, 'ai_cited': ai_cited, 'kg_present': kg_present,
        'snippet_owned': snippet_owned, 'organic_position': org_pos,
        'geo_score': score,
    }

Step 3: Run the full GEO performance report

Check all queries and compute aggregate GEO metrics. Save the report for historical comparison.

Python
def geo_report(queries: list, domain: str) -> dict:
    results = []
    for q in queries:
        r = measure_geo(q, domain)
        results.append(r)
        print(f'[{r["geo_score"]:3d}] {q} | AI:{r["ai_cited"]} KG:{r["kg_present"]} Snip:{r["snippet_owned"]} Org:#{r["organic_position"]}')
        time.sleep(0.5)
    avg_score = sum(r['geo_score'] for r in results) / len(results)
    ai_rate = sum(1 for r in results if r['ai_cited']) / len(results)
    kg_rate = sum(1 for r in results if r['kg_present']) / len(results)
    snip_rate = sum(1 for r in results if r['snippet_owned']) / len(results)
    report = {
        'date': datetime.now().strftime('%Y-%m-%d'),
        'domain': domain,
        'queries_checked': len(results),
        'avg_geo_score': round(avg_score, 1),
        'ai_citation_rate': round(ai_rate * 100, 1),
        'kg_presence_rate': round(kg_rate * 100, 1),
        'snippet_capture_rate': round(snip_rate * 100, 1),
        'results': results,
    }
    print(f'\nGEO Performance Summary')
    print(f'  Avg GEO Score: {avg_score:.1f}/100')
    print(f'  AI Citation Rate: {ai_rate:.0%}')
    print(f'  Knowledge Graph Rate: {kg_rate:.0%}')
    print(f'  Featured Snippet Rate: {snip_rate:.0%}')
    return report

geo_report(QUERIES, TARGET_DOMAIN)

Python Example

Python
import os, requests, time

SCAVIO_KEY = os.environ['SCAVIO_API_KEY']
H = {'x-api-key': SCAVIO_KEY, 'Content-Type': 'application/json'}

def measure_geo(query, domain):
    resp = requests.post('https://api.scavio.dev/api/v1/search', headers=H,
        json={'query': query, 'country_code': 'us', 'include_ai_overview': True})
    data = resp.json()
    ai_cited = any(domain in s.get('link', '') for s in data.get('ai_overview', {}).get('sources', []))
    snippet = data.get('featured_snippet', {})
    snip_owned = domain in snippet.get('link', '') if snippet else False
    score = (40 if ai_cited else 0) + (25 if snip_owned else 0)
    print(f'[{score:3d}] {query} | AI:{ai_cited} Snip:{snip_owned}')

for q in ['search api for agents', 'serp api comparison']:
    measure_geo(q, 'scavio.dev')
    time.sleep(0.5)

JavaScript Example

JavaScript
const SCAVIO_KEY = process.env.SCAVIO_API_KEY;

async function measureGeo(query, domain) {
  const resp = await fetch('https://api.scavio.dev/api/v1/search', {
    method: 'POST',
    headers: { 'x-api-key': SCAVIO_KEY, 'Content-Type': 'application/json' },
    body: JSON.stringify({ query, country_code: 'us', include_ai_overview: true })
  });
  const data = await resp.json();
  const aiCited = (data.ai_overview?.sources || []).some(s => (s.link || '').includes(domain));
  const snipOwned = (data.featured_snippet?.link || '').includes(domain);
  const score = (aiCited ? 40 : 0) + (snipOwned ? 25 : 0);
  console.log(`[${score}] ${query} | AI:${aiCited} Snip:${snipOwned}`);
}

(async () => {
  for (const q of ['search api for agents', 'serp api comparison']) {
    await measureGeo(q, 'scavio.dev');
  }
})();

Expected Output

JSON
[ 40] search api for ai agents | AI:True KG:False Snip:False Org:#4
[ 75] web search mcp server | AI:True KG:True Snip:True Org:#1
[  0] serp api pricing comparison | AI:False KG:False Snip:False Org:#8
[ 40] tiktok data api | AI:True KG:False Snip:False Org:#5
[ 10] google search api alternative | AI:False KG:False Snip:False Org:#3

GEO Performance Summary
  Avg GEO Score: 33.0/100
  AI Citation Rate: 60%
  Knowledge Graph Rate: 20%
  Featured Snippet Rate: 20%

Related Tutorials

Frequently Asked Questions

Most developers complete this tutorial in 15 to 30 minutes. You will need a Scavio API key (free tier works) and a working Python or JavaScript environment.

Python 3.9+ installed. requests library installed. A Scavio API key from scavio.dev. A list of target queries to track. A Scavio API key gives you 250 free credits per month.

Yes. The free tier includes 250 credits per month, which is more than enough to complete this tutorial and prototype a working solution.

Scavio has a native LangChain package (langchain-scavio), an MCP server, and a plain REST API that works with any HTTP client. This tutorial uses the raw REST API, but you can adapt to your framework of choice.

Start Building

Track your GEO performance by monitoring AI Overview appearances, Knowledge Graph presence, and featured snippet capture rates across target queries.