Node.js AI Visibility API
Query the raw answers ChatGPT, Perplexity, Google AI Overviews, Google AI Mode, Copilot and Gemini actually give — from Node.js, using nothing but the fetch that ships with the runtime. POST a query to https://api.agentgeo.org/v1/fetches with your ag_live_ key and the answer text, citations and sources come back as clean JSON. No Puppeteer, no proxy pool, no npm install at all. AgentGEO is the answer-access layer for AI visibility — a Node.js GEO API in the plainest sense. Your own code (or your users' AI agent over MCP) runs the GEO/AEO analysis; the API just hands you the raw data, structured and identical across all six engines.
Query the raw answers ChatGPT, Perplexity, Google AI Overviews, Google AI Mode, Copilot and Gemini actually return — from Node.js, with the fetch that ships in the runtime. No Puppeteer, no browser pool, no dependency to install: one POST, and the answer text, citations and sources arrive as clean JSON your code can act on.
Get your free API key → · Read the quickstart → — Free tier, no credit card required.
Start for freeCall the AI Visibility API from Node.js
POST to https://api.agentgeo.org/v1/fetches with your ag_live_ key as a bearer token and the engines you want in the surfaces array. Node 20+ ships fetch natively, so the entire integration is standard library — no axios, no SDK, no node_modules at all.
// Node 20+ — native fetch, zero dependencies. Run as ESM for top-level await.
const resp = await fetch("https://api.agentgeo.org/v1/fetches", {
method: "POST",
headers: {
"Authorization": "Bearer ag_live_your_key_here",
"Content-Type": "application/json",
},
body: JSON.stringify({
query: "best running shoes for flat feet",
surfaces: ["chatgpt"], // chatgpt | perplexity | gemini | google_ai_overview | copilot
}),
});
if (!resp.ok) throw new Error(`AgentGEO ${resp.status}: ${await resp.text()}`);
const run = await resp.json();
for (const answer of run.answers) {
console.log(answer.surfaceKey, "→", answer.answerText);
for (const src of answer.sources) { // cited sources, in order
console.log(" ", src.position, src.title, src.url);
}
}What comes back is plain JSON — no DOM, no cleanup pass:
{
"id": "run_84c1f2ab91d3",
"query": "best running shoes",
"surfaces": ["chatgpt"],
"status": "completed",
"recordsDelivered": 1,
"creditsCharged": 1,
"answers": [
{
"surfaceKey": "chatgpt",
"answerText": "For most runners, top picks include ...",
"sources": [
{ "title": "Best Running Shoes", "url": "https://example.com/guide", "position": 1 }
]
}
]
}The response shape is identical across all six engines, so the fan-out below is the whole integration: the same query into chatgpt, perplexity, gemini, google_ai_overview and copilot, then diff the citation sets. That diff is the core of any GEO/AEO analysis you'd want to build.
Fanning one query across every engine is a Promise.all away — one request per surface key, then print who cites what:
const SURFACES = ["chatgpt", "perplexity", "gemini", "google_ai_overview", "google_ai_mode", "copilot"];
async function fetchAnswers(query, surface) {
const resp = await fetch("https://api.agentgeo.org/v1/fetches", {
method: "POST",
headers: {
"Authorization": "Bearer ag_live_your_key_here",
"Content-Type": "application/json",
},
body: JSON.stringify({ query, surfaces: [surface] }),
});
if (!resp.ok) throw new Error(`AgentGEO ${resp.status}`);
return resp.json();
}
// One query, six engines, in parallel
const runs = await Promise.all(
SURFACES.map((s) => fetchAnswers("best running shoes for flat feet", s))
);
// Who cites what — the raw material for any share-of-voice metric
for (const run of runs) {
for (const answer of run.answers) {
console.log(answer.surfaceKey, "cites:", answer.sources.map((src) => src.url));
}
}What the Node.js API gives you
AgentGEO is a thin answer-access layer, not another closed dashboard. The API delivers the raw material; your Node service, worker or cron job (or your users' AI agent) does the analysis.
- Six engines, one contract — ChatGPT, Perplexity, Google AI Overviews, Google AI Mode, Copilot and Gemini behind the same request/response shape, so covering a new engine is one more string in the
surfacesarray. - Citations and sources, structured — every answer carries
sources[]withtitle,urlandposition, ready to store, rank or diff. No cheerio, no regexing links out of markup. - Managed-scraper engine, maintained for you — answers come through supported access paths, so there is no Chromium to babysit, no stealth plugin to update and no IP ban to recover from at 2 a.m.
- Provider metadata —
surfaceKeyandproviderFieldsride along on every response, keeping your dataset auditable as the underlying models change. - MCP connection, too — the same data is exposed over MCP via the
agentgeo-mcpnpm package, so Claude Code, Cursor, Codex or any MCP client can pull answers and run the analysis itself. - Usage-based billing with a spend cap — free tier with no credit card, then subscription plus overage. Priced by usage, never per seat.
Common problems with DIY answer-scraping in Node.js
The default instinct in Node is to reach for Puppeteer or Playwright, drive ChatGPT or Perplexity headlessly, and pull answers out of the DOM. It works in a demo on Tuesday and becomes an ops project by the following sprint.
- Puppeteer/Playwright farms. Every tracked query runs a real Chromium instance — hundreds of MB apiece — so a modest keyword set turns into a browser farm with its own memory graphs, restarts and crash handling.
- Stealth-plugin cat-and-mouse.
puppeteer-extra-plugin-stealthworks until fingerprinting updates, then you're pinning forks and chasing GitHub issues just to stay logged in — work that ships zero product. - Selectors break on their deploys, not yours. cheerio and DOM queries bind to hashed, ever-shifting class names; ChatGPT ships a frontend change and your extraction starts returning nulls in production with no failing test.
- Proxy rotation and pool orchestration. Residential proxies, retries, backoff and
p-queueconcurrency caps across six engines — a distributed-systems side quest bolted onto a marketing metric. - Headless Chrome doesn't fit serverless. Chromium blows past Lambda's bundle limits, so you maintain
@sparticuz/chromiumbuilds and slow cold starts — or give up and run a persistent browser fleet.
| DIY scraping in Node.js | AgentGEO API | |
|---|---|---|
| Setup | Puppeteer + stealth plugins, proxies, browser pools | Native fetch + a key — zero dependencies |
| Parsing | cheerio/DOM selectors that break on frontend deploys | Stable JSON, identical across engines |
| Citations | Hand-rolled DOM walking per engine | Structured answers[].sources[] |
| Serverless & CI | Chromium vs Lambda size limits | A plain HTTPS call — runs anywhere |
| Adding an engine | A new scraper and browser pool | One more key in the surfaces array |
AgentGEO is the answer-access layer; whatever dashboard, alert or score you build in Node on top of it is yours. You own the raw data and ship it inside your own product, instead of renting a login to someone else's.
Prefer MCP? Same data, no glue code
Everything the REST endpoint returns is also exposed over MCP. If the consumer is an AI agent rather than a Node service — Claude Code, Cursor, Codex or any MCP client — install the agentgeo-mcp package from npm and the agent fetches answers and runs the GEO analysis itself:
claude mcp add agentgeo -- npx -y agentgeo-mcp --key ag_live_your_key_hereWho builds on this
Mostly product engineers adding AI-visibility tracking to an existing SaaS — a nightly worker that fans a keyword set across six engines and writes citations to Postgres is an afternoon of Node — plus agencies white-labeling GEO/AEO reporting for clients. If you evaluated closed dashboards like Profound and wanted the data underneath them, or you were about to scrape ChatGPT yourself, this is the layer to build on.
Ready to pull your first answer? Start free → · Follow the 5-minute quickstart →
Start for freeFrequently asked questions
- Do I need Puppeteer or Playwright to track AI answers in Node.js?
- No. AgentGEO's managed scrapers run server-side, so the whole integration is one
fetchcall tohttps://api.agentgeo.org/v1/fetcheswith aBearer ag_live_key. There is no Chromium to launch, no stealth plugin to keep current and no proxy pool to rotate — the failure modes of browser automation simply don't exist in this stack. - Does it work in serverless and edge runtimes like Lambda, Vercel and Cloudflare Workers?
- Yes. The API is a plain HTTPS POST that returns JSON, so it runs anywhere
fetchexists — AWS Lambda, Vercel and Netlify functions, Cloudflare Workers, Deno and Bun included. Because there is no bundled Chromium, you also skip the package-size gymnastics that headless-browser scrapers hit on Lambda. - Are there TypeScript types for the response?
- The response is small enough to type yourself in about twenty lines — a
SurfaceKeyunion plusSource,AnswerandFetchRuninterfaces. The TypeScript page at /geo-api/typescript shows the full typed wrapper, ready to paste into your project. - How should I handle concurrency and rate limits in Node?
- Two good options. Put every engine in a single request —
"surfaces": ["chatgpt", "perplexity", "gemini", "google_ai_overview", "copilot"]— and get oneanswers[]entry per engine back. Or fan out one request per surface withPromise.all, capping parallelism with something likep-limitat higher volumes. Billing is usage-based with a spend cap, so a runaway loop can't produce a surprise invoice. - Is there an npm SDK?
- You don't need one — the integration is native
fetchwith zero dependencies. The npm package that does exist isagentgeo-mcp, the MCP server:claude mcp add agentgeo -- npx -y agentgeo-mcp --key ag_live_...puts the same answer data inside Claude Code, Cursor, Codex or any other MCP client.
Keep reading