AEO TOOLKIT AdRails 0.8.0

Answer engine optimization, run from your own machine

AEO (answer engine optimization) is the work of making a site readable, extractable and citable by AI answer engines such as ChatGPT, Claude, Perplexity, Copilot and Gemini. AdRails ships eight read-only AEO tools plus an IndexNow submit: crawler audit, page answer-readiness, JSON-LD audit and generators, llms.txt builder, share-of-answer scoring, AI traffic analysis, and an ads-to-answers content gap bridge.

Last reviewed 2026-11-06 with AdRails 0.8.0

AEO

What is AEO, and why should an ads team care?

Answer engine optimization is the discipline of being quoted by AI assistants. Your paid search account already contains the raw material.

AEO in one paragraph

Answer engines read a page, pull one self-contained chunk, and cite it. That means crawler access, plain body text, answer-first paragraphs, question-shaped headings, FAQ blocks with numbers, and JSON-LD that matches what the page says. AdRails checks each of those properties and returns a score you can move.

Why ads teams are already ahead

Your search terms report is a list of questions people typed and paid for. Clicks prove the phrasing. AdRails turns those terms into question briefs and checks which of your pages already answers them. selftest: aeo.ads_to_aeo

What AEO is not

It is not a guarantee of citation, and no crawler score predicts an AI answer. AdRails reports what it can measure: robots rules, page structure, structured data, and the share-of-answer panel you recorded. Anything beyond that is a claim without a test behind it.

Every AEO tool here is read-only except aeo_indexnow with submit=true. Run the whole toolkit offline with python3 -m adsops --selftest.

STEP 01

Can AI crawlers reach your site at all?

A blocked agent cannot cite you. aeo_site_audit checks robots.txt, sitemap, canonicals and body text for 18 crawlers.

What gets checked

  • Robots rules. Longest-match evaluation under RFC 9309 for GPTBot, OAI-SearchBot, ChatGPT-User, ClaudeBot, Claude-User, Claude-SearchBot, PerplexityBot, Perplexity-User, Google-Extended, Googlebot, Bingbot, Applebot, Applebot-Extended, DuckAssistBot, Meta-ExternalAgent, Amazonbot, CCBot and Bytespider. selftest: aeo.crawl_audit
  • llms.txt presence. Whether /llms.txt exists and parses in the llmstxt.org format. selftest: aeo.llms_txt
  • Indexing signals. Sitemap, noindex and nosnippet directives, and canonical tags that point somewhere sensible.
  • Body text only. Whether the main content is present without JavaScript, so a fetch returns prose rather than an empty shell.

Reading the score

Output is a 0-100 score with the failing checks named individually. A common pattern: GPTBot allowed, but CCBot blocked by a blanket rule written for a different crawler, or a canonical tag pointing to a redirect. Fix the named check, rerun, watch the number move.

Keep an allow rule per crawler you want, rather than one wildcard you hope covers them. Wildcards are evaluated by longest match, so a specific disallow quietly wins.

STEP 02

Is the page answer-ready, or just readable?

aeo_page_audit scores ten structural properties and returns the extractable answer blocks it found.

Signals that raise the score

  • Answer-first paragraph. A 45-70 word block near the top that stands alone if lifted out of the page. selftest: aeo.page_audit
  • Question-shaped headings. H2s phrased as questions a person would actually ask, followed by the answer.
  • FAQ blocks. Question and answer pairs with concrete numbers, dates and named tools rather than adjectives.
  • Specifics and freshness. Product names, versions, limits, dated claims. Vague copy is not extractable.
  • Citations and entity clarity. Named sources, and a page that says plainly who publishes it and what it does.
  • Scannability. Short paragraphs, lists, no walls of text between the heading and the answer.

What it returns

The audit reports the extractable answer blocks it identified, plus the questions the page appears to be answering but never states. Those missing questions are your next headings.

It also runs a JSON-LD audit in the same call, since structured data is part of answer-readiness and separate from the prose. selftest: aeo.schema_audit

STEP 03

Does your structured data survive an audit?

JSON-LD audit for eleven schema types, plus generators that pass their own audit before you paste them.

Audited types

Required and recommended fields are checked for Organization, LocalBusiness, SoftwareApplication, Product, FAQPage, Article, BreadcrumbList, WebSite, HowTo, Dataset and Event. selftest: aeo.schema_audit

