cURL AI Visibility API
You cannot curl ChatGPT or Perplexity — their UIs are JavaScript-rendered, login-walled and bot-protected, so a script gets back an empty shell. What you can curl is https://api.agentgeo.org/v1/fetches: POST a query with your ag_live_ key and the raw answer each engine gives comes back as JSON — answer text, citations, sources — ready to pipe into jq. AgentGEO is the answer-access layer for AI visibility. Your own scripts (or your users' AI agent over MCP) run the GEO/AEO analysis; the API's job is to turn an AI answer into something a shell can address — the same structured data across all six engines.
You can't curl ChatGPT — the UI is JavaScript-rendered, login-walled and bot-protected. You can curl AgentGEO: one POST returns the raw answer each of the six engines gives — ChatGPT, Perplexity, Google AI Overviews, Copilot, Gemini — with citations and sources as JSON that pipes straight into jq. An AI answer becomes something a shell script can work with.
Get your free API key → · Read the quickstart → — Free tier, no credit card required.
Start for freeCall the AI Visibility API with cURL
One POST with your ag_live_ key as a bearer token. -d carries the query and the engines you want in surfaces; the answer, citations and sources come back as JSON. Pipe to jq to pretty-print — or try the no-signup playground before you even create a key.
curl -s https://api.agentgeo.org/v1/fetches \
-H "Authorization: Bearer ag_live_your_key_here" \
-H "Content-Type: application/json" \
-d '{"query": "best running shoes for flat feet", "surfaces": ["chatgpt"]}' \
| jqThe full response — every field addressable by a jq path:
{
"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 shape is identical for every engine, so one call listing several surfaces — any of chatgpt, perplexity, gemini, google_ai_overview, copilot — returns parallel citation sets, and diffing them is a jq one-liner. Who each engine cites is the core question of GEO/AEO analysis.
From there, extraction is jq. Wrap the call in a two-line shell function and the one-liners stay one-liners:
# A tiny helper so the one-liners stay one-liners
ag() {
curl -s https://api.agentgeo.org/v1/fetches \
-H "Authorization: Bearer ag_live_your_key_here" \
-H "Content-Type: application/json" \
-d "$1"
}
# Just the answer text
ag '{"query": "best running shoes for flat feet", "surfaces": ["chatgpt"]}' \
| jq -r '.answers[0].answerText'
# Every URL the answer cited
ag '{"query": "best running shoes for flat feet", "surfaces": ["chatgpt"]}' \
| jq -r '.answers[].sources[].url'
# Two engines, one call — per-engine citation lists, ready to diff
ag '{"query": "best running shoes for flat feet", "surfaces": ["chatgpt", "perplexity"]}' \
| jq -r '.answers[] | .surfaceKey, (.sources[] | " " + .url)'And because it's just curl, it schedules. Append each run as a timestamped JSON line and you have a minimal visibility monitor that runs unchanged in cron or CI:
# Append one timestamped JSON line per run — cron- and CI-friendly
curl -s https://api.agentgeo.org/v1/fetches \
-H "Authorization: Bearer ag_live_your_key_here" \
-H "Content-Type: application/json" \
-d '{"query": "best running shoes for flat feet", "surfaces": ["chatgpt", "perplexity"]}' \
| jq -c '. + {fetchedAt: (now | todate)}' >> visibility.jsonl
# crontab -e — track the answer every morning at 07:00
# 0 7 * * * /usr/local/bin/track-visibility.shWhat the cURL API gives you
AgentGEO is a thin answer-access layer, not a dashboard you log into. The API hands your scripts the raw material; whatever you pipe, diff, alert on or archive is yours.
- Six engines, one contract — ChatGPT, Perplexity, Google AI Overviews, Google AI Mode, Copilot and Gemini answer through the same endpoint; switching engines is editing one JSON array in your
-dpayload. - Citations at a stable `jq` path — every cited source lives at
.answers[].sources[]withtitle,urlandposition, so extraction is a one-liner, not a parser. - Managed-scraper engine, maintained for you — a plain HTTPS POST that behaves like every other API you script: no browser, no cookies, no bot wall between you and the data.
- Provider metadata —
surfaceKeyandproviderFieldson every response keep a long-running JSONL log auditable as the models underneath change. - MCP connection, too — the same data over MCP via
agentgeo-mcpon npm, for the day the consumer is Claude Code or Cursor instead of a script. - Usage-based billing with a spend cap — free tier without a credit card, then subscription plus overage; a forgotten cron loop can't surprise you. Never per-seat.
Common problems with DIY answer-scraping from the shell
The engines' UIs are apps, not documents — which is why pure shell against them goes nowhere, and why every DIY route ends with a headless browser: the opposite of what you reached for curl to get.
- curl gets an empty shell of the page. ChatGPT and Perplexity render answers client-side; fetch the page from a script and the body is a JavaScript bootstrap with no answer text anywhere in it.
- No session from a script. The UIs sit behind login and rotating session tokens — nothing a shell script can legitimately hold on to, so requests never reach an answer at all.
- Bot walls flag curl instantly. Cloudflare-class protection exists precisely to stop non-browser clients, and curl's TLS and header fingerprint is the first thing it filters.
- Parsing rendered HTML in shell is hopeless. grep and sed against a hydrated React DOM is write-only code — and it dies on the next frontend deploy anyway.
- The workaround erases the point. Each dead end pushes you toward bolting on headless Chrome — and once a browser farm sits behind your 'one-liner', the simplicity that made curl attractive is gone.
| curl against the AI UIs | curl against AgentGEO | |
|---|---|---|
| What comes back | An empty JS bootstrap shell | The full answer as JSON |
| Auth | Session cookies you can't script | One Authorization: Bearer header |
| Parsing | grep/sed against a hydrated DOM | jq one-liners |
| Bot walls | Fingerprinted and blocked | Managed-scraper engine, maintained for you |
| Automation | Bolt on headless Chrome, lose the one-liner | cron + curl + jq, done |
AgentGEO is the answer-access layer; a shell script appending JSONL is already a minimal monitoring product. The data lands on your disk, in your format, under your control — not inside someone else's dashboard.
MCP: when the consumer is an agent, not a script
If the thing consuming answers is an AI agent instead of a shell script — Claude Code, Cursor, Codex or any MCP client — the same data is available over MCP via the agentgeo-mcp npm package. The install is, fittingly, one command:
claude mcp add agentgeo -- npx -y agentgeo-mcp --key ag_live_your_key_hereWho builds on this
Anyone who wants answers without writing an application: a quick audit before a client call, a CI job that fails when your brand drops out of an answer, a cron loop building citation history in flat files. It's also the fastest way to evaluate the API before committing real code — start in the no-signup playground, graduate to a key. And if you were about to script against ChatGPT's UI instead, read that comparison first.
Ready to pipe your first answer? Start free → · Follow the 5-minute quickstart →
Start for freeFrequently asked questions
- Can I call the AI visibility API from cron or CI?
- That's the sweet spot. The JSONL pattern above — curl,
jq -c, append to a file — runs unchanged in crontab, GitHub Actions or any CI step, with nothing to install beyond curl and jq. The free tier and the spend cap on usage-based billing make an always-on scheduled check safe to leave running. - Do I need jq?
- No — the endpoint returns plain JSON you can pipe into anything, or nothing. jq just makes shell extraction one-liners:
.answers[0].answerTextfor the text,.answers[].sources[].urlfor the citations. Every example on this page works without it if you'd rather post-process elsewhere. - How do I query multiple engines in one call?
- List them in the
surfacesarray:"surfaces": ["chatgpt", "perplexity", "gemini", "google_ai_overview", "copilot"]. You get oneanswers[]entry per engine in a single response — ready to print per-engine citation lists with onejqexpression — and the response reportsrecordsDeliveredandcreditsChargedso cost per run stays visible. - Where do I get an API key?
- Sign up at /onboarding — the free tier requires no credit card. Keys look like
ag_live_...and go in theAuthorization: Bearerheader. Want to see a live response before signing up at all? The raw-answer playground at /tools/raw-answer-fetch runs a fetch with no key. - Why can't I just curl ChatGPT or Perplexity directly?
- Their UIs are JavaScript-rendered single-page apps behind login and bot protection — curl gets back a script-bootstrap shell with no answer in it, and anything smarter means running a headless browser, which defeats the point of a shell one-liner. This API exists to make an AI answer addressable by a plain HTTP request: the same data, but as JSON with citations.
Keep reading