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.
Read this page with an AI
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 a free AI-visibility audit → · Read the quickstart → - No card, no account.
Get my free auditCall 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 - the full response shape is in the docs.
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
jqpath - 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 - run allowances by tier with a spend cap, so 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 with the free audit, graduate to a key. And if you were about to script against ChatGPT's UI instead, read that comparison first.
Ready to see the answers on your own brand? Get a free audit → · Read the 5-minute quickstart →
Get my free audit