A Claude/agent skill (and standalone Python scanner) that audits how discoverable and citable a website is to AI search engines — ChatGPT Search, Microsoft Copilot, Perplexity. This is Generative Engine Optimization (GEO), also called AI SEO, AEO, or LLM SEO.
The one idea that makes it work: OpenAI's crawlers (
OAI-SearchBot,ChatGPT-User) — and most AI answer engines — do not execute JavaScript. So a plain no-JS HTTP fetch (curl/urllib) sees exactly what the AI crawler sees. A page whose content is painted client-side arrives blank to the model, no matter how good it looks in a browser or to a JS-rendering SEO tool. This scanner fetches asOAI-SearchBotwith no JS, so it reports the crawler's real view.
Audited top-down; you fix the highest failing block first.
| Block | Question | Signals checked |
|---|---|---|
| 0 · Access | Can the crawler reach & read it? | robots.txt allows OAI-SearchBot/ChatGPT-User/GPTBot, indexability, JS-only vs server-rendered |
| 1 · Retrieval | Will Bing rank it? (ChatGPT leans on Bing's index) | structured data, Bing indexation, entity/off-site mentions |
| 2 · Citability | Will the model pick it out of the results? | extractable body text, answer-first, statistics, quotes, headings, freshness |
Works with Claude Code, Codex, Cursor, OpenCode and 70+ agents via the
skills CLI:
# add to the current project
npx skills add hashd1ve/geo-audit-skill
# or install globally, targeting Claude Code
npx skills add hashd1ve/geo-audit-skill --global --agent claude-codeOr copy it in manually:
cp -r skills/geo-audit ~/.claude/skills/geo-audit # user-level
cp -r skills/geo-audit .claude/skills/geo-audit # project-levelA packaged .skill bundle is in dist/ for setups that install from one.
Once installed, just ask your agent: "run a GEO audit on example.com", "why doesn't ChatGPT cite my site?", or "how do I get my content into AI answers?" — the skill triggers, runs the scanner, interprets the 3 blocks, and returns a prioritized fix list.
No dependencies — Python 3.8+ standard library only.
python3 skills/geo-audit/scripts/geo_audit.py example.com
python3 skills/geo-audit/scripts/geo_audit.py example.com --sample 12 --json out.json --html report.html
python3 skills/geo-audit/scripts/geo_audit.py example.com --pages /,/pricing,/blog/post-aExample output (abridged):
GEO READINESS AUDIT https://example.com
Block grades Access D Retrieval B Citability B
Sitemap 822 URLs
URL shape /t x814, /research x5, /(home) x1
Sampled 6 pages | 4 JS-only | 2 content-rich
BLOCK 0 - ACCESS
[PASS] robots.txt / OAI-SearchBot allow: / [explicit rule]
[FAIL] Rendering (JS vs raw HTML) 4/6 sampled pages are JS-only shells
(<120 body words, no <p>). BLANK to AI crawlers.
--html writes a shareable, theme-aware report; --json writes full machine-readable
results.
geo-audit-skill/
├── README.md
├── LICENSE
├── dist/geo-audit.skill # packaged bundle
└── skills/geo-audit/
├── SKILL.md # skill instructions + workflow (what the agent reads)
├── scripts/
│ └── geo_audit.py # the scanner (stdlib only)
└── references/
├── methodology.md # how AI search works, the 3 blocks, GEO lift weights
├── fixes.md # remediation playbook: exactly what to touch
└── measurement.md # measuring GEO without spamming ChatGPT
- The retrieval layer (Bing ranking) is roughly deterministic and modelable — ~80% of most problems. The selection/citation layer (which candidate the model quotes) is stochastic and closed: you approximate it, you don't reverse-engineer it. No tool can promise a guaranteed citation.
- Some Block 1 signals (live Bing rank, server-log crawl evidence) need external tools or access the scanner can't reach remotely — it flags these rather than guessing.
- The scanner reports what a no-JS crawler sees. A JS-rendering engine (Googlebot) may see more — that gap is precisely the point for AI search.
MIT — see LICENSE.