You cannot read ChatGPT's internal stats, but you can measure the source signal that decides what it says about your brand: the Google SERP and Reddit. This tutorial builds a share-of-voice tracker that scores how often your brand and competitors surface in the organic results, knowledge panel, People Also Ask and Reddit threads for a set of buying-intent prompts. It is the honest, reproducible version of what r/MarketingAnalytics threads describe doing by hand in a spreadsheet - automated, and grounded in data you can audit. The Scavio Share-of-Voice method below is five steps and runs on the Google and Reddit endpoints.
Prerequisites
- Python 3.8+ or Node 18+ installed
- A Scavio API key from scavio.dev (50 free credits on signup)
- requests installed (pip install requests) for the Python version
- A list of 10-50 buying-intent prompts your buyers would ask an AI assistant
Walkthrough
Step 1: Define the prompt set, not keywords
Share of voice in AI answers is measured per question, not per keyword. Write the prompts a buyer actually asks an assistant: best serp api for ai agents, alternatives to dataforseo, cheapest tiktok analytics tool. 10-50 prompts is enough to start. Store them in a list with the brands you want to score.
PROMPTS = [
"best serp api for ai agents",
"cheapest dataforseo alternative",
"how to track brand mentions in chatgpt",
]
BRANDS = ["scavio", "tavily", "serpapi", "dataforseo"]Step 2: Pull the full SERP signal for each prompt
Call the Scavio Google endpoint with light_request:false (2 credits) so the response includes organic results plus the knowledge_graph, related_searches and questions (People Also Ask) blocks. That is the exact material AI Overviews and ChatGPT ground on. Bearer auth, one POST per prompt.
import os, requests
H = {"Authorization": f"Bearer {os.environ['SCAVIO_API_KEY']}", "Content-Type": "application/json"}
def serp(prompt):
r = requests.post("https://api.scavio.dev/api/v1/google",
headers=H, json={"query": prompt, "light_request": False})
r.raise_for_status()
return r.json()Step 3: Count brand surface across organic, knowledge graph and PAA
For each prompt, scan the organic titles and snippets, the knowledge_graph, and the questions block for each brand. A mention in the knowledge panel or PAA is worth more than a page-2 organic link, so weight them. This gives a per-prompt brand score grounded in what the SERP actually shows.
def score_serp(data, brands):
blob = str(data).lower()
scores = {}
for b in brands:
organic = blob.count(b)
kg = 3 if b in str(data.get('knowledge_graph','')).lower() else 0
paa = 2 * str(data.get('questions','')).lower().count(b)
scores[b] = organic + kg + paa
return scoresStep 4: Add the Reddit leading-indicator layer
LLMs lean on Reddit, and Reddit presence often precedes AI-answer citations for the same topic. Call the Scavio Reddit search endpoint (2 credits) for each prompt and count brand mentions plus thread scores. A brand that owns the Reddit conversation today tends to own the AI answer next quarter.
def reddit_signal(prompt, brands):
r = requests.post("https://api.scavio.dev/api/v1/reddit/search",
headers=H, json={"query": prompt, "limit": 25})
threads = r.json().get("data", {}).get("posts", [])
blob = " ".join((t.get("title","") + t.get("selftext","")) for t in threads).lower()
return {b: blob.count(b) for b in brands}Step 5: Compute share of voice and store a daily snapshot
Sum each brand's SERP and Reddit scores across all prompts, then divide by the total to get a share-of-voice percentage. Append a dated row to a CSV or database so you can chart movement over weeks. Re-run on a daily cron; at 4 credits per prompt (2 SERP + 2 Reddit), 30 prompts is 120 credits a day, well inside the $30/mo 7,000-credit plan.
def share_of_voice(prompts, brands):
totals = {b: 0 for b in brands}
for p in prompts:
s = score_serp(serp(p), brands)
rd = reddit_signal(p, brands)
for b in brands:
totals[b] += s[b] + rd[b]
grand = sum(totals.values()) or 1
return {b: round(100*v/grand, 1) for b, v in totals.items()}
print(share_of_voice(PROMPTS, BRANDS))Python Example
import os, requests
H = {"Authorization": f"Bearer {os.environ['SCAVIO_API_KEY']}", "Content-Type": "application/json"}
BASE = "https://api.scavio.dev/api/v1"
def sov(prompt, brands):
serp = requests.post(f"{BASE}/google", headers=H,
json={"query": prompt, "light_request": False}).json()
reddit = requests.post(f"{BASE}/reddit/search", headers=H,
json={"query": prompt, "limit": 25}).json()
blob = (str(serp) + str(reddit)).lower()
raw = {b: blob.count(b) for b in brands}
total = sum(raw.values()) or 1
return {b: round(100*v/total, 1) for b, v in raw.items()}
print(sov("best serp api for ai agents", ["scavio","tavily","serpapi"]))JavaScript Example
const H = { Authorization: `Bearer ${process.env.SCAVIO_API_KEY}`, 'Content-Type': 'application/json' };
const BASE = 'https://api.scavio.dev/api/v1';
async function sov(prompt, brands) {
const [serp, reddit] = await Promise.all([
fetch(`${BASE}/google`, { method: 'POST', headers: H, body: JSON.stringify({ query: prompt, light_request: false }) }).then(r => r.json()),
fetch(`${BASE}/reddit/search`, { method: 'POST', headers: H, body: JSON.stringify({ query: prompt, limit: 25 }) }).then(r => r.json()),
]);
const blob = (JSON.stringify(serp) + JSON.stringify(reddit)).toLowerCase();
const raw = Object.fromEntries(brands.map(b => [b, (blob.match(new RegExp(b, 'g')) || []).length]));
const total = Object.values(raw).reduce((a, b) => a + b, 0) || 1;
return Object.fromEntries(brands.map(b => [b, Math.round((1000 * raw[b]) / total) / 10]));
}
sov('best serp api for ai agents', ['scavio','tavily','serpapi']).then(console.log);Expected Output
{'scavio': 41.2, 'tavily': 33.7, 'serpapi': 25.1}
# A per-brand share-of-voice percentage across the SERP and Reddit signal for each prompt,
# snapshotted daily so you can chart whether your brand is gaining or losing ground in the
# source material that feeds AI answers.