A missing FAQPage on a page that visibly has an FAQ is the most common finding, followed by an Organization block with no same_as and no logo.

Generators

aeo_schema_generate is a pure function: no network, no account. Pass kind=faq with qas, or software, org or breadcrumbs, and it returns the object, its own audit result and a ready-to-paste script block. selftest: aeo.schema_audit

Because the generator runs the same rules as the audit, what comes out scores clean in what comes next.

Keep the JSON-LD and the visible page saying the same thing. A FAQPage block whose questions do not appear in the body is a mismatch, not a boost.

STEP 04

Should you publish an llms.txt?

It is a plain markdown index for language models. Validate an existing file or build one from your page list.

Validate or build

aeo_llms_txt checks a live /llms.txt against the llmstxt.org format, or builds one: pass pages as {url, title, description, section} with a title and a one-line summary each, and it returns a file that passes its own validation. selftest: aeo.llms_txt

It also builds llms-full.txt for the version that inlines the content rather than linking it.

Honest framing

llms.txt is a young convention. Publishing one costs a static file and a paragraph of maintenance; treat it as a signal you control, not a switch that turns on citations. AdRails tells you whether the file is well-formed and complete relative to your sitemap. It does not claim any engine reads it.

Point the entries at pages that already pass aeo_page_audit. An index of unready pages does nothing.

STEP 05

How do you track share of answer without an API bill?

The calling agent asks the engines. AdRails scores what came back, and the delta since the last run.

How a panel is recorded

  • You ask. Your agent puts the prompts to ChatGPT, Claude and Perplexity in whatever interface you already pay for.
  • You pass the answers. aeo_visibility_record takes {engine, prompt, text, citations} per result.
  • AdRails scores. Mentions of your brand, rank against named competitors, which citations were used, sentiment, and the change since the previous panel. selftest: aeo.visibility

Why it is built this way

AdRails makes no LLM calls itself, so there is no metered API in the loop and no model choice made on your behalf. The panel is a record you own: same prompts, same engines, run weekly, and the delta is comparable because the inputs are identical.

Track five to fifteen prompts, not fifty. A prompt set that changes every week measures noise.

STEP 06

How do new pages get found quickly?

IndexNow tells Bing, Yandex, Seznam and Naver that a URL changed. Validation is the default.

The call

aeo_indexnow checks the key file at https://host/<key>.txt before sending anything. With submit=false, which is the default, it only reports what would be submitted. selftest: aeo.indexnow

Pass sitemap_since with an ISO date to send every sitemap URL changed since that point, rather than maintaining a hand-written list.

Scope and blame

This is the single mutating AEO tool: it submits URLs to four search engines. Everything else in the toolkit reads. If you want a weekly agent routine with no writes at all, leave IndexNow out of it or keep submit=false.

A missing or mismatched key file is refused before the request goes out, so a bad key does not burn a submission window.

STEP 07

How much AI traffic do you actually get?

Read your own server log and split referrals into verified, unverified and spoofed.

What the log pass extracts

  • Referrals. Visits from ChatGPT, Perplexity, Claude, Gemini and Copilot, with the landing page for each. selftest: aeo.ai_traffic
  • Crawler fetches. GPTBot, ClaudeBot, PerplexityBot and the rest, counted separately from human referral traffic.
  • The spoof filter. Claims matched against published IP ranges and split into verified, unverified and spoofed, with known scanner paths flagged.

Format and caution

Accepts nginx combined logs or JSON lines, read from a local file. Nothing is uploaded. Quote the verified count in reports, and treat the spoofed bucket as a reminder that user-agent strings are free to type.

A rising verified-crawler count with a flat referral count means the pages are being fetched and not cited: a page-readiness problem, not a crawl problem.

THE BRIDGE

What is the ads-to-answers bridge?

Your search terms report is a funded list of real questions. AdRails converts it into content briefs for the gaps.

Three steps, one tool

  • Terms become questions. aeo_content_gaps reads the campaign's clicked terms over the last N days and rewrites them as the questions people ask. selftest: aeo.ads_to_aeo
  • Pages are checked. Each question is matched against your pages to see which ones already answer it.
  • Gaps become briefs. The biggest unanswered questions come back as content briefs built only from facts you supply. Nothing is invented.

