PHP AI Visibility API
Query the raw answers ChatGPT, Perplexity, Google AI Overviews, Google AI Mode, Copilot and Gemini actually give — from PHP, with the curl extension you already have. 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 JSON that json_decode turns into a plain array. No Panther, no headless Chrome, no long-running browser fighting PHP's request lifecycle. AgentGEO is the answer-access layer for AI visibility. 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 PHP, with the bundled curl extension and json_decode. No symfony/panther, no headless Chrome to keep alive between requests: one POST, and the answer text, citations and sources come back as an associative array your controllers and jobs 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 PHP
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. The whole integration is the curl extension that ships with PHP — no Composer package required. (Want to see the payload before writing code? The raw answer playground runs a query in the browser, no signup.)
<?php
$ch = curl_init('https://api.agentgeo.org/v1/fetches');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ag_live_your_key_here',
'Content-Type: application/json',
],
// surfaces: chatgpt | perplexity | gemini | google_ai_overview | google_ai_mode | copilot
CURLOPT_POSTFIELDS => json_encode([
'query' => 'best running shoes for flat feet',
'surfaces' => ['chatgpt'],
]),
]);
$body = curl_exec($ch);
if ($body === false) {
throw new RuntimeException('agentgeo: ' . curl_error($ch));
}
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);
if ($status !== 200) {
throw new RuntimeException("agentgeo: unexpected status $status");
}
$run = json_decode($body, true);
foreach ($run['answers'] as $answer) {
echo $answer['surfaceKey'], ' → ', $answer['answerText'], PHP_EOL;
foreach ($answer['sources'] as $src) { // cited sources, in order
echo ' ', $src['position'], ' ', $src['title'], ' ', $src['url'], PHP_EOL;
}
}The response is a plain JSON object — json_decode($body, true) hands you a clean associative array, no HTML to scrub:
{
"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 same array walk covers chatgpt, perplexity, gemini, google_ai_overview, google_ai_mode and copilot. Ask for all six in one call and diff the citation sets — that diff is the core of any GEO/AEO analysis you'd want to build.
Wrap the call in a small function and you can ask every engine at once — the API returns one answer per surface, so a single request covers all six. Here it collects the cited host per surface:
<?php
/** Fetch one query across several engines, return cited hosts per surface. */
function fetch_cited_hosts(string $query, array $surfaces): array
{
$ch = curl_init('https://api.agentgeo.org/v1/fetches');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ag_live_your_key_here',
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode(compact('query', 'surfaces')),
]);
$run = json_decode(curl_exec($ch), true);
curl_close($ch);
$hosts = [];
foreach ($run['answers'] as $answer) {
foreach ($answer['sources'] as $src) {
$hosts[$answer['surfaceKey']][] = parse_url($src['url'], PHP_URL_HOST);
}
}
return $hosts;
}
// All six engines in one call
$hosts = fetch_cited_hosts('best running shoes for flat feet', [
'chatgpt', 'perplexity', 'gemini', 'google_ai_overview', 'google_ai_mode', 'copilot',
]);
print_r($hosts);Prefer Guzzle? It works identically — $client->post('https://api.agentgeo.org/v1/fetches', ['headers' => [...], 'json' => [...]]) returns the same JSON, and everything below is unchanged.
What the PHP API gives you
AgentGEO is a thin answer-access layer, not another closed dashboard. The API delivers the raw material; your PHP app, queue worker or scheduled command (or your users' AI agent) does the analysis.
- Six engines, one contract — ChatGPT, Perplexity, Google AI Overviews, Google AI Mode, Copilot and Gemini behind one request/response shape, so covering another engine is one more string in the
surfacesarray. - Citations and sources, structured — every answer carries
sources[]with title, URL and position, ready to store, rank or diff. No DOMDocument, no XPath, no regexing links out of markup. - Managed-scraper engine, maintained for you — answers come through supported access paths, so there is no browser process to supervise, no proxy list to rotate and no IP ban to recover from mid-run.
- Provider metadata — every response carries
surfaceKeyandproviderFields, so the dataset you build stays auditable as the engines underneath evolve. - MCP connection, too — the same data is exposed over MCP through the
agentgeo-mcppackage, so Claude Code, Cursor, Codex or any MCP client can pull answers and run the GEO analysis itself, no glue code. - Usage-based billing with a spend cap — start on the free tier with no credit card, then subscription plus overage, priced by usage and never per seat.
Common problems with DIY answer-scraping in PHP
The DIY route in PHP is symfony/panther or chrome-php/chrome: drive ChatGPT or Perplexity in a headless browser and scrape the DOM. It works on your laptop and then collides with everything PHP is good at.
- Headless browsers vs the request lifecycle. PHP is built to handle a request and exit; a Panther-driven Chrome wants to live for minutes. On shared or FPM hosting that mismatch means timeouts, zombie chrome processes and workers you have to reap by hand.
- `max_execution_time` and memory limits. A 40–90 second scrape blows past default execution limits, and a headless Chrome per query pushes
memory_limit— so you end up tuning php.ini around a browser instead of shipping features. - DOMDocument and XPath against hydrated React. ChatGPT and Perplexity render answers client-side with hashed class names; your XPath selectors start returning empty nodes after a frontend deploy you never see, usually in production.
- No stable citation model. Pulling which sources an answer cited — in order, deduplicated — out of rendered HTML is fiddly and engine-specific, and you reinvent it for each of six engines.
- cron + proxy churn. Keeping scrapers alive means residential proxies, retries and CAPTCHA solving wired into your scheduler — operational work that has nothing to do with your product.
| DIY scraping in PHP | AgentGEO API | |
|---|---|---|
| Setup | Panther/chrome-php, Chrome binary, proxies | The bundled curl extension + a key |
| Parsing | DOMDocument/XPath against hashed classes | Stable JSON, identical across engines |
| Citations | Hand-rolled extraction per engine | Structured answers[].sources[] |
| Hosting | Long-running browser vs FPM lifecycle | A plain HTTPS call — shared hosting fine |
| Adding an engine | A new scraper and browser session | One more key in the surfaces array |
AgentGEO is the answer-access layer; the ranking, alerting or dashboards you build in PHP on top of it are yours. You own the raw data and ship it inside your own app 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. When the consumer is an AI agent rather than a PHP app — Claude Code, Cursor, Codex or any MCP client — one command wires it up, and the agent pulls 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 Laravel and WordPress teams adding AI-visibility tracking to an existing product — a queued job that fans a keyword set across six engines and writes citations to MySQL is an afternoon of PHP — plus agencies white-labeling GEO/AEO reporting for clients. If you compared closed dashboards like Profound and wanted the raw data underneath, or you were sketching your own scraper, 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 Panther or a headless browser to track AI answers in PHP?
- No. AgentGEO's managed scrapers run server-side, so the whole integration is a
curlPOST tohttps://api.agentgeo.org/v1/fetcheswith aBearer ag_live_key. There is no Chrome to launch, no browser process to reap and no proxy pool to rotate — the failure modes of browser automation never enter your codebase. - Does it work on shared hosting?
- Yes. It is a single outbound HTTPS request with the
curlextension every PHP host bundles, so there is no long-running browser to fight the FPM request lifecycle, nomax_execution_timeblowups from a headless scrape and nomemory_limitpressure from a Chrome per query. If your host can call an API, it can call this one. - Can I use Guzzle instead of the curl extension?
- Yes — they are interchangeable here.
$client->post('https://api.agentgeo.org/v1/fetches', ['headers' => ['Authorization' => 'Bearer ag_live_...'], 'json' => ['query' => '...', 'surfaces' => ['chatgpt']]])returns the same JSON. Use whichever your project already depends on; the response handling is identical. - How do I use it from Laravel or WordPress?
- In Laravel, wrap the call in a service class and run it from a queued job or a scheduled Artisan command —
Http::withToken('ag_live_...')->post(...)works out of the box. In WordPress,wp_remote_post()with the same headers and body does the job inside a cron event. Either way you get the same structured records back. - Which AI engines does the PHP API cover?
- Six: ChatGPT, Perplexity, Google AI Overviews, Google AI Mode, Copilot and Gemini. The request and response shape is identical across all of them, so adding an engine to your tracker is one more string in the
surfacesarray — no new scraper, no new parser.
Keep reading