GEO (Generative Engine Optimization) is the 2026 rebrand of SEO for AI answer engines. A GEO audit tool checks presence, citation share, and content structure across ChatGPT, Perplexity, and Gemini. This tutorial builds a simple scoring audit backed by Scavio.
Prerequisites
- Python 3.10+
- A Scavio API key
- A query panel (20-50 target queries)
- Your domain
Walkthrough
Step 1: Run each query across engines
ChatGPT, Perplexity, Gemini (AI Overview) coverage.
import requests, os
API_KEY = os.environ['SCAVIO_API_KEY']
def run(query):
engines = ['chatgpt', 'perplexity']
out = {}
for e in engines:
r = requests.post('https://api.scavio.dev/api/v1/search',
headers={'x-api-key': API_KEY},
json={'platform': e, 'query': query})
out[e] = r.json()
ov = requests.post('https://api.scavio.dev/api/v1/search',
headers={'x-api-key': API_KEY},
json={'query': query, 'include': ['ai_overview']}).json().get('ai_overview')
out['gemini_overview'] = ov
return outStep 2: Score citation presence per engine
1 point per engine where your domain is cited.
def score_query(resp, domain):
score = 0
for e, r in resp.items():
srcs = r.get('citations', []) + r.get('sources', []) if isinstance(r, dict) else []
if any(domain in s.get('url', '') for s in srcs):
score += 1
return scoreStep 3: Audit content structure
Check your pages have FAQ schema, clear headings, and short paragraphs.
def structure_check(url):
html = requests.get(url).text
return {
'has_faq_schema': 'FAQPage' in html,
'headings': html.count('<h2'),
'paragraphs': html.count('<p')
}Step 4: Compute overall GEO score
Weighted average of presence + structure.
def geo_score(presence_score, structure):
return 0.7 * presence_score + 0.3 * min(structure['headings'] / 5, 1)Step 5: Generate an action list
Flag queries where you are absent with suggested content fixes.
def actions(results, domain):
return [q for q, s in results.items() if s['presence'] == 0]Python Example
import os, requests
API_KEY = os.environ['SCAVIO_API_KEY']
QUERIES = ['best ai agent framework', 'how to build a rag agent']
DOMAIN = 'scavio.dev'
score = 0
for q in QUERIES:
r = requests.post('https://api.scavio.dev/api/v1/search',
headers={'x-api-key': API_KEY},
json={'platform': 'perplexity', 'query': q})
if any(DOMAIN in s.get('url', '') for s in r.json().get('sources', [])):
score += 1
print(f'GEO coverage: {score}/{len(QUERIES)}')JavaScript Example
const API_KEY = process.env.SCAVIO_API_KEY;
const QUERIES = ['best ai agent framework'];
const DOMAIN = 'scavio.dev';
let score = 0;
for (const q of QUERIES) {
const r = await fetch('https://api.scavio.dev/api/v1/search', {
method: 'POST',
headers: { 'x-api-key': API_KEY, 'Content-Type': 'application/json' },
body: JSON.stringify({ platform: 'perplexity', query: q })
});
const d = await r.json();
if (d.sources?.some(s => s.url.includes(DOMAIN))) score++;
}
console.log(`GEO coverage: ${score}/${QUERIES.length}`);Expected Output
Per-query GEO report with score, missing engines, and content structure gaps. Typical first-run score: 15-30% coverage on B2B SaaS, improvable to 60%+ in 90 days.