Why paid terms beat keyword tools

A clicked search term is demand with money behind it: someone typed that phrasing and the click was paid for. Keyword tools estimate. The terms report reports. Feed the terms with clicks and cost, not the long tail with zero impressions, and the briefs stay focused on questions worth answering.

Pair it with adsops_terms for the raw list and adsops_lp_diagnose to check whether the landing page already echoes the query. All 82 tools

ROUTINE

What does a weekly AEO routine look like for an agent?

Five steps, roughly fifteen minutes of agent work, one mutating call optional at the end.

01

Crawl health

Run aeo_site_audit on the site. If the 0-100 score dropped, find the named failing crawler or directive before touching anything else. selftest: aeo.crawl_audit

02

Page readiness

Run aeo_page_audit on the three pages that matter this week. Take the missing questions list as next week's headings, and fix the JSON-LD findings it returns. selftest: aeo.page_audit

03

Answer panel

Your agent asks the fixed prompt set, then records answers with aeo_visibility_record. Compare against last week's delta and note which citations changed. selftest: aeo.visibility

04

Traffic reality check

Run aeo_ai_traffic on the rotated log. Verified referrals up and citations flat is a page problem; spoofed volume up is a reporting problem. selftest: aeo.ai_traffic

05

Content gaps

Run aeo_content_gaps against the campaign. Pick the top two briefs, write them, then submit the changed URLs through aeo_indexnow with submit=true once the key file checks out. selftest: aeo.indexnow

AdRails is Apache-2.0, runs on your machine with your credentials, and needs Python 3.11+ with the standard library. Source at github.com/antonioac1/adrails.

FAQ

Straight answers.

What is AEO and how is it different from SEO?

AEO optimizes for being quoted inside an AI answer rather than ranked in a list of links. The technical work overlaps: crawlability, clean body text, structured data, canonical URLs. The difference is the unit of content. Answer engines extract a self-contained paragraph, so pages need answer-first paragraphs, question-shaped headings, FAQ blocks with specifics, and machine-readable JSON-LD. AdRails scores those properties directly with aeo_page_audit.

Does AdRails call ChatGPT or Perplexity for me?

No. aeo_visibility_record takes answers you pass in. The calling agent asks each engine the prompts, then records engine, prompt, text and citations. AdRails scores mentions, rank against competitors, citations and sentiment, and the change since the last run. AdRails makes no LLM calls itself, which keeps credentials and spend on your side and makes every score reproducible from the recorded panel.

Which AI crawlers does the audit check?

aeo_site_audit evaluates robots.txt rules under RFC 9309 longest-match for 18 crawlers: GPTBot, OAI-SearchBot, ChatGPT-User, ClaudeBot, Claude-User, Claude-SearchBot, PerplexityBot, Perplexity-User, Google-Extended, Googlebot, Bingbot, Applebot, Applebot-Extended, DuckAssistBot, Meta-ExternalAgent, Amazonbot, CCBot and Bytespider. It also checks llms.txt, sitemap, noindex/nosnippet, canonical tags and whether body text is available without JavaScript. Output is a 0-100 score.

How does the ads-to-answers bridge work?

aeo_content_gaps reads the campaign's real clicked search terms, rewrites them as the questions people actually ask, then fetches your pages to see which questions are already answered. Gaps come back as content briefs built only from facts you supply. Paid search is the cheapest research you will run: people typed the question and paid for the click, so the phrasing is proven demand rather than a keyword tool guess.

Is IndexNow submission safe to run from an agent?

aeo_indexnow validates by default. It checks the key file at https://host/<key>.txt before sending anything, and submit=false only reports what would change. Passing submit=true is the single mutating AEO call, pushing URLs to Bing, Yandex, Seznam and Naver. You can scope it with sitemap_since and an ISO date to send only pages changed since a known point.

How do I tell real AI traffic from spoofed bots?

aeo_ai_traffic reads a local nginx or JSON-lines log. Referrals from ChatGPT, Perplexity, Claude, Gemini and Copilot are matched to published IP ranges, and AI crawler fetches are split into verified, unverified and spoofed, with known scanner paths flagged. You get landing pages per engine and a verified count you can trust before quoting AI traffic numbers in a report.