Track competitor visibility in AI answers
When a buyer asks ChatGPT for the best tool in your category and you're not in the answer, somebody else is. Competitor AI visibility is the job of knowing exactly who — which rivals get named, whose pages get cited as evidence, and how that roster shifts month to month. It is the same fetch as tracking yourself; the difference is the brand list you test against the records. AgentGEO returns each engine's raw answer and sources as JSON, and your script tests every competitor in one pass — no second product, no extra credits.
Read this page with an AI
One run answers three questions a positioning meeting actually needs: who is in the answers we're not in, whose content earns the citations, and did last month's launch move anything.
Get a free AI-visibility audit → · Read the docs → — No card, no account.
Get my free auditHow competitor tracking works
- Name the set. You plus the four to eight rivals that show up in real deals. Track their domains too, not just their names — mentions and citations are different games.
- Fetch the category questions. The queries where competitors displace you are comparison-shaped: "best X", "X alternatives", "[rival] vs".
POST /v1/fetches, engines insurfaces, one record per engine per query. - Score every brand against every answer. Name against
answerText, domain againstsources[].urlhosts. Store the matrix — query × engine × brand — and diff it between runs.
import requests
RIVALS = {
"Acme": "acme.com", # you
"Northwind": "northwind.io",
"Globex": "globex.dev",
}
resp = requests.post(
"https://api.agentgeo.org/v1/fetches",
json={"query": "acme alternatives", "surfaces": ["chatgpt", "perplexity", "gemini"]},
headers={"Authorization": "Bearer ag_live_your_key_here"},
timeout=200,
)
resp.raise_for_status()
for answer in resp.json()["answers"]:
text = (answer.get("answerText") or "").lower()
hosts = " ".join(s["url"].lower() for s in answer.get("sources") or [])
print(answer["surfaceKey"])
for brand, domain in RIVALS.items():
mark = "named" if brand.lower() in text else "—"
cite = "cited" if domain in hosts else "—"
print(f" {brand:10} {mark:6} {cite}")| What you find | What it means | The move |
|---|---|---|
| Rival named, you're not | The model's default roster excludes you for this phrasing. | Publish the page that answers this exact question; check again next run. |
| Rival's blog cited under your brand query | Their comparison page owns your narrative. | Ship your own comparison page — engines cite whoever states the comparison plainly. |
| You're named, rival is cited | You have the reputation; they have the evidence. | The missing piece is a citable page, not more brand copy. |
Fetch your competitors' brand queries too — "is Northwind good", "Northwind pricing". Where the engines cite your comparison content under their name, you've taken shelf space; where they cite the rival's own docs, that query is defended.
Let your agent run it
Over MCP, the geo-competitors skill — one of the eight shipped in agentgeo-mcp — runs this matrix and writes the comparison up. The useful part of agent-side analysis: it holds your positioning docs in context, so "who's beating us in Gemini and why" gets answered against your actual claims, not a generic template.
claude mcp add agentgeo -- npx -y agentgeo-mcp --key ag_live_...Alternatives
Competitor modules in GEO dashboards (Profound, Peec AI) show the same story inside their UI, priced per seat, with their fixed rival list and query set. If the output needs to reach a strategy doc, a board slide or an agent, raw records you own are the sturdier input. Scraping it yourself multiplies the maintenance by every engine you add.
More use cases
The same matrix feeds share-of-voice monitoring and product recommendation tracking — see all use cases.
Know who's in the answers instead of you: Get a free AI-visibility audit →
Get my free audit