Esta guía está disponible en inglés.
How to Track AI and AEO Visibility Programmatically
Measure how ChatGPT, Gemini and Perplexity describe and recommend a business through an API, enqueue reports, poll for results and roll up a portfolio.
De un vistazo
- Qué logra esta guía
- Measure and monitor how AI answer engines describe and recommend a business, per location and across a portfolio.
- APIs utilizadas
- post
/api/v1/aeo/reports/enqueueEncolar la generación de un informe AEO - get
/api/v1/aeo/reportsObtener el informe AEO de una ubicación - get
/api/v1/aeo/rollupObtener el resumen AEO de todas las ubicaciones
- post
- Referencia
- AEO
- Requisitos previos
- An API key with write access to enqueue a report
- A locationId, and its clientId for the rollup
- Casos de uso habituales
- Adding an AI-visibility tab to a local marketing product
- Monitoring how ChatGPT and Gemini describe many locations
- Surfacing the fixes that would lift AI recommendations
Answer-engine optimisation is measurable: you can ask, for a location, how ChatGPT, Gemini and Perplexity describe and recommend it, and get back a score, the weak engines, and the fixes that would move it. The one thing to build around is that a report is a real job, not a lookup. You enqueue it, it runs model calls in the background, and you poll for the result.
Why this is a job, not a read
A report runs several prompts against several engines, and each is a model call. There is no job id to track. You
start a report and then poll the read endpoint, watching a generating flag until it flips.
- Enqueue
POST /api/v1/aeo/reports/enqueueStarts a report for a location - GeneratingSeveral model calls per engine and prompt run in the background
- Poll
GET /api/v1/aeo/reportsWatch generating until it is false - ReadVisibility score, per-engine detail, recommendations
Enqueue a report
POST /api/v1/aeo/reports/enqueue starts a report for a location. Because generation is expensive, treat this as a
monthly job that you kick off on onboarding and on a schedule, not something you run on a page load.
curl -X POST https://ai.synup.com/api/v1/aeo/reports/enqueue \
-H "Authorization: Bearer $SYNUP_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "locationId": "LOCATION_ID" }'Poll for the result
GET /api/v1/aeo/reports returns the latest report for a location. Watch generating: while it is true the
report is still running, and when it flips to false the generatedAt timestamp updates and the detail is filled
in. One case to handle: a location that has never had a report run also returns generating: false, but with a
null generatedAt and null scores on every engine. So generating: false on its own is ambiguous. Treat a null
generatedAt as "never run", not "ready", or a brand-new location reads as a finished report sitting at zero.
curl "https://ai.synup.com/api/v1/aeo/reports?locationId=LOCATION_ID" \
-H "Authorization: Bearer $SYNUP_API_KEY"const API = "https://ai.synup.com/api/v1";
const headers = { Authorization: `Bearer ${process.env.SYNUP_API_KEY}` };
async function getAeoReport(locationId) {
const res = await fetch(`${API}/aeo/reports?locationId=${locationId}`, { headers });
if (!res.ok) throw new Error(`${res.status}: ${(await res.json()).error}`);
const { data } = await res.json();
return data; // { generating, generatedAt, configuredEngines, visibility, factors, recommendations, ... }
}
// Poll on a schedule, not in a tight loop: generation takes real time
async function whenReady(locationId, { tries = 10, waitMs = 30000 } = {}) {
for (let i = 0; i < tries; i++) {
const report = await getAeoReport(locationId);
if (!report.generating) return report;
await new Promise((r) => setTimeout(r, waitMs));
}
return null; // still generating; check again later
}import os, time, requests
API = "https://ai.synup.com/api/v1"
HEADERS = {"Authorization": f"Bearer {os.environ['SYNUP_API_KEY']}"}
def get_aeo_report(location_id):
r = requests.get(f"{API}/aeo/reports", headers=HEADERS,
params={"locationId": location_id}, timeout=30)
r.raise_for_status()
return r.json()["data"] # generating, generatedAt, configuredEngines, visibility, factors, recommendations
def when_ready(location_id, tries=10, wait_s=30):
for _ in range(tries):
report = get_aeo_report(location_id)
if not report["generating"]:
return report
time.sleep(wait_s)
return None # still generating; check again laterWhat a report contains
Two blocks do the real work. factors is the scored drivers behind the visibility number, each with a label, a
value, a detail, and a recommended action. recommendations is the prioritised to-do list built from them,
each with a kind, a priority, a title, and an action. Most products render factors and recommendations and stop, because
together they turn a score into a list of things to change. Everything else in the report, the per-engine
matrix, ranking, citations, sentiment, and competitorHistory, is there for the drill-down when someone
asks why one engine scored the way it did.
Roll up a portfolio
GET /api/v1/aeo/rollup is the across-locations view, and it is the one to build a dashboard on.
curl "https://ai.synup.com/api/v1/aeo/rollup?clientId=CLIENT_ID" \
-H "Authorization: Bearer $SYNUP_API_KEY"It returns one row per location with an overall score and a per-engine score (chatgpt, gemini,
perplexity), a delta against last period, a history spark and the location's topIssue. Alongside the rows
it gives configuredEngines, a portfolioHistory trend, and systemicFixes: the factors that are weak across
many locations at once, each with the count and pct of locations affected. Those systemic fixes are where an
agency gets the most lift, because one change applied everywhere moves many scores.
Next
Measuring AI visibility is the first step. Acting on it is the second. The AI agent guide uses these scores and recommendations as inputs an agent works from, and AEO is one of the six features in the local marketing guide.