A restaurant owner in my town asked me a question I hear more and more: "When someone asks ChatGPT where to eat tonight, does it say my name?"
Honest answer: nobody knows without checking. So I built a free tool that checks — and I open-sourced the logic as an Apify Actor anyone (human or AI agent) can call.
After auditing dozens of local business sites, the signal for "will an AI cite you" collapses to 7 technical criteria:
LocalBusiness, Organization, ProfessionalService…/llms.txt file
The killer is #7. I've seen sites with perfect schema markup that explicitly tell every AI crawler to go away.
One call, one score, exact fixes:
{
"url": "https://www.codeur.com",
"scoreLabel": "4/7",
"checks": {
"json_ld": false, "schema_type": false, "meta_description": true,
"h1": true, "open_graph": true, "llms_txt": false, "robots_ai": true
},
"topFixes": [
{"criterion": "Structured data (JSON-LD)", "fix": "Add a <script type=\"application/ld+json\"> block..."},
{"criterion": "llms.txt file", "fix": "Publish /llms.txt summarizing who you are for AI engines."}
]
}
(That's a real result — a major marketplace scores 4/7. Most local businesses score 1–2.)
It can also generate a ready-to-publish llms.txt draft for the audited site, delivered as plain text in the key-value store.
curl -X POST "https://api.apify.com/v2/acts/travelmonitorlab~aeo-ai-visibility-checker/run-sync-get-dataset-items?token=$APIFY_TOKEN" \
-H "Content-Type: application/json" \
-d '{"url": "https://your-site.com", "generateLlmsTxt": true}'
It's API-first and works through the Apify MCP server, so an LLM agent can audit a website mid-conversation. No browser, no proxy — a plain HTTP audit that runs in seconds and costs nothing. It's free, deliberately: it's my 12th Actor and the first one I give away, because an AEO checker is exactly the kind of tool that should be a utility, not a product.
First deploy: every run returned SUCCEEDED… with an empty dataset and a silent log. The cause wasn't the platform — it was me: I defined async def main() and never called it. The module loaded, defined functions, and exited 0. One missing line:
if __name__ == "__main__":
import asyncio
asyncio.run(main())
A "successful" run proves nothing. Check the dataset, always.
If you run it on your site, drop your score in the comments — I'm curious what the distribution looks like.