Webappski is an AEO agency that measures client visibility with aeo-platform, its own open-source npm engine — clients can install it and reproduce the measurement grid themselves.
Every hosted AEO platform scores you with a model you cannot inspect. Here the scoring is the code you just installed: the answer arrives from the engine's official API, lib/ turns it into a number, and nothing happens in between on a vendor's server. Same brand, same query basket, same day — same grid. (Engines drift week to week, so a later run is a new measurement, not a contradiction; that is why every run is dated and kept.)
The receipts, unedited:
- On ourselves. Webappski's own agency brand is cited in 2 of 39 AI-answer cells — 13 buyer queries × ChatGPT / Gemini / Claude, measured 2026-06-14 — and we published the whole grid, including the thirty-seven cells that do not cite us: aeo-webappski-2026-06-14. A vendor who hides their own score is asking you to trust a number you cannot check.
- On a product we optimized. TypelessForm is present in 12 of 12 cells on the 11 July 2026 run, Unified Visibility Index 92/100 (11 direct mentions + 1 source-only citation — both count as Presence, see How we count visibility): aeo-typelessform-2026-07-11.
Both files are ordinary aeo-platform report output, produced by the three commands in the next section. Nothing in them was written by hand.
1. Set your API keys. Two keys are strongly recommended — any two of OpenAI, Gemini, or Anthropic let the tool cross-verify competitor mentions across two models (the pair below is just an example — use whichever two you have). One key is enough to start (any of the three) — competitor names just won't be cross-verified.
Perplexity is the exception. Its API is search-tuned, not a general classifier, so it can't power query validation or the competitor cross-check. A
PERPLEXITY_API_KEYonly adds a 4th answer-engine column atruntime — it doesn't count as one of your two keys, and it can't be your only key.
macOS / Linux (bash / zsh):
export OPENAI_API_KEY="sk-proj-..." # platform.openai.com/api-keys
export GEMINI_API_KEY="AIza..." # aistudio.google.com/apikeyWindows (PowerShell):
$env:OPENAI_API_KEY = "sk-proj-..." # platform.openai.com/api-keys
$env:GEMINI_API_KEY = "AIza..." # aistudio.google.com/apikey2. Run the three commands (same on every OS):
aeo-platform init # Setup — picks your tracking queries, writes .aeo-tracker.json
aeo-platform run # Audit — asks each AI engine your queries, records the answers
aeo-platform report --html # Report — builds report.html (+ report.md) and opens it in your browserThat's the whole loop — three commands, once a week. Not sure what each step is actually
doing? How the loop works, just below the flags, walks through
init → run → report in plain English.
Persistent keys (that survive a restart), Windows CMD, extra engines, and no-install npx
→ see the full quickstart below.
Short version; the full reference is further down. Every command runs fine with no flags — reach for these when you need them.
init
--yes --brand=X --domain=x.com --auto— non-interactive setup (CI / scripts); still auto-suggests the queries.--keywords="q1,q2,q3"— skip the AI suggester and bring your own 3 queries (zero LLM cost).--queries-only— re-pick queries without touching brand / domain / keys.
run
--json— machine-readable output for CI. Exit code says what happened:0stable ·1regressed ·2invisible ·3API errors.--regions=us,de,fr— run every query under each region (multiplies cost by region count).--replay— rebuild a summary from cached answers, zero API cost (offline).--force— proceed even if the query-validation gate flags something.--samples=5— ask each cell 5 times instead of once and report a confidence interval (default is one call per cell; cost scales with N). See How we count visibility.
report
--no-html— write Markdown only, skip the HTML + browser.--no-open— write both files but don't auto-open the browser.--public— strip internal cost figures + source paths for a shareable report.
aeo-platform answers one question: when someone asks ChatGPT, Claude, Gemini, or
Perplexity to recommend tools in your space, does your brand come up? It sends your
queries to each engine through their official APIs, checks whether you're mentioned (and
who's mentioned instead), and turns the result into a browser report with a visibility
score and a prioritized to-do list.
The three commands above, in plain English — no AEO background needed. Read this once and you know everything the tool does for you.
init(run once) — reads your site, detects your API keys, and auto-picks 3 tracking queries: unbranded, buyer-intent phrases, the way a real customer searches when comparing vendors — e.g. "best voice form filler software", "top X tools for Y" — not your brand name and not "what is X" questions. Everything lands in.aeo-tracker.json.run(run weekly) — sends each query to every engine you have a key for and records, per answer: were you mentioned, roughly where, who was mentioned instead, and which sources got cited. Raw answers + a summary go underaeo-responses/.report— turns the latest run into a self-containedreport.html(auto-opens in your browser) plusreport.md: visibility score, per-engine breakdown, top competitors, cited sources, and 3–5 prioritized fixes. Compare weeks withaeo-platform diff.
Run it to — find out whether AI recommends you today · track that number week over week · get a concrete list of what to fix to get cited more often.
aeo-platformis the open-source CLI for answer-engine optimization (AEO / GEO). It measures your brand across ChatGPT, Claude, Gemini, and Perplexity, audits AI-bot crawlability + authority signals, and exports a JSON brand-context you paste into any AI for a personalised 30-mission AEO plan. MIT-licensed. Runs locally. Zero runtime dependencies. Free alternative to Otterly, Profound, Peec, and Bluefish.
macOS / Linux (bash / zsh) — npx …@latest always runs the newest release, nothing to keep updated:
npx aeo-platform@latest init --yes --brand=YOURBRAND --domain=YOURDOMAIN.COM --auto \
&& npx aeo-platform@latest run \
&& npx aeo-platform@latest reportPrefer a global install for a weekly rhythm? npm install -g aeo-platform and use bare aeo-platform … — the CLI prints its version on every command and tells you when a newer release is out (one cached check a day against the npm registry; opt out with AEO_NO_UPDATE_CHECK=1).
Windows (PowerShell)
npx aeo-platform@latest init --yes --brand=YOURBRAND --domain=YOURDOMAIN.COM --auto
if ($LASTEXITCODE -eq 0) { npx aeo-platform@latest run }
if ($LASTEXITCODE -eq 0) { npx aeo-platform@latest report }Windows (CMD)
set OPENAI_API_KEY=sk-proj-...
set GEMINI_API_KEY=AIzaSy...
npx aeo-platform@latest init --yes --brand=YOURBRAND --domain=YOURDOMAIN.COM --auto && npx aeo-platform@latest run && npx aeo-platform@latest reportNote:
&&chain works in CMD and PowerShell 7+, but not in PowerShell 5.1 (the default Windows 10/11 shell — check via$PSVersionTable.PSVersion). For persistent env vars across sessions on Windows, see the Full quickstart below. Git Bash and WSL users — the bash block above works as-is.
Already know your 3 target queries? Skip the LLM auto-suggest pipeline (zero LLM cost, BYO mode added as first-class in 1.0.3):
aeo-platform init --yes --brand=YOURBRAND --domain=YOURDOMAIN.COM \
--keywords="best X for Y,top X 2026,X vs alternatives"Use category-based phrasing («best X for Y» / «top X 2026») the way real users search — the strict commercial-only validator blocks brand-comparison archetypes like «brand vs alternatives» that LLMs auto-correct away for new brands.
Renamed from
@webappski/aeo-trackerin1.0.0(2026-05-13). Theaeo-trackerCLI command stays as a built-in alias — existing scripts keep working. Migration:npm i -g aeo-platform.
Six concrete reasons aeo-platform exists, in order of how often they decide the install:
- Measures 4 engines via official APIs — ChatGPT (
gpt-5.6-luna+ the Responsesweb_searchtool), Claude (claude-sonnet-5), Gemini (gemini-3.8-flash), Perplexity (sonar-reasoning-pro). No scraping. No proprietary score. - Local-first. Raw responses stay on your disk in
aeo-responses/<domain>/YYYY-MM-DD/. No telemetry. No traffic to webappski.com. API keys read fromprocess.env, never written to disk. OpenAI Responses web-search calls sendstore:falseto disable Responses application-state storage; this is not a claim of Zero Data Retention, and provider abuse-monitoring retention remains governed by your provider account. The only non-provider network call is an update check againstregistry.npmjs.org(the host npm itself talks to) — at most once a day, nothing sent, skipped in CI/non-TTY, opt out withAEO_NO_UPDATE_CHECK=1. - CI-grade. Exit codes
0/1/2/3(stable / regressed / invisible / providers errored).--jsonstdout. Cron-friendly. - Zero runtime dependencies.
"dependencies": {}inpackage.json. Vanilla Node 20+. The report is a single self-contained HTML file (~390 KB — about 170 KB of that is the embedded variable fonts that let it render identically offline, with zero CDN calls). - MIT. Fork it, embed it, ship it inside a paid product — your choice.
Webappski is an AEO agency that measures client visibility with aeo-platform, its own open-source npm engine — clients can install it and reproduce the measurement grid themselves. The agency is based in Gdynia, Poland, and works in English, German, Polish and Russian. This package is not a side project it left behind: it is the engine behind the client audits. When Webappski measures a client's AI visibility, the grid in that client's report comes out of this repository, at the version stamped inside the report itself (the 2026-06-14 grid above says v1.3.2).
That has one consequence worth stating plainly, because no hosted AEO platform can offer it: the client can audit the auditor. Install the package, point it at your own domain, and you are running the exact code path that produced the grid you were sent — the query validation, the engine calls your keys allow, the competitor cross-check, and the scoring in lib/. There is no vendor-side step to take on trust.
Three things to keep honest about that:
- What is reproducible is the measurement. A full client audit also carries on-page findings and a written roadmap around the grid — human work, not tool output, and nothing you can re-derive by installing a package.
- The free instant check on the agency's site is a deliberately reduced version — three questions, two engines, no API keys asked of you. The full four-engine run, the crawlability audit, the authority-signal pass and the 30-mission plan are what this repository does.
- Running it against your own brand costs you your own API spend, a few cents per week. Webappski earns nothing from your runs, and the tool sends nothing to webappski.com.
The agency's site is webappski.com, and the reduced hosted check described above lives at webappski.com/en/aeo-audit — free, no account, no card. This README carries no prices and no sales call-to-action by design: commercial detail belongs on that site, not in an MIT repository you were invited to fork.
The recommended pair above (OpenAI + Gemini) covers the ChatGPT and Gemini columns with cross-model verification. Minimum to start: any ONE research-capable key (OpenAI, Gemini, or Anthropic) — single-key mode runs the same pipeline on one model and marks competitor mentions as unverified. Two more keys are optional and each adds an engine column to the report:
# macOS / Linux
export ANTHROPIC_API_KEY="sk-ant-..." # adds Claude column
export PERPLEXITY_API_KEY="pplx-..." # adds Perplexity column# Windows PowerShell — current session
$env:ANTHROPIC_API_KEY = "sk-ant-..."
$env:PERPLEXITY_API_KEY = "pplx-..."
# Windows PowerShell — persistent (User scope, requires terminal restart)
[System.Environment]::SetEnvironmentVariable('ANTHROPIC_API_KEY','sk-ant-...','User')
[System.Environment]::SetEnvironmentVariable('PERPLEXITY_API_KEY','pplx-...','User'):: Windows CMD — current session
set ANTHROPIC_API_KEY=sk-ant-...
set PERPLEXITY_API_KEY=pplx-...
:: Windows CMD — persistent (requires terminal restart)
setx ANTHROPIC_API_KEY "sk-ant-..."
setx PERPLEXITY_API_KEY "pplx-..."Get keys at: platform.openai.com/api-keys, aistudio.google.com/apikey, console.anthropic.com/settings/keys, docs.perplexity.ai.
Never used a CLI before? A founder-friendly walk-through (5 minutes, no terminal background required) is in the Full quickstart collapsible below.
Every aeo-platform report writes two files in aeo-reports/<domain>/<date>/:
report.md— markdown with inline SVG charts. Renders on GitHub, Notion, VSCode preview, email. Perfect for CI logs and PR comments.report.html— single-file editorial bento layout, ~390 KB (≈170 KB of which is embedded variable fonts), works offline fromfile://, zero CDN, zero JS dependencies, zero tracking pixels.
The HTML report has:
| Section | Surfaces |
|---|---|
| Hero | UVI (Unified Visibility Index) · mention rate · lift opportunities · top competitor · ⓘ popover with per-axis math |
01 Overview |
8-week score trend · listicle-pitch KPI · topic-cluster bars · top-3 actionable gaps preview |
02 Visibility |
Per-engine cards · query × engine matrix (Mention / Position / Sentiment lenses) · region breakdown when --geo is used · verbatim quotes |
03 Competitors |
Most-named brands · 4-axis radar (presence / sentiment / rank / mentions) vs top-3 competitors |
04 Citations |
Domain share-of-voice (own-domain marker) · category breakdown · top-cited publishers |
05 Diagnostics |
AI-Bot Crawl Readiness · authority presence (Wikipedia / Reddit / GitHub) · per-engine session cost · region indicator · UTM citations · AI-ad detector |
06 Actions |
5 ordered moves (badges: FIX GAP / LOCK IN WIN / COMPETE / DEFEND) with specific competitors to displace and URLs to pitch |
| Bridge card | Copy-prompt button → 30-mission paste-into-AI plan |
Each surface is grounded in actual run data: specific competitors named by this run, specific URLs cited by AI, specific gaps you can act on this week.
After measuring you across 4 engines, aeo-platform report exports a JSON brand-context block with everything the AI needs to write a grounded plan: visibility index, per-engine citation deltas, top competitors, citation gaps, crawl matrix, authority signals, page signals, entity graph, region, freshness, competitor pricing tier.
Paste that JSON into your own ChatGPT / Claude / Gemini / Perplexity (any frontier model — same chat subscription you already pay for, no extra API spend). Ask: "give me a 30-mission plan to be cited more". The answer is keyed to your specific gaps — named competitors from topCompetitors, URLs from topCanonicalSources, weakest-engine fortification, citation-gap closure.
Workflow:
aeo-platform report— opens HTML report in browser.- Find the section Your AEO action prompt. Click the one-tap Copy button.
- Paste into your ChatGPT, Claude, Gemini, or Perplexity chat.
- Receive a 30-mission plan: 30 actions × ≈1–3 hours each, grouped into 4 weekly chunks, every action references a specific competitor / URL / engine / gap from the data.
Real run for typelessform.com — the file the whole pipeline exists to produce:
UVI 42% (5/12 cells), leading 8 named competitors by mentions but missing on 7; strongest on ChatGPT (67% / 67%), weakest on Perplexity (33% / 0%).
| # | Action | Expected outcome | Time |
|---|---|---|---|
| 3 | Add 40–60 word answer capsule under each H2 lacking one (coverage 1/9) | Multiplies extractability across all 12 engine cells in one pass | 90 min |
| … | 28 more missions, each tied to a specific finding in the audit | ||
| 30 | Re-run aeo-platform to measure delta; AMA in strongest sub from new topSubreddits |
Quantifies progress; doubles down on warmest Reddit surface | 120 min |
Full plans: sample-plan-typelessform.md (established brand at UVI 42%) · sample-plan-output.md (bare-site brand at 0%).
Why no hosted AEO dashboard ships a portable one: a paste-into-AI plan cannibalises the dashboard moat. Once the user takes the JSON to their own AI chat, the vendor's UI is no longer the destination. Hosted tools do generate action plans — Waikay meters them by the unit, "up to 30 / 90 / 220 AIO Action Plans" depending on tier (checked 4 September 2026) — but they generate them inside the dashboard you are renting, and the plan stops when the subscription does. Open-source has the opposite incentive: show zero when it's zero, hand you the data, win when you take it wherever you want.
aeo-platform calls four AI answer engines via their official REST APIs in a single run: ChatGPT (gpt-5.6-luna + the Responses web_search tool), Gemini (gemini-3.8-flash), Claude (claude-sonnet-5), Perplexity (sonar-reasoning-pro). OpenAI + Gemini keys are recommended (they power the two-model hallucination filter); any ONE research-capable key (OpenAI, Gemini, or Anthropic) is enough to start in single-key mode — competitor mentions are then marked unverified. Claude + Perplexity add optional columns. Browser-only surfaces (Perplexity Pro UI, ChatGPT Pro personalisation, Claude.ai chat) are covered via run-manual paste mode that merges into the same _summary.json.
| Engine | Default model | API path | Web-search grounding | Required key |
|---|---|---|---|---|
| ChatGPT (OpenAI) | gpt-5.6-luna |
direct REST | yes (Responses web_search tool) |
OPENAI_API_KEY |
| Gemini (Google) | gemini-3.8-flash |
direct REST | optional (request flag) | GEMINI_API_KEY |
| Claude (Anthropic) | claude-sonnet-5 |
direct REST | optional (request flag) | ANTHROPIC_API_KEY |
| Perplexity | sonar-reasoning-pro |
direct REST | always | PERPLEXITY_API_KEY |
OpenAI + Gemini keys are recommended (two-model competitor extractor: GPT-5-nano + Gemini-2.5-flash cross-check filters hallucinated brand mentions). Minimum: any ONE of OpenAI / Gemini / Anthropic — single-key mode runs the extractor on one model and marks competitor mentions unverified. Perplexity is optional — adds a column.
For engines whose API tier you can't access (Perplexity Pro browser, ChatGPT Pro UI personalisation, Claude.ai UI), use manual paste mode:
# macOS / Linux
mkdir perplexity-responses
# paste UI answers into perplexity-responses/q1.txt, q2.txt, q3.txt
aeo-platform run-manual perplexity --from-dir ./perplexity-responses# Windows PowerShell
New-Item -ItemType Directory perplexity-responses
# paste UI answers into perplexity-responses\q1.txt, q2.txt, q3.txt
aeo-platform run-manual perplexity --from-dir .\perplexity-responsesWindows note: save your
q1.txt/q2.txt/q3.txtfiles as UTF-8 without BOM. Notepad's default («ANSI» or «UTF-8 with BOM») leaves an invisible byte at the file start that can affect mention detection. In Notepad: File → Save As → Encoding: UTF-8 (NOT «UTF-8 with BOM»). VSCode and Notepad++ default to UTF-8 without BOM.
Results merge into today's _summary.json alongside API runs. diff and report treat both identically.
Every number in the report comes from a rule you can read in this repository. This section states the rules that decide the score: how many times each engine is asked (once per cell per run, by default), what counts as a mention, and how each axis is derived. Two neighbouring sections stay the authority for the rest and are not repeated here — what this measures and what it does not for scope, and UVI methodology for the weights.
A cell is one combination of query × engine × region × pass. aeo-platform run makes exactly one API call per cell. Three queries against three engines is nine answers — one answer per cell. The tool does not ask the same question twice and average the results.
Say plainly what that means for the number. Language models are non-deterministic: the same question, to the same engine, an hour later, can name a different set of tools. A default run is therefore a point estimate with unknown spread — not a measurement carrying an error bar. Two runs on the same day can honestly disagree, and a single cell flipping from no to yes is not a trend.
If you need the error bar, ask for it:
aeo-platform run --samples 5--samples N queries every cell N times and keeps each trial in _summary.json (results[].trials[]), with the cell's own hit rate under results[].presence. The report then pools the sampled cells into one Wilson confidence interval on the Presence axis — share of cells where brand was mentioned · 12/15 trials · 95% CI [62%, 96%]. aeo-platform diff uses the same statistics for its regression verdict: when both runs sampled a cell, a flip whose intervals overlap is classified as noise and dropped rather than reported as a change, so one jittery trial cannot trip the exit-1 regression code. When either side is single-shot there is no distribution to test, and the flip is reported as before, tagged point-estimate. Cost scales roughly N×, which is why the default is 1 and the flag is capped at 25. Five is a sensible starting point when a decision depends on the number.
Each answer gets exactly one label (lib/mention.js):
| Label | What it means |
|---|---|
yes |
your brand name, one of your configured aliases, or your domain appears in the answer body |
src |
your brand appears only inside a cited source URL — not in the text a reader sees |
no |
absent under every spelling checked |
yes and src both count as one for Presence. Being cited as a source counts as being visible. That is the least obvious scoring decision in the tool, so it is stated rather than buried.
Name matching is case-insensitive and separator-tolerant — gcore matches Gcore, G-Core, G Core, (Gcore) — and anchored on word boundaries, so it does not fire inside a longer word (gcorehouse) or across a seam (a bi**g core** network). Three limits, stated rather than hidden:
- No fuzzy matching. An engine that misspells your name counts as an absence until you add that spelling to
brandAliasesin.aeo-tracker.json. - The domain is matched as a plain substring, without word anchoring — so a longer host that contains your domain string would register as a mention.
- A dot is significant. A brand configured as
Node.jsneeds the literalnode.js;nodejswill not match it.
- Presence — share of non-error cells labelled
yesorsrc. A cell that errored (bad key, rate limit, provider outage) is dropped from the denominator entirely; it is not counted as an absence. Under--samples Na cell contributes its fraction of hits (0.667 for two of three trials) rather than a flat 1 or 0, while its headline label stays the most common outcome of the trials, breaking ties towards the stronger reading (yesoversrcoverno). - Sentiment — two cheap classification-tier models, one per provider, resolved at run time, score each mentioning cell independently. Both agree, the label stands at high confidence; they disagree, the label degrades to neutral at low confidence; one fails, the other's label is used and marked single-model. The axis then averages the surviving cells at
positive = 100,neutral = 50,negative = 0. A low-confidence neutral is dropped, not averaged in as a 50 — a tie between two disagreeing models records "no signal", not "a middling opinion". Cells that never mentioned you carry no sentiment at all and never enter this axis. - Rank — an integer only when the answer is a structured list of at least three numbered or bulleted items and the mention sits inside one of those items. Prose answers get an ordinal from a classification-tier model instead, carried at lower confidence and multiplied by 0.7 before it enters the average, so an explicit list position always outweighs a prose one. When no cell yields a usable position, the rank axis is excluded and the remaining weights re-normalise — it is never filled with a zero or a 50.
- Citation — cells where your own domain appears among the answer's cited sources, matched at the registered-domain level, so
blog.yourbrand.comcounts as yours.
Model IDs are discovered live from each provider at run time; the values in .aeo-tracker.json are fallbacks, not promises. The ID that actually produced each answer is stamped into _summary.json, and a run prints a warning when a provider serves a different model lineage than the one requested (--strict-model-pin turns that warning into a failed run, for a frozen basket you want kept comparable month over month). Read the served ID, not the configured one.
Scope is covered in the next section — engine APIs rather than the consumer apps, and no coverage of Google AI Overviews / AI Mode or Microsoft Copilot. Four more things this tool never claims to know:
- How many real people ask these questions. The basket is the one you chose. It carries no search-volume or demand signal.
- Your position in classic Google search. Different surface, different tool.
- Why the number moved. A score that rises after you shipped a page is a correlation. The tool records what changed, not what caused it.
- What an engine will answer tomorrow. Every score is a reading of one moment on one surface.
Be precise about scope: aeo-platform queries each engine's API surface with your own keys. That is a reproducible, auditable proxy you can re-run and put in CI — but it is not the same thing a human sees in the consumer app. The consumer apps use a different retrieval pipeline, can serve a different model version, and add personalization and locale that the API does not. Treat the score as "how the engine's API answers your queries", not "exactly what a user of chatgpt.com sees".
Each run records this in _summary.json under measurement ({ "surface": "api", "disclaimer": "…" }), and the report header shows it.
| Engine measured (API) | What we call | Is NOT the same as |
|---|---|---|
OpenAI gpt-5.6-luna + Responses web_search |
direct REST, search-grounded | chatgpt.com (Pro personalisation, memory, plugins) |
Perplexity sonar-reasoning-pro |
direct REST, always grounded | perplexity.ai Pro browser UI |
Gemini generateContent + grounding |
direct REST | the Gemini app |
Anthropic claude-sonnet-5 |
direct REST (optional column) | claude.ai chat |
Not covered at all (no first-party query API): Google AI Overviews / AI Mode and Microsoft Copilot. A run reports zero signal for these because the tool never queries them — their absence from the score is a coverage gap, not evidence you are invisible there. For the browser-only surfaces above, use run-manual paste mode (previous section) to fold a real UI answer into the same _summary.json.
A Google AI Overviews connector is on the roadmap (not built yet).
The crawlability audit scores your domain against the 12-bot AI-crawler matrix (GPTBot, ClaudeBot, PerplexityBot, Google-Extended, and 8 others) using ~3 HTTPS GETs against your /robots.txt, /sitemap.xml, and /llms.txt. No LLM calls, no auth, no cost. The composite AI-Bot Crawl Readiness score (0–100) weighs robots posture 30%, bots-not-blocked 25%, sitemap 25%, and whether your homepage content is in the served HTML 20%.
aeo-platform report runs a pure-HTTP audit of your own domain against the AI-crawler matrix. No LLM calls. Roughly 3 HTTPS GETs.
| Bot | Owner | Purpose | Does blocking it cost citations? |
|---|---|---|---|
GPTBot |
OpenAI | Training crawl | No — training only |
OAI-SearchBot |
OpenAI | ChatGPT Search indexer | Yes — opted-out sites «will not be shown in ChatGPT search answers» |
ChatGPT-User |
OpenAI | On-demand fetch when a user pastes a URL | No — user-initiated, so robots rules may not apply anyway |
Google-Extended |
Gemini training + grounding in other Google products | No — «does not impact a site's inclusion in Google Search» | |
GoogleOther |
General-purpose crawl | No — «doesn't affect any specific product» | |
ClaudeBot |
Anthropic | Training crawl | No — training only |
Claude-Web |
Anthropic | Legacy UA, absent from Anthropic's current bot doc | No |
anthropic-ai |
Anthropic | Legacy UA, absent from Anthropic's current bot doc | No |
PerplexityBot |
Perplexity | Indexer for Perplexity's own ~200B-URL index | Yes — that index is Perplexity's own, so being in Google or Bing does not carry you into it (publisher content also arrives via third-party crawlers under agreements, but that route is not open to an ordinary site) |
Perplexity-User |
Perplexity | On-demand fetch | No — user-initiated, generally ignores robots.txt |
CCBot |
Common Crawl | Used by OpenAI, Anthropic, others as training data | No — training only |
Bytespider |
ByteDance | Doubao / China-market AI | No |
Two more crawlers do gate citations and this audit does not probe them yet: Anthropic's Claude-SearchBot and Google's Googlebot (for Google, the levers are robots.txt, noindex and nosnippet — not Google-Extended). Check those by hand.
Each bot is mapped to allowed | blocked | partial | unspecified from your /robots.txt. sitemap.xml and llms.txt presence are also checked. The composite AI-Bot Crawl Readiness score (0-100) weighs robots 30% · bots-not-blocked 25% · sitemap 25% · content in the served HTML 20%.
llms.txt is measured but deliberately does not affect the score, and the report never tells you to add one. Google states the file «isn't needed for AI Overviews, AI Mode, or other generative AI Search features»; a 2026 study across ~300,000 domains, reported by lumentir.com, found no relationship between having the file and how often a domain is cited; no major provider has confirmed support. It is reported as a fact so you can see the answer, nothing more. (Until 2026-08-02 the file carried 20% of this score and the report recommended creating one — that was wrong, and the change is written up in CHANGELOG.md, including why scores from before and after are not directly comparable.)
Note: this measures technical access — not actual answer-pool inclusion. Answer-pool inclusion is driven by off-page authority (Wikipedia, Reddit, listicles, review platforms) — covered in the next section.
Authority signals check the off-page surfaces AI engines weight heavily when picking who to cite: Wikipedia article presence + length, Reddit mention count, GitHub repo stars/forks (auto-surfaced for dev-tool brands with disambiguation guard), and Wikidata Q-ID + sameAs reciprocity. All four use free public APIs — no auth required (optional GITHUB_TOKEN lifts GitHub rate from 60/h to 5000/h).
aeo-platform report checks the off-page surfaces AI engines weight heavily when deciding who to cite. Free public APIs only — no auth.
| Source | What's checked | Method |
|---|---|---|
| Wikipedia | Article exists for your brand? Disambiguation page? Length? | Wikipedia REST API |
| Mention count in posts + comments referencing your brand | Reddit search JSON | |
| GitHub | Repo exists under your namespace? Stars / forks? (auto-surfaced for dev-tool brands; disambiguation guard prevents wrong-repo matches for popular names) | GitHub REST API; optional GITHUB_TOKEN env lifts 60/h → 5000/h |
| Wikidata | Q-ID present? sameAs chain reciprocal? |
Wikidata SPARQL |
Why this matters: in Webappski's 2026 weekly audits, brands with a Wikidata entity, named-author sameAs chains, and presence on Reddit/G2/Wikipedia consistently outperform on AI Overview citation rates compared to brands relying on domain authority alone. Entity signals and citation-source presence are the highest-ROI surfaces to fix.
UVI (Unified Visibility Index) is a 0–100 composite of four AI-answer signals: Presence 35% (mentioned cells / non-error cells), Sentiment 25% (average tone of mentioning cells, positive 100 · neutral 50 · negative 0), Rank 20% (normalised average rank position), Citation 20% (cells where your domain was cited as a source). Weights are visible in lib/report/visibility-index.js; sub-components with insufficient data are excluded and remaining weights re-normalise — never phantom values. Sample size is published alongside the score.
aeo-platform rolls four AI-answer signals into a single 0-100 composite. Every weight is in the source (lib/report/visibility-index.js); the ⓘ popover next to the hero number shows the per-axis math on every run.
| Sub-component | Weight | What it measures |
|---|---|---|
| Presence | 35% | Cells where your brand was mentioned (yes/src) out of total cells |
| Sentiment | 25% | Average tone of the cells that mention you (positive 100 · neutral 50 · negative 0), over cells whose label carries signal — see how we count visibility |
| Rank | 20% | Average rank position when mentioned, normalised 0-100 |
| Citation | 20% | Cells where your domain was cited as a source |
Sub-components with insufficient data (e.g. zero rank positions in a first run) are excluded; remaining weights re-normalise and the popover flags the re-norm. No phantom values. Sample size is published alongside the score (n=K high-confidence cells).
A low or zero score is a question to investigate, not a conclusion to act on. The tool measures exactly what it asked — and a score is only as trustworthy as the basket behind it. Before you treat a 0% as "AI does not know my brand", confirm two things the tool now surfaces for you in the report's "How representative is this score?" panel:
- Did the raw answer mention your brand under every spelling? A naive match misses
G-Corewhen you trackedGcore, or a brand cited only inside a source URL. The tool checks aliases and separators and shows the exact sentences engines produced (the "What AI engines actually said" section) — read them before trusting a 0. If you see the brand there but the score is 0, that is a matching gap to report, not invisibility. - Does the basket cover the field where your brand actually competes? A CDN brand measured only on "VPC for healthcare" will score 0 — not because it is invisible, but because the basket asked about ground it does not play on. The report's coverage line ("your queries touch X of N product lines") and the small-sample warning are there to catch this. A headline driven by off-target or too-few queries is an artefact of the basket, not a verdict on the brand.
This is the same discipline the tool applies to itself: a number without provenance is a guess. Re-run with a corrected basket (aeo-platform init --queries-only --add-queries preserves your trend history) before drawing conclusions. Only a 0% on a basket that covers your real product lines, checked against the raw answer text, is evidence of an AEO gap worth acting on.
| Tool | Pricing model | Open source | Raw data stays local | Portable paste-into-AI plan |
|---|---|---|---|---|
aeo-platform |
Free + your own API spend | MIT | Yes | Yes — the only engine in this table that ships one |
| Ahrefs Brand Radar | Paid SEO-suite add-on | No | No | No |
| AirOps ✳ | Freemium platform — AI-search visibility is one module beside content production and agents | No | No | No |
| AthenaHQ | Paid subscription | No | No | No |
| Bluefish | Enterprise contract | No | No | No |
| Discovered Labs | Managed-service retainer | No | No | No |
| Evertune | Custom contract | No | No | No |
| Goodie | Paid subscription | No | No | No |
| HubSpot AI Search Grader ✳ | Free one-shot grade; HubSpot's ongoing AEO monitoring from $50 a month | No | No | No |
| LLMrefs ✳ | $79 a month, 7-day trial | No | No | No |
| Otterly ✳ | Paid subscription from $29 a month | No | No | No |
| Peec.ai | Paid subscription | No | No | No |
| Profound | Paid subscription | No | No | No |
| Scrunch AI ✳ | Sales-led — 7-day trial, no public price | No | No | No |
| Semrush AI Visibility | Paid SEO-suite add-on | No | No | No |
| Superlines ✳ | Paid subscription, price on request | No | No | No |
| Trakkr ✳ | $100 a month for one brand · $500 a month for ten | No | No | No |
| Waikay ✳ | $69.95 / $199.95 / $449.95 per month | No | No | No — meters plans by quota, generated in-dashboard |
| Webappski (agency — the maintainer of this project) | Monthly retainer or one-off project, invoiced | MIT — the same engine as row 1 | No for an audit we deliver — it runs on our infrastructure; Yes if you run the CLI yourself | Yes — this engine's export, handed to the client |
Two of the 19 rows in this table are ours, and the last two columns need a qualifier the cells cannot hold. aeo-platform is this repository; Webappski is the agency that maintains it and delivers audits with it. The agency row is here because two other agencies already are — Discovered Labs and Evertune — and a table that grades everyone else while leaving out its own author is not a comparison. It also fixes what the wedge claim means, so read that claim precisely: no hosted vendor ships a portable plan is a statement about the 17 third-party products in this table, and the two Yes cells in the last column trace back to one engine — this CLI, and the agency running the same CLI for a client. On raw data stays local the honest answer for the agency row leads with No: the free audit on webappski.com executes in Firebase Cloud Functions on Webappski's own provider keys, and an audit we deliver is run by us, on our side — either way the raw answers land in our infrastructure, not on your disk. Run the CLI yourself, row one, and they never leave your machine. That is the same distinction the AirOps row draws below: whose machine, not whose key. One number to keep apart while reading on: the 19 rows here and the live re-check of 19 tools stamped in the next section are different nineteens — the stamp counts tools whose own pages we read that day (8 ✳ hosted platforms, 8 open-source peers, 3 projects excluded with the reason printed), and neither of our rows is among them. The pricing cell is the shape published on webappski.com/en/pricing, read on 7 September 2026 — both paths are invoiced, and no Webappski price appears in this README by design.
✳ = re-checked live on 4 September 2026; the linked page is the page that was read. It marks third-party rows only — our own two rows carry no ✳, because "we read the vendor's page" is not a claim we can make about ourselves. Unmarked third-party rows keep the pricing shape recorded in our July 2026 review of 23 AEO tools and had only their URL re-confirmed on the same day — read those price cells as a category, not as a quote. Two labels moved at that check and are corrected above: HubSpot's tool now ships as AI Search Grader (it was the "AEO Grader") and Semrush's surface now reads AI Visibility. Adobe LLM Optimizer is on our watch list but is deliberately absent from the table — its product page timed out twice on 4 September 2026, and an unreachable page is unknown, not absent.
To be fair about where the hosted tools are genuinely ahead. Engine breadth is the clearest gap: aeo-platform calls four engines and deliberately does not scrape Google AI Overviews or AI Mode, while Superlines reads 10+ surfaces, LLMrefs 11, Trakkr 8 (including Meta AI and DeepSeek) and Otterly 7 (including AI Overviews and AI Mode) — all counts from their own pages on 4 September 2026. Trakkr and Superlines each ship an MCP server and a hosted API; Trakkr adds white-label client portals on its $500 tier. AirOps is a different animal again, wrapping visibility inside content production and pipeline attribution — and it is the one row where the "raw data stays local" No deserves a sentence rather than a word: AirOps does let you supply your own provider keys on its paid plans, which removes the vendor's model markup but not the vendor's servers, since the runs still execute on their infrastructure. Bring-your-own-key is not the same property as bring-your-own-machine, and this column measures the second one. And HubSpot AI Search Grader returns a score in under two minutes with nothing to install. aeo-platform does none of that, and none of it is on the roadmap — it trades breadth and polish for a run whose every number you can read in lib/.
Pick something else when. Three questions; one "yes" and a row above serves you better than this CLI:
- Does the result need to reach people who will never open a terminal, on a schedule? Then you want seats, alerting and SSO — Profound or Peec.ai.
aeo-platformwrites an HTML file; nothing emails it for you. - Do Google AI Overviews or AI Mode have to be inside the number? Then you need a vendor that goes and gets them — Otterly (7 surfaces) or Superlines (10+). This tool covers the four engines reachable by official API and says so instead of estimating the rest.
- Does procurement need SOC-2, a DPA and an invoice? Then you need a company, not an MIT repo — Bluefish, or AirOps at its enterprise tier.
If all three are "no", the rest of this README is written for you.
Pick aeo-platform when: indie founders, small AEO / GEO agencies, dev-centric teams who prefer CLI + CI integration, anyone who wants the portable paste-into-AI plan, anyone who can't justify a subscription for a tool whose direct-API cost is a few cents per week.
One axis the table cannot show: who checks the checker. Every third-party platform above is closed-source — see the column; the only MIT in it is this engine, on the two rows that run it. That means the score reaches you from a server you cannot enter, and you are trusting the vendor's definition of a mention, their competitor matching, and their weighting, none of which you can read. Here all three sit in lib/ in the copy on your disk, how we count visibility writes out the mention rule and the one-call-per-cell sampling behaviour, and the UVI methodology writes out the weights. It is also the axis on which an agency is judged: Webappski measures clients with this engine, so a client can install it and re-derive the grid they were sent. Transparency as a file you can open, rather than as a word on a landing page.
The open-source side of this category is real, crowded, and moving faster than we are on stars. Rather than name one peer and stop, here is the whole neighbourhood. Every cell below was derived live on 4 September 2026 — gh api repos/<owner>/<repo> for the stars, licence and last push, and the project's own README for what it takes to run and what it covers. Star counts are what GitHub served that day, not a cached number, and they include ours.
| Project | Stars | Licence | What it takes to run | Engines | Portable paste-into-AI plan |
|---|---|---|---|---|---|
aeo-platform (this repo) |
11 | MIT | npx aeo-platform — no server, no database, no container |
4 (ChatGPT, Claude, Gemini, Perplexity) | Yes |
| elmohq/elmo | 293 | MIT | Docker Compose + PostgreSQL you run, or their managed cloud from $29 a month | 9 named, incl. Google AI Mode and AI Overviews | No |
| danishashko/geo-aeo-tracker | 250 | MIT | Clone + npm install, local dashboard; bring your own keys |
6 | No |
| ai-search-guru/getcito | 182 | MIT — a derivative work of elmo, above | Docker Compose + PostgreSQL 16+, plus a scraping provider | 7, incl. Google AI Mode and AI Overview | No |
| aryamantodkar/oneglanse | 164 | MIT | Docker + PostgreSQL + ClickHouse; captures through the real product UIs rather than the APIs | 5, incl. AI Overview | No |
| Canonry/canonry | 129 | FSL-1.1-ALv2 — fair-source, converts to Apache-2.0 after two years, not OSI-approved at release | npm i -g @canonry/canonry, then a self-hosted single-tenant deployment |
4 | No |
| ansvisor/ansvisor | 106 | MIT | Self-host on Supabase, or their managed cloud | 8 | No |
| letterstory/lettertrace | 69 | MIT | Self-host Next.js + Supabase, Docker path documented; bring your own keys | 4 provider APIs | No |
| sharozdawa/ai-visibility | 9 | MIT | Clone + npm install + Prisma |
4 | No |
Read the fourth column before the second. Stars measure attention; that column measures the afternoon between you and your first number. Every other project above asks you to stand something up first — a Postgres, a Supabase project, a Docker Compose file, at minimum a clone and an install. npx aeo-platform writes to disk and is done, and that is the trade being made: they get a dashboard and a database you can query, we get no deployment step at all. If you want the dashboard, take one of theirs — elmo and ansvisor are both MIT and both actively shipping.
Activity, from the same check. elmo, canonry and lettertrace all pushed on 4 September 2026; getcito on 31 August; geo-aeo-tracker on 12 August; this repo on 2 September. Two are quiet: oneglanse has had no push since 10 May 2026 and sharozdawa/ai-visibility since 22 March 2026 — they are listed because engines still name them, not because we would bet a workflow on them.
Two naming traps worth knowing before you type an install command. The npm package literally called ai-visibility is not sharozdawa/ai-visibility — it is a robots.txt / llms.txt / JSON-LD generator for AI crawlers (npm view ai-visibility, 4 September 2026). And geo-aeo-tracker, oneglanse, getcito and ansvisor have no package under those names on the npm registry — all four returned 404 on that same check — so whatever a search result implies, you get them by cloning, not by installing.
Deliberately not in the table, with the reason. addyosmani/agentic-seo is the most-starred repo in this neighbourhood at 307, but it audits a site or a docs folder for agent readiness (npx agentic-seo ./my-docs-site) — it does not measure brand mentions across engines, so it is not a peer of the rows above. Same reasoning for mverab/eGEOagents (173), a GEO content-optimisation skills pack. ivannikov-pro/ai-visibility-tracker is a tracker by intent, but its README still marks self-hosting as "planned", the repo sits at 0 stars, and nothing has been pushed since 22 May 2026 — putting it in would pad the table rather than inform it.
The structural difference is still the plan. After measuring you across four engines, aeo-platform exports a JSON brand-context block you paste into any frontier AI chat and get back a 30-action plan keyed to your own gaps. In our July 2026 review of 23 tracked AEO tools, plus a live re-check of 19 tools on 4 September 2026, no other tracker — open or closed — shipped a portable one. Those 19, so the number can be audited rather than taken on faith: the eight open-source projects listed beside ours above, the three excluded in the paragraph before this one, and the eight ✳ hosted platforms whose own pages were read that day (AirOps, HubSpot AI Search Grader, LLMrefs, Otterly, Scrunch AI, Superlines, Trakkr, Waikay). None of the 19 is one of ours: this nineteen counts tools whose own pages we read, not rows in a table, so it is a different nineteen from the row count of the hosted table above. Semrush is deliberately not among them: its URL and current product name were re-confirmed, but its capabilities were not re-checked, so it carries no ✳ and is not counted. A further twelve tools logged by our Product Hunt scout on 2026-08-19 — LLM SEO Monitor, PromptSignal, Passionfruit Labs, Appear on AI, Promptmonitor, SEORCE, ClayHog, AI Visibility Rank Tracker, Rankfender, Citable, CrowdReply and Keupera — were not vetted in this pass, and are absent from both tables for that reason alone: their fit here is unknown, not ruled out.
| Command | Purpose |
|---|---|
aeo-platform init |
Set up .aeo-tracker.json — auto-discovers category, generates 3 commercial queries, validates them |
aeo-platform init --queries-only |
Re-suggest queries without touching brand / domain / providers |
aeo-platform run |
Query each AI engine with each query. Save raw responses to aeo-responses/<domain>/YYYY-MM-DD/ |
aeo-platform run --replay [--replay-from=YYYY-MM-DD] |
Rebuild today's summary from cached responses (zero API cost, fully offline — no extractor/sentiment LLM calls either; no API keys required) |
aeo-platform run-manual <engine> --from-dir ./folder |
Import pasted UI answers for engines without an accessible API |
aeo-platform report |
Generate report.md + report.html. HTML auto-opens in your browser |
aeo-platform diff |
Compare last two runs — what changed, what's new, what regressed |
aeo-platform export --format=csv |
Flatten every snapshot into a CSV (or JSON) for Looker / Sheets / your warehouse |
aeo-platform crawl-stats --log-file=path |
Parse Apache/nginx access logs to see AI-bot crawl frequency on your own site (Combined Log Format only — IIS W3C Extended Format not supported, see Limitations) |
aeo-platform --help lists every flag. aeo-platform <cmd> --help for per-command help.
Flags reference — every flag, by command
Every flag aeo-platform accepts, grouped by which command consumes it.
| Flag | Commands | Purpose |
|---|---|---|
--yes / -y |
init |
Non-interactive (CI / dotfiles). Requires --brand, --domain, and --auto or --manual |
--auto |
init --yes |
Full research pipeline: brainstorm → filter → score → cross-model validate → select |
--manual |
init --yes |
Skip LLM analysis; use pre-existing queries |
--light |
init --yes --auto |
Bypass research pipeline; single-shot suggest |
--keywords "q1,q2,q3" |
init --yes |
Bring-your-own queries — zero LLM cost |
--queries-only |
init |
Re-suggest queries without changing brand / domain / providers |
--strict-validation |
init, run |
Cross-check query validation with 2 LLM providers (~2× validation cost) |
--force |
run |
Bypass validation gate |
--json |
run |
Structured JSON to stdout, ANSI suppressed (CI-friendly) |
--geo=us,uk,de,... |
run |
Run queries under multiple regional contexts. 12 codes: us, uk, de, fr, es, it, ca, au, in, br, jp, nl. Multiplies cost by region count |
--depth=<web|full|auto> |
run |
web (default) — single web pass. full — adds training-data pass (~2× cost). auto — prompts if last training baseline > 14 days |
--samples=<N> |
run |
Query each cell N times instead of once, so a noisy LLM flip is not read as a real change. Presence then carries a 95% Wilson confidence interval and diff treats overlapping intervals as noise. Default 1 (single-shot); capped at 25; cost scales ~N×. See How we count visibility |
--replay |
run |
Rebuild summary from cached raw responses (zero API cost, fully offline — skips live model discovery AND extractor/sentiment LLM calls; no API keys required) |
--replay-from=YYYY-MM-DD |
run |
Replay a specific date instead of the most recent capture |
--from-dir <path> |
run-manual |
Directory containing q1.txt, q2.txt, q3.txt with pasted UI answers |
--last <N> / --since <date> |
diff |
Compare last N runs / compare a date to latest run |
--format=<csv|json> |
export |
Output format (CSV default) |
--refresh-cache <fields> |
report |
Force-refresh cached fields before report. CSV list or all |
--no-html |
report |
Markdown only — skip HTML write + browser auto-open |
--no-open |
report |
Write files but don't auto-open the browser |
--no-authority / --no-page-signals / --no-entity-graph / --no-pricing |
report |
Skip optional fetch-heavy checks (use behind a VPN, offline, or to dodge rate limits) |
--openai-model=<id> / --gemini-model=<id> / --anthropic-model=<id> / --perplexity-model=<id> |
run |
Override the model for one run only (no config rewrite). E.g. switch from the default gpt-5.6-luna to another model available to your OpenAI project |
--add-queries "q1,q2,q3" |
init |
Add queries to an existing config without re-running brainstorm; preserves prior basket history |
--replace-queries "q1,q2,q3" |
init |
Replace queries in an existing config (forks basket version); preserves prior versions in basketHistory |
Exit codes & CI integration — cron, Task Scheduler, GitHub Actions
aeo-platform run returns one of four exit codes after every audit — wire them into your alerting tier.
| Code | Meaning | Typical CI response |
|---|---|---|
0 |
Score stable or improved vs previous run | Success — nothing to alert |
1 |
Score dropped more than regressionThreshold (default 10pp) |
High-priority alert |
2 |
All checks returned zero mentions | Medium alert — brand invisible (normal on day 1) |
3 |
All providers errored | Infrastructure alert (keys / billing / network) |
Tune the threshold in .aeo-tracker.json:
{ "regressionThreshold": 5 }CI integration
Bash + cron (macOS / Linux):
#!/bin/bash
aeo-platform run --json > /var/log/aeo-$(date +%F).json
case $? in
0) : ;; # stable
1) slack-alert "AEO regression detected" ;;
2) : ;; # invisible — expected for new brands
3) slack-alert "aeo-platform: API errors" ;;
esacWindows (PowerShell + Task Scheduler):
One-time setup: enable script execution for the current user —
Set-ExecutionPolicy -Scope CurrentUser RemoteSigned(or skip and use-ExecutionPolicy Bypassin the schtasks command below).
Save as aeo-audit.ps1:
# UTF-8 output (PowerShell 5.1 defaults to UTF-16; PowerShell 7+ is UTF-8 already)
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
$OutputEncoding = [System.Text.Encoding]::UTF8
$logDir = Join-Path $env:LOCALAPPDATA 'aeo'
New-Item -ItemType Directory -Force -Path $logDir | Out-Null
$logPath = Join-Path $logDir ("aeo-{0}.json" -f (Get-Date -Format 'yyyy-MM-dd'))
aeo-platform run --json | Out-File -Encoding utf8 $logPath
$exitCode = $LASTEXITCODE # capture BEFORE any other command — Invoke-RestMethod overwrites $LASTEXITCODE
function Send-SlackAlert($msg) {
if ($env:SLACK_WEBHOOK) {
Invoke-RestMethod -Uri $env:SLACK_WEBHOOK -Method Post `
-Body (@{text = $msg} | ConvertTo-Json) -ContentType 'application/json' | Out-Null
}
}
switch ($exitCode) {
0 { } # stable
1 { Send-SlackAlert 'AEO regression detected' }
2 { } # invisible — expected for new brands
3 { Send-SlackAlert 'aeo-platform: API errors' }
}
exit $exitCodeRegister as a weekly Task Scheduler job (Monday 09:00 local time — Task Scheduler does not understand UTC):
schtasks /Create /SC WEEKLY /D MON /TN "AEO Weekly Audit" ^
/TR "powershell -NoProfile -ExecutionPolicy Bypass -File C:\path\to\aeo-audit.ps1" ^
/ST 09:00Cron and Task Scheduler use different time bases: Linux cron typically runs in the server's TZ (often UTC on cloud VMs), Task Scheduler
/STis always local machine time. GitHub Actions cron (next block) is always UTC. Pick your TZ deliberately.
GitHub Actions:
name: Weekly AEO Audit
on:
schedule: [{ cron: '0 9 * * 1' }] # Monday 9:00 UTC
jobs:
audit:
runs-on: ubuntu-latest # works identically with windows-latest;
# on Windows replace bash `>` with `| Out-File -Encoding utf8`
# to avoid UTF-16 BOM in the JSON artifact.
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: 20 }
- run: npm install -g aeo-platform@1 # pin the major in CI — upgrade deliberately
- run: aeo-platform run --json > latest.json
env:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
NO_COLOR: '1'
- uses: actions/upload-artifact@v4
with: { name: aeo-latest, path: aeo-responses/ }Configuration — .aeo-tracker.json fields
aeo-platform init creates .aeo-tracker.json in the working directory. The file name is preserved across the rename so existing dotfiles keep working.
{
"brand": "YOURBRAND",
"domain": "YOURDOMAIN.COM",
"category": "Short description of your competitive space",
"queries": [
"best YOURCATEGORY services 2026",
"top YOURCATEGORY monitoring tools 2026",
"YOURCATEGORY consultants for B2B startups"
],
"regressionThreshold": 10,
"providers": {
"openai": { "model": "gpt-5.6-luna", "classifyModel": "gpt-5-nano", "env": "OPENAI_API_KEY" },
"gemini": { "model": "gemini-3.8-flash", "classifyModel": "gemini-3.1-flash-lite", "env": "GEMINI_API_KEY" },
"anthropic": { "model": "claude-sonnet-5", "classifyModel": "claude-haiku-4-5", "env": "ANTHROPIC_API_KEY" },
"perplexity": { "model": "sonar-reasoning-pro", "classifyModel": "sonar", "env": "PERPLEXITY_API_KEY" }
}
}Fields:
brand,domain,category— what the tool measuresqueries— exactly 3, unbranded, commercial-intent. Methodological queries («how to X») are rejected by the validatorregressionThreshold— exit code1fires when score drops by more than this many percentage points week-over-week (default 10)providers[].env— name of the env var that holds the key (override for non-standard names likeOPENAI_API_KEY_DEV)providers[].model— auto-discovered at run start (newest available); override here to pin a specific modelproviders[].classifyModel— cheaper model used for extraction, sentiment, validation, and other short classification calls
Webappski is an AEO agency that measures client visibility with aeo-platform, its own open-source npm engine — clients can install it and reproduce the measurement grid themselves. The agency is in Gdynia, Poland. The engine is published under MIT rather than kept internal, which is the whole point: a client who is handed a visibility grid can re-derive it instead of trusting it. Webappski also publishes its own grid, including the cells where it is not cited — 2 of 39 on the 2026-06-14 run. Agency services are not sold in this README; the repository stays a tool.
Answer engine optimization (AEO) and generative engine optimization (GEO) describe the same field — the practice of making your brand recommended by AI answer engines (ChatGPT, Claude, Gemini, Perplexity). The naming split is industry-political: AEO is preferred by Profound and parts of the agency world; GEO is preferred by Wikipedia, AthenaHQ, and most 2026 listicles. aeo-platform works for both and surfaces both terms in metadata and reports.
Traditional SEO optimises for click-through from search-result pages. AEO/GEO optimises for inclusion in the AI-generated answer itself. Per Webappski's 2026 audits and the wider industry consensus, classic domain-authority signals predict a small fraction of AI citations — entity signals (Schema.org with verified sameAs, Wikidata Q-IDs, named-author attribution) and citation-source presence (Reddit, YouTube, Wikipedia, G2, niche listicles) do most of the work. aeo-platform measures the foundational metric directly: "when a user asks an AI engine about my category, does my brand appear in the answer?"
Four, via official APIs: ChatGPT (gpt-5.6-luna + the Responses web_search tool), Claude (claude-sonnet-5), Gemini (gemini-3.8-flash), Perplexity (sonar-reasoning-pro). For browser-only surfaces (Perplexity Pro UI, ChatGPT Pro personalisation, Claude.ai UI) use run-manual to paste UI answers. Models auto-discover at run time and refresh to the newest stable variant via provider model-listing APIs — pin a specific model in .aeo-tracker.json::providers[].model if you need version-locked measurements for compliance.
Yes. Nothing leaves your machine except to the AI providers you explicitly configure (the same providers you'd query from a browser) — plus at most one version check a day against registry.npmjs.org (the host npm itself talks to; nothing is sent, skipped in CI/non-TTY, opt out with AEO_NO_UPDATE_CHECK=1). No telemetry. No analytics. No traffic to webappski.com. Raw responses stay on disk in aeo-responses/<domain>/YYYY-MM-DD/. API keys are read from process.env and never written to disk. OpenAI Responses requests include store:false, which disables Responses application-state storage but does not eliminate separate provider abuse-monitoring retention or constitute Zero Data Retention.
No. One is enough to start: any of OPENAI_API_KEY / GEMINI_API_KEY / ANTHROPIC_API_KEY (single-key mode — competitor mentions are marked unverified, since there is no second model to cross-check them). Recommended: OPENAI_API_KEY + GEMINI_API_KEY — they double as the ChatGPT + Gemini columns and power the two-model competitor extractor. PERPLEXITY_API_KEY is optional; it adds its engine column.
A personalised action plan you get by pasting aeo-platform's JSON brand-context block (visibility index, per-engine deltas, top competitors, citation gaps, crawl matrix, authority signals) into your own ChatGPT, Claude, Gemini, or Perplexity chat. The receiving AI returns 30 missions (≈1–3 hours each, grouped into 4 weekly chunks) keyed to your specific gaps — named competitors to displace, specific URLs to pitch, weakest-engine fortification, citation-gap closure. In our July 2026 review of 23 tracked AEO tools, plus a live re-check of 19 tools on 4 September 2026, no other one shipped a portable plan generator — hosted tools do produce action plans, but inside the dashboard you are renting. Detailed flow above in The 30-mission AEO plan.
Otterly, Profound, Peec, Bluefish, AthenaHQ, Goodie — and Waikay, Trakkr, LLMrefs, Superlines, Scrunch AI and AirOps, added to the table after the 4 September 2026 check — are paid hosted dashboards. They tell you the problem inside their UI and mostly stop there; where they do generate an action plan, Waikay most explicitly, it is metered and stays in the dashboard. aeo-platform is a free open-source CLI that calls provider APIs directly, runs on your machine, stores raw responses locally, and none of the 17 third-party products in that table shipped a portable paste-into-AI 30-mission plan generator when they were re-checked on 4 September 2026. Two of the 19 rows in that table do ship one, and both are ours: this CLI, and Webappski, the agency that maintains it and delivers audits with the same engine — a Webappski-delivered audit runs on our infrastructure, so raw data stays local only on the CLI path. See Comparison vs hosted AEO platforms for the full table, including what those tools do better than this one.
Yes. --json flag for structured stdout, ANSI auto-disabled on non-TTY, NO_COLOR env honoured, exit codes 0/1/2/3 map cleanly to alerting tiers. GitHub Actions / cron example above.
No. New brands typically score 0–5% in the first 4 weeks. AI engines update when third-party sources (blog posts, directories, review sites) start mentioning your brand, not in real time. Typical trajectory: 0% in weeks 1–4, first mention between week 6 and 12. The value is in week-over-week deltas, not the absolute score on day 1. The Recommended actions section of every report tells you which third-party sources to pitch to move the needle. Before acting on a 0%, treat it as a hypothesis and confirm the two checks in A 0% is a hypothesis, not a fact — a zero on an off-target basket is an artefact, not invisibility.
Yes. The auto-suggest prompt tells the LLM to match the site's primary language (detected from <html lang>). Tested on English, Polish, and German sites.
Yes — Node 20+ and npm install -g aeo-platform is all you need. The CLI uses path.join everywhere, opens the HTML report via start on Windows (the PowerShell/CMD equivalent of macOS open and Linux xdg-open), and reads API keys from process.env identically. PowerShell 5.1, PowerShell 7+, CMD, Git Bash, and WSL are all supported.
Known Windows-specific gotchas to watch for:
&&chain operator works in CMD and PowerShell 7+; not in PowerShell 5.1 (the default Windows 10/11 shell — check via$PSVersionTable.PSVersion). Use;or separate commands withif ($LASTEXITCODE -eq 0) { ... }checks.aeo-platform run --json > out.jsonin PowerShell 5.1 writes UTF-16 LE, which breaks JSON parsers downstream. Pipe throughOut-File -Encoding utf8instead, or upgrade to PowerShell 7+ (UTF-8 by default). See the CI section above for the full pattern.- PowerShell Execution Policy blocks
.ps1scripts by default. RunSet-ExecutionPolicy -Scope CurrentUser RemoteSignedonce, or pass-ExecutionPolicy Bypasstopowershell.exefor one-off invocations. npm i -gPATH issue: binaries land in%APPDATA%\npmwhich is not always on PATH right after Node install. Restart the terminal ifaeo-platformis not found, or usenpx aeo-platforminstead (no global install needed).- Persistent env vars require a terminal restart.
setx(CMD) and[System.Environment]::SetEnvironmentVariable(...,'User')(PowerShell) write to the User profile but do not affect the current session. Useset/$env:for the current session only. crawl-statsparses Apache/nginx logs only — IIS W3C Extended Log Format is not supported in 1.0.x (on the roadmap). Workaround: convert with Log Parser 2.2 to NCSA Combined first.- Brand names with non-ASCII characters render correctly in PowerShell 7+ and Windows Terminal; legacy
cmd.exemay show?for Cyrillic / CJK in console output (file output to_summary.jsonis always UTF-8 and unaffected). For Cyrillic console output in CMD:chcp 65001switches the codepage to UTF-8. - Manual paste mode + Notepad: save
.txtfiles as UTF-8 without BOM (Notepad's «UTF-8 with BOM» default leaves an invisible byte at file start that affects mention detection). VSCode and Notepad++ default to UTF-8 without BOM. - Long paths (
MAX_PATH260 chars). If your repo lives deep underC:\Users\<long-username>\...and you hitENAMETOOLONGmid-run, enable Windows Long Paths once:Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\FileSystem" -Name LongPathsEnabled -Value 1(admin PowerShell, then reboot). Alternative: move the working directory closer to a drive root (e.g.C:\aeo\<brand>). - Windows Defender occasionally flags Node-based CLI tools. If
aeo-platform runis blocked, add%APPDATA%\npmto Defender exclusions. - Task Scheduler
/STis local time, not UTC (unlike GitHub Actions cron). Pick your timezone deliberately.
Yes. All new on-disk artifacts are namespaced by domain — aeo-responses/<domain>/<date>/ and aeo-reports/<domain>/<date>/ — so two different domains can safely share one working directory: re-run init for the next domain (it overwrites .aeo-tracker.json), then run / report. Each domain keeps its own runs, trend history, and reports; they never blend. Internationalized domains use their canonical Punycode hostname as <domain>, so distinct Unicode domains cannot collapse to the same directory. Pre-namespacing aeo-responses/<date>/ history remains readable when its _summary.json records the active domain; cache updates and same-day continuations stay in that source directory, while fresh dates use the namespaced layout. run/report/diff/export operate on the domain in the current .aeo-tracker.json (or, if there's no config, the single unambiguous domain present on disk).
A separate working directory per brand still works and keeps each client's .aeo-tracker.json (queries, provider config, validation cache) fully isolated — that's the cleaner setup for many clients. A wrapper script that loops over client directories is ~10 lines of bash (macOS / Linux) or PowerShell (Windows).
Weekly. Daily adds noise without signal (AI models don't update fast enough to make daily deltas meaningful). Monthly loses meaningful trend resolution.
Hotfix on top of 1.0.7 — surfaced by maintainer dogfood. Three connected fixes plus relaxed confidence rules:
- Trust LLM's
valid:trueverdict. Previously the validator blocked queries withvalid:truebut confidence < 0.7 — but normal commercial queries routinely score 0.6–0.7 (LLM accounts for alternate meanings). The hard threshold rejected good queries. Now: if LLM said «valid» — we accept. Confidence stays in cache for audit only. - Substitution uses the SAME rules as main validation. Pre-1.0.8 the silent-substitution block checked only
search_behavior, while main validation checkedvalidANDsearch_behavior. Queries passed substitution then got re-blocked by main — recovery panel fired with «5 of 5 commercial passed» but still listed blocked queries. Same class of bug we fought in 1.0.4–1.0.7. Closed structurally — both paths now apply identical criteria. - Recovery panel labels show the real reason. Pre-1.0.8 a
valid:false, retrieval-triggeredblocker rendered as «non-commercial (search_behavior: retrieval-triggered)» — internally contradictory. Now: «LLM rejected: » forvalid:false, «non-commercial» only when truly non-commercial, static-issue message for acronym ambiguity. - Recovery panel header shows both counts honestly. Was «only X of 5 commercial candidates passed validation» which lied when LLM-blocking happened. Now: «X of 5 commercial-OK, Y blocked by LLM verdict».
Hotfix on top of 1.0.6 — surfaced by maintainer dogfood within minutes of the 1.0.6 publish.
gpt-5-search-apino longer fails on every cell. 1.0.6 sentreasoning_effortto allgpt-5*models indiscriminately; search-variants (which are RAG-tuned and stripped-down) rejected it with HTTP 400.SUPPORTS_REASONING_EFFORTwhitelist now excludes any model ID containingsearch— works forgpt-5-search-api,gpt-5-mini-search-api,gpt-4o-search-preview, and any future search variant.- Live status labels rewritten. Cryptic
firing…and60s pacingreplaced with concrete labels:calling provider API (network in-flight),TPM rate-limit — 58s until token-bucket refill,provider cooldown (post-429 backoff) — 12s remaining. - Countdown ticks down in real time. Operator sees
60s → 59s → 58s …updating each frame, not a static label that looks frozen. - Abort hint at top of live region —
(running 12 cells across 4 providers — press Ctrl+C to abort cleanly). Documents the affordance.
Commercial-only pipeline with silent substitution. The 4-bucket query generation (commercial / problem / vertical / comparison) was retired — vertical/problem/comparison queries reliably failed the downstream commercial-only validator, which is what produced the recurring "recovery panel suggests command that the CLI rejects" trust failure from 1.0.2 through 1.0.5. New pipeline generates 5 commercial vendor-listing queries (3 needed + 2 spares), validates all 5 at init time, silently substitutes any of the top-3 that fail with passing spares. Operator sees only the final 3 — no recovery panel for the typical case. Recovery panel fires ONLY when fewer than 3 of 5 commercial candidates survive validation (the genuine impossibility case). Full notes in CHANGELOG.md.
Validator-honesty release (1.0.4 work + a pool top-up follow-up that landed in 1.0.5; 1.0.4 was never published to npm). The (validated) tag in init now means BOTH validator stages passed (category-validation + industry-fit / commercial-only) — earlier versions tagged queries as (validated) after one stage only, and the recovery panel then suggested commands that the validator re-blocked on the next run. A --manual interactive escape hatch was added to the recovery panel for new brands the LLM has no context for. Plus self-sufficient pool top-up: when initial pool validation produces fewer than 3 RETRIEVAL-passing alternatives, the tool autonomously generates the missing queries via a dedicated LLM call instead of asking the operator to retry — so the recovery panel never suggests an invalid --keywords command. Full notes in CHANGELOG.md.
Honest list of where aeo-platform stops short — read before you wire it into a contract or a board slide.
- API ≠ browser UI. Personalisation, session context, and occasional model upgrades mean API responses can differ slightly from what users see in the ChatGPT / Gemini / Claude browser apps. Manual paste mode catches the browser-personalisation layer. Scope is spelled out in full under What this measures — and what it does NOT.
- No Google AI Overviews / AI Mode or Microsoft Copilot coverage. These have no first-party query API; the tool does not measure them, so their absence from a score is a coverage gap, not invisibility on those surfaces. A connector is on the roadmap.
- Week-over-week stochastic variance. Same queries on the same day typically produce ±5–10% score fluctuation because AI outputs are probabilistic. Use weekly cadence (not daily) to smooth noise.
- Provider rate limits on free tiers. Running 3 queries in parallel is usually fine, but back-to-back brand runs can hit 429s.
- Single-brand scope per config. Multi-brand workflows need a wrapper that loops over per-client directories.
- Gemini citation URLs are Vertex AI redirect tokens — resolved to readable domains using the
titlefield; unreadable tokens are dropped rather than displayed. crawl-statsparses Apache/nginx Combined Log Format only. IIS W3C Extended Log Format is not supported in 1.0.x (on the roadmap). Workaround for IIS users: pre-convert with Log Parser 2.2 (logparser "SELECT * INTO out.log FROM in.log" -o:NCSA) to NCSA Combined format, then point--log-file=out.log.
Where aeo-platform is going next (no fixed dates — feedback-driven):
- Google AI Overviews / AI Mode coverage (no first-party query API today — needs a separate connector; see What this measures — and what it does NOT)
- Multi-brand profiles for agencies running weekly audits on many clients
- Diagnostic prompts asking AI engines why they don't cite you
- Optional SQLite-backed history for trends beyond filesystem snapshots
- README AEO-discoverability optimisation driven by real npm-download query patterns
Not planned: hosted dashboard, proprietary scoring layer, data uploads to Webappski servers. Local-first privacy and methodology transparency are core values of aeo-platform, not features.
Full version history: CHANGELOG.md.
npm uninstall -g @webappski/aeo-tracker
npm install -g aeo-platformThe CLI command aeo-tracker keeps working as a built-in alias inside aeo-platform, and your .aeo-tracker.json remains compatible. Existing flat aeo-responses/<date>/ history is read in place; new runs and reports are written under per-domain namespaces. Project-dependency users with caret ^0.3.0 in package.json should manually edit it to "aeo-platform": "^1.0.0" (caret semantics don't cross majors). See CHANGELOG.md for the full migration note.
Full quickstart — for first-time terminal users (~5 minutes)
If you've never run a CLI tool before, that's fine — aeo-platform needs one-time setup, but the weekly run takes zero terminal skill after that.
1. Open Terminal.
- macOS: ⌘+Space → type Terminal → Enter.
- Windows 11: Win+X → Terminal (recommended — runs PowerShell 7+ if installed, else Windows PowerShell 5.1).
- Windows 10: Start menu → Windows PowerShell → Enter (or install Windows Terminal from the Microsoft Store).
- Linux: you know where it is.
2. Install Node.js 20+ (once per machine). Check first: paste node --version + Enter. If it prints v20.x or higher, skip to step 3. Otherwise:
- macOS / Linux: download from nodejs.org (LTS version), or
brew install node@20. - Windows: download from nodejs.org (LTS version), or
winget install OpenJS.NodeJS.LTS, orchoco install nodejs-lts(Chocolatey users).
Re-open Terminal after install so PATH refreshes.
3. Install aeo-platform.
npm install -g aeo-platform- macOS / Linux: if you see
EACCES, fix per npm docs — typicallysudo npm install -g aeo-platform. - Windows:
npm i -gputs the binary in%APPDATA%\npm, which is not always on PATH right after Node install. Ifaeo-platformis not found after install — restart your terminal. If still missing, usenpx aeo-platform <command>instead (skips global install entirely). Windows does not needsudo.
4. Get your 2 required API keys. Open these in new tabs, sign up (free), click Create new key:
- OpenAI — key starts with
sk-proj-... - Google Gemini — key starts with
AIzaSy...
5. Save the keys to your shell. Replace placeholders with the actual key strings.
macOS (zsh) / Linux (bash):
echo 'export OPENAI_API_KEY="PASTE_OPENAI_KEY_HERE"' >> ~/.zshrc
echo 'export GEMINI_API_KEY="PASTE_GEMINI_KEY_HERE"' >> ~/.zshrc
source ~/.zshrcOptional — adds the Claude / Perplexity columns:
echo 'export ANTHROPIC_API_KEY="sk-ant-..."' >> ~/.zshrc
echo 'export PERPLEXITY_API_KEY="pplx-..."' >> ~/.zshrc
source ~/.zshrcBash users on Linux: replace
~/.zshrcwith~/.bashrc. Git Bash on Windows: same —~/.bashrc.
Windows (PowerShell — persistent, User scope):
[System.Environment]::SetEnvironmentVariable('OPENAI_API_KEY','PASTE_OPENAI_KEY_HERE','User')
[System.Environment]::SetEnvironmentVariable('GEMINI_API_KEY','PASTE_GEMINI_KEY_HERE','User')
# Optional — adds Claude / Perplexity columns
[System.Environment]::SetEnvironmentVariable('ANTHROPIC_API_KEY','sk-ant-...','User')
[System.Environment]::SetEnvironmentVariable('PERPLEXITY_API_KEY','pplx-...','User')Windows (CMD — persistent):
setx OPENAI_API_KEY "PASTE_OPENAI_KEY_HERE"
setx GEMINI_API_KEY "PASTE_GEMINI_KEY_HERE"
:: Optional
setx ANTHROPIC_API_KEY "sk-ant-..."
setx PERPLEXITY_API_KEY "pplx-..."Windows note: both
SetEnvironmentVariable(...,'User')andsetxwrite to the User profile and require a terminal restart beforeaeo-platformsees the new variables. To verify after restart:echo $env:OPENAI_API_KEY(PowerShell) orecho %OPENAI_API_KEY%(CMD). For one-off / current-session-only use,$env:OPENAI_API_KEY = "..."(PowerShell) orset OPENAI_API_KEY=...(CMD) take effect immediately but vanish when the window closes.setxhas a 1024-character limit per value (not an issue for current API keys, but worth knowing for long custom values).
6. Run aeo-platform. Replace YOURBRAND and YOURDOMAIN.COM:
aeo-platform init --yes --brand=YOURBRAND --domain=YOURDOMAIN.COM --auto
aeo-platform run
aeo-platform reportThe HTML report auto-opens in your browser.
API keys under non-standard env-var names
Common on dev machines — you already use ChatGPT / Claude via another tool and the keys live in ~/.zshrc under custom names (OPENAI_API_KEY_DEV, MY_CLAUDE_KEY, etc.). aeo-platform init detects them in three stages:
- Standard names —
OPENAI_API_KEY,GEMINI_API_KEY,ANTHROPIC_API_KEY,PERPLEXITY_API_KEY. - Heuristic match — scans env vars matching
^(OPENAI|GPT)…(API|KEY|TOKEN)$and similar patterns per provider. Matches proposed for confirmation during init. - Interactive prompt — for any provider still unmatched, init asks for the env var name directly.
Whatever you confirm is written into .aeo-tracker.json::providers[].env, so every subsequent run knows where to look. Your actual key values stay in process.env — never written to disk.
Windows users: init reads process.env identically on all platforms — your custom-name variables are detected the same way. Set them via:
# PowerShell (persistent)
[System.Environment]::SetEnvironmentVariable('OPENAI_API_KEY_DEV','sk-proj-...','User'):: CMD (persistent)
setx OPENAI_API_KEY_DEV "sk-proj-..."Restart the terminal after either command, then run aeo-platform init.
CI mode (init --yes) disables interactive prompts. For CI, set the standard names in env, or pre-commit .aeo-tracker.json with explicit env field per provider.
Cost (per weekly run)
You pay only the LLM-API cost for your own runs. There is no aeo-platform subscription fee.
- 2-engine minimum (OpenAI + Gemini) — typically a few cents per weekly run
- 4-engine matrix (+ Claude + Perplexity) — roughly 2× the 2-engine cost
--depth=fulldoubles cost (adds a training-data pass per cell where supported)--geo=us,uk,demultiplies cost linearly by region count- Sentiment classification: ~$0.0008 per cell that has a brand mention (skip-on-no-mention)
- Outreach-template drafts: ~$0.003 one-off per report, cached in
_summary.json::outreachTemplates
All other modules — crawlability audit, page signals, entity graph reciprocity, authority presence (Wikipedia / Reddit / GitHub), competitor pricing tier, region context, ads detector, UTM tracker, topic clusters — are zero LLM cost (free public APIs / direct HTTP).
Behind this tool
Built and maintained by Webappski — an AEO / GEO agency. aeo-platform (formerly @webappski/aeo-tracker) is the open-source spinout of Webappski's internal AEO / GEO audit toolchain. Weekly runs against Webappski's own brand and client brands are in production.
The tool was open-sourced after observing a gap between third-party AEO scorecards (which surfaced a mid-range proprietary score for Webappski) and direct-API tests (which showed zero brand mentions across the same engines in the same week). That gap between proprietary score and direct-API truth is the bug aeo-platform is built to fix.
Methodology lives in the weekly reports at webappski.com/blog. The tool itself is the what; the blog is the how.
Contributing & git hooks
PRs welcome. Open an issue first if you're planning a non-trivial change so we can sketch the shape together. Bug reports and feature requests at github.com/webappski/aeo-platform/issues.
Git hooks
Repo ships with pre-commit (runs npm test before commit) and pre-push (runs npm test and on main / master runs it a second time for a determinism check) hooks under .githooks/. They are auto-installed via the npm install postinstall script — it points git at the tracked hooks via git config core.hooksPath .githooks. Zero new dependencies (no Husky, no lint-staged, no simple-git-hooks); vanilla bash that runs on macOS and Linux. postinstall ends with || true, so the step is a no-op in non-git environments (CI containers, Docker, etc.).
Bypass when needed: git commit --no-verify / git push --no-verify, or add [skip-tests] anywhere in the commit message to make the intent explicit (suitable for typo / comment-only / doc-only changes).
Running from source on Windows: the shebang line in
bin/aeo-tracker.jsis ignored by Windows, so./bin/aeo-tracker.jswon't work. Usenode bin/aeo-tracker.js <command>for development, or install globally (npm install -g .from the repo root) which creates theaeo-platform.cmdwrapper that handles the shebang transparently.
MIT — do whatever you want with it.
{
"@context": "https://schema.org",
"@graph": [
{
"@type": "SoftwareApplication",
"@id": "https://github.com/webappski/aeo-platform#software",
"name": "aeo-platform",
"alternateName": ["aeo-tracker", "@webappski/aeo-tracker"],
"applicationCategory": "DeveloperApplication",
"applicationSubCategory": "Answer Engine Optimization, Generative Engine Optimization, Brand Visibility Monitoring",
"operatingSystem": "macOS, Linux, Windows",
"softwareVersion": "1.15.0",
"datePublished": "2026-06-06",
"dateModified": "2026-09-20",
"license": "https://opensource.org/licenses/MIT",
"downloadUrl": "https://www.npmjs.com/package/aeo-platform",
"codeRepository": "https://github.com/webappski/aeo-platform",
"offers": { "@type": "Offer", "price": "0", "priceCurrency": "USD" },
"description": "Open-source CLI that measures brand visibility across ChatGPT, Claude, Gemini, and Perplexity using direct provider APIs, audits AI-bot crawlability + authority signals, and exports a JSON brand-context you paste into any AI for a personalised 30-mission AEO/GEO plan. Free MIT-licensed alternative to Otterly, Profound, Peec, and Bluefish.",
"featureList": [
"4 engines via official APIs (ChatGPT, Claude, Gemini, Perplexity)",
"Portable paste-into-AI 30-mission AEO plan (JSON brand-context export)",
"AI-bot crawlability audit (robots.txt × bot matrix)",
"Authority signals: Wikipedia, Reddit, GitHub, Wikidata",
"Unified Visibility Index (UVI) — 4 sub-components with re-norm",
"Two-model hallucination filter (GPT-5 + Gemini cross-check)",
"Region context (--geo) across 15 locales",
"Editorial bento HTML report (offline, embedded fonts, zero CDN)",
"CSV / JSON export for Looker, Sheets, BI",
"CI-friendly exit codes 0/1/2/3 + --json stdout",
"Zero runtime dependencies, MIT, local-first"
],
"keywords": "AEO, GEO, answer engine optimization, generative engine optimization, ChatGPT, Claude, Gemini, Perplexity, brand monitoring, AI visibility, Otterly alternative, Profound alternative, Peec alternative, Bluefish alternative, AthenaHQ alternative, 30-mission AEO plan",
"sameAs": [
"https://www.npmjs.com/package/aeo-platform",
"https://github.com/webappski/aeo-platform",
"https://webappski.com/en/aeo-platform"
],
"publisher": { "@id": "https://webappski.com/#webappski-org" },
"exampleOfWork": [
{
"@type": "CreativeWork",
"name": "Sample 30-mission AEO plan — typelessform.com (UVI 42%)",
"url": "https://github.com/webappski/aeo-platform/blob/main/examples/sample-plan-typelessform.md",
"description": "Real aeo-platform run output for typelessform.com (2026-05-18): 30 missions grounded in 12 engine cells, 8 named competitors, and live entity-graph reciprocity data."
},
{
"@type": "CreativeWork",
"name": "Sample 30-mission AEO plan — bare-site brand (UVI 0%)",
"url": "https://github.com/webappski/aeo-platform/blob/main/examples/sample-plan-output.md",
"description": "Sample 30-mission AEO plan for a brand absent from all four answer engines — shows the cold-start trajectory the report generates."
}
]
},
{
"@type": "Organization",
"@id": "https://webappski.com/#webappski-org",
"name": "Webappski",
"url": "https://webappski.com",
"description": "Answer Engine Optimization (AEO / GEO) studio. Maintains aeo-platform — the open-source AEO platform for ChatGPT, Claude, Gemini, and Perplexity.",
"sameAs": [
"https://www.linkedin.com/company/web-appski/",
"https://www.youtube.com/channel/UCeanerJjnwmznlRA1Mzf5pA",
"https://github.com/webappski",
"https://www.g2.com/sellers/webappski",
"https://www.npmjs.com/package/aeo-platform",
"https://typelessform.com",
"https://typelessity.com"
]
},
{
"@type": "FAQPage",
"mainEntity": [
{
"@type": "Question",
"name": "What is answer engine optimization (AEO), and how is it different from GEO?",
"acceptedAnswer": { "@type": "Answer", "text": "AEO and GEO describe the same field — making your brand recommended by AI answer engines (ChatGPT, Claude, Gemini, Perplexity). The naming split is industry-political: AEO is preferred by Profound; GEO by Wikipedia and most 2026 listicles. aeo-platform supports both terms in metadata and reports." }
},
{
"@type": "Question",
"name": "How is AEO different from SEO?",
"acceptedAnswer": { "@type": "Answer", "text": "SEO optimises for click-through from search-result pages. AEO/GEO optimises for inclusion in the AI-generated answer itself. Domain Authority predicts under 4% of AI citations in 2026 audits; entity signals (Schema.org sameAs, Wikidata Q-IDs) and citation-source presence (Reddit, Wikipedia, listicles) do most of the work." }
},
{
"@type": "Question",
"name": "Which AI engines does aeo-platform cover?",
"acceptedAnswer": { "@type": "Answer", "text": "Four engines via official APIs: ChatGPT (gpt-5.6-luna with the Responses web_search tool), Claude (claude-sonnet-5), Gemini (gemini-3.8-flash), Perplexity (sonar-reasoning-pro). Manual paste mode also covers browser-only surfaces like Perplexity Pro UI and ChatGPT Pro personalisation." }
},
{
"@type": "Question",
"name": "Is my data private?",
"acceptedAnswer": { "@type": "Answer", "text": "Yes. Nothing leaves your machine except to the AI providers you configure, plus at most one version check a day against registry.npmjs.org (nothing is sent; opt out with AEO_NO_UPDATE_CHECK=1). No telemetry. No analytics. No traffic to webappski.com. Raw responses stay on disk. API keys are read from process.env and never written." }
},
{
"@type": "Question",
"name": "Do I need API keys for all four engines?",
"acceptedAnswer": { "@type": "Answer", "text": "No. One is enough to start: any of OPENAI_API_KEY, GEMINI_API_KEY, or ANTHROPIC_API_KEY (single-key mode — competitor mentions are marked unverified). Recommended: OPENAI_API_KEY + GEMINI_API_KEY, which double as the ChatGPT + Gemini columns and power the two-model competitor extractor. PERPLEXITY_API_KEY is optional — it adds its engine column." }
},
{
"@type": "Question",
"name": "What is the 30-mission AEO plan?",
"acceptedAnswer": { "@type": "Answer", "text": "A personalised AEO action plan you get by pasting aeo-platform's JSON brand-context block into your own ChatGPT, Claude, Gemini, or Perplexity chat. The receiving AI returns 30 missions (≈1–3 hours each, grouped into 4 weekly chunks) keyed to your specific gaps — named competitors to displace, URLs to pitch, weakest-engine fortification. The only portable paste-into-AI plan generator among the 23 AEO tools we reviewed in July 2026 and the 19 we re-checked live on 4 September 2026. Hosted tools do generate action plans — Waikay meters them by quota — but those plans are produced and kept inside the vendor dashboard, not exported as a prompt you own." }
},
{
"@type": "Question",
"name": "How is aeo-platform different from Otterly, Profound, Peec, Bluefish?",
"acceptedAnswer": { "@type": "Answer", "text": "Otterly, Profound, Peec, Bluefish, AthenaHQ, Goodie, Waikay, Trakkr, LLMrefs, Superlines, Scrunch AI, AirOps, Ahrefs Brand Radar, Semrush AI Visibility, Evertune, Discovered Labs and HubSpot AI Search Grader are closed hosted products — 17 of them are compared row by row in the README. aeo-platform is a free open-source CLI that calls provider APIs directly, runs on your machine, stores raw responses locally, and none of the 17 third-party products in that table shipped a portable paste-into-AI 30-mission plan generator when the table was re-checked live on 4 September 2026. Two of the 19 rows in that table do ship one, and both are ours: this CLI, and Webappski, the agency that maintains it and delivers audits with the same engine — a Webappski-delivered audit runs on our infrastructure, so raw data stays local only on the CLI path. Where the hosted tools are ahead: engine breadth (Superlines reads 10+ surfaces, LLMrefs 11, Trakkr 8, Otterly 7 including Google AI Overviews and AI Mode, while aeo-platform covers the 4 engines reachable by official API), plus seats, alerting, SSO and SOC-2, none of which aeo-platform has." }
},
{
"@type": "Question",
"name": "What are the open-source alternatives to aeo-platform?",
"acceptedAnswer": { "@type": "Answer", "text": "The main open-source AI-visibility trackers, with stars and licences derived live from the GitHub API on 4 September 2026: elmohq/elmo (293 stars, MIT, Docker Compose or managed cloud), danishashko/geo-aeo-tracker (250, MIT, clone and run), ai-search-guru/getcito (182, MIT, a derivative work of elmo, Docker Compose), aryamantodkar/oneglanse (164, MIT, captures through product UIs rather than APIs), Canonry/canonry (129, FSL-1.1-ALv2 fair-source rather than OSI-approved), ansvisor/ansvisor (106, MIT), letterstory/lettertrace (69, MIT, bring your own keys) and sharozdawa/ai-visibility (9, MIT). aeo-platform itself is at 11 stars and MIT. The practical difference is not the star count but the setup: every one of those projects needs something stood up first — a PostgreSQL, a Supabase project, a Docker Compose file, at minimum a clone and an install — whereas npx aeo-platform writes to disk with no server, no database and no container. None of them exports a portable paste-into-AI plan. Note two naming traps: the npm package named ai-visibility is a robots.txt and llms.txt generator, not the GitHub project of that name, and geo-aeo-tracker, oneglanse, getcito and ansvisor are not published on npm at all." }
},
{
"@type": "Question",
"name": "Is aeo-platform CI-friendly?",
"acceptedAnswer": { "@type": "Answer", "text": "Yes. --json flag for structured stdout, ANSI auto-disabled on non-TTY, NO_COLOR env honoured, exit codes 0/1/2/3 map cleanly to alerting tiers. GitHub Actions and cron examples in the README." }
},
{
"@type": "Question",
"name": "What is the UVI (Unified Visibility Index) score?",
"acceptedAnswer": { "@type": "Answer", "text": "UVI is aeo-platform's 0–100 composite of four AI-answer signals: Presence 35% (mentioned cells / total cells), Sentiment 25% (high-confidence positive cells / mentioned cells), Rank 20% (normalised average rank position when mentioned), Citation 20% (cells where your domain was cited as a source). Weights live in lib/report/visibility-index.js and the ⓘ popover next to the hero number shows per-axis math on every run. Sub-components with insufficient data are excluded and remaining weights re-normalise — no phantom values." }
},
{
"@type": "Question",
"name": "What does an aeo-platform output file look like?",
"acceptedAnswer": { "@type": "Answer", "text": "A run writes aeo-responses/<domain>/YYYY-MM-DD/_summary.json with the canonical machine-readable shape (UVI score, per-engine mention/citation cells, topCompetitors, topCanonicalSources, entityGraph reciprocity, pageSignals, crawlability, authority blocks). The same data renders as a 6-surface editorial HTML report (Headline · Overview · Engine matrix · Citations · Actions · Diagnostics) and the bridge card exports a JSON brand-context block you paste into any AI for a 30-mission AEO plan. Worked example: examples/sample-plan-typelessform.md (UVI 42% from the 2026-05-13 run)." }
}
]
}
]
}