GEO checklist: 24 items, each with a check you can run
This checklist has 24 items in five groups. Each item has a check: a command, a query or a metric with a pass condition. You can re-run the list after every deploy and content change.
Run the groups in order. Measurement (group E) is last in the list but first in time: take a baseline before changing anything, or you won’t know which item mattered.
Summary table
| # | Item | Check | Pass when |
|---|---|---|---|
| A1 | Search crawlers allowed | robots.txt parser per user agent | Target agents may fetch key URLs |
| A2 | Robots rules match intent | Diff robots.txt against a written policy | No unintended blocks |
| A3 | Key pages indexed in Google | Search Console URL Inspection | “URL is on Google” |
| A4 | Key pages known to Bing | Bing Webmaster Tools URL Inspection | Indexed |
| A5 | Sitemaps current | Sitemap lastmod vs deploy log |
Updated pages listed with new dates |
| A6 | Firewall doesn’t block allowed bots | Server logs by user agent | 2xx responses to verified bots you allow |
| B1 | Snippet-eligible | Meta robots and X-Robots-Tag scan |
No nosnippet, no tiny max-snippet, no noindex on target pages |
| B2 | Answers present in raw HTML | Fetch without JavaScript, search for key facts | Facts found |
| B3 | Structured data matches visible text | Rich Results Test plus manual diff | No values absent from the page |
| B4 | Stable, descriptive URLs and titles | Crawl report | One canonical URL per topic |
| C1 | One page per question cluster | Map prompts to pages | Every prompt cluster has a target page |
| C2 | Answer in the first two sentences | Manual review sample | Direct answer precedes context |
| C3 | Specific, attributable facts | Count of numbers, dates, named specs | Each target page states its key facts explicitly |
| C4 | Comparison and alternatives coverage | Map comparison prompts to pages | Page exists for top competitor pairs |
| C5 | Visible freshness | Dates on page, datePublished in collected sources |
Target pages show a current date |
| C6 | Facts consistent across your properties | Scripted diff of price, limits, names | No conflicting values |
| D1 | Present on the domains engines cite | Top cited domains where you’re absent | Action owner per domain |
| D2 | Profiles and listings current | Google Business Profile, Merchant Center, directories | Match first-party facts |
| D3 | Fan-out queries covered in search | Your rank on engines’ own queries | Top 10 on a growing share |
| E1 | Versioned prompt set | Prompt file under version control | Ids, intents, markets recorded |
| E2 | Baseline with repeated runs | Interval width per metric | Narrow enough to see your target change |
| E3 | Accuracy tracked | Wrong-fact rate in answers naming you | Tracked and alerting |
| E4 | Control group | Prompts excluded from changes | Exists and measured on the same schedule |
| E5 | First-party reports joined | Search Console, Bing AI Performance | Pulled on the same cadence |
The rest of the post explains each item and its check.
A. Access and indexing
A1. Search crawlers can fetch your key pages
An engine can’t cite what its search system never fetched. For ChatGPT, OpenAI states that sites opted out of OAI-SearchBot are not shown in ChatGPT search answers, and that it can take about 24 hours after a robots.txt update for its systems to adjust (OpenAI crawlers, checked 2026-09-17). For Google’s AI features, Google lists allowing crawling in robots.txt among the practices that apply (AI features and your website, checked 2026-09-17). AI crawlers lists the agents for each vendor.
Check: parse your live robots.txt for each agent against each key URL.
from urllib import robotparser
SITE = "https://www.example.com"
AGENTS = ["OAI-SearchBot", "Googlebot", "Bingbot", "PerplexityBot"]
URLS = ["/", "/pricing", "/compare/acme-vs-globex", "/docs/getting-started"]
rp = robotparser.RobotFileParser(f"{SITE}/robots.txt")
rp.read()
for agent in AGENTS:
blocked = [u for u in URLS if not rp.can_fetch(agent, SITE + u)]
print(f"{agent:15} {'PASS' if not blocked else 'FAIL ' + str(blocked)}")
Pass: every agent you intend to allow can fetch every key URL.
A2. Robots rules match a written policy
Vendors separate crawlers by purpose. OpenAI’s GPTBot crawls content that may be used for training, while OAI-SearchBot surfaces sites in search; disallowing one does not disallow the other. Write down which agents you allow and why, then keep robots.txt in sync with that document.
Check: a CI step that fetches robots.txt after deploy and runs the A1 script against the policy file. Pass: zero differences.
A3. Key pages are indexed in Google
Google says a page must be indexed and eligible to be shown with a snippet to be a supporting link in AI Overviews or AI Mode.
Check: URL Inspection in Search Console for each target page. Pass: the page is on Google and the canonical Google selected is the one you declared.
A4. Key pages are known to Bing
Microsoft’s AI Performance report in Bing Webmaster Tools reports citations across Microsoft Copilot, AI-generated summaries in Bing and select partner integrations (Bing Webmaster Blog, checked 2026-09-17). Verify your site there.
Check: URL Inspection in Bing Webmaster Tools. Pass: indexed. The same post recommends IndexNow to notify participating search engines when content is added, updated or removed.
A5. Sitemaps are current
Check: compare each target URL’s <lastmod> with the last deploy that changed it. Pass: changed pages carry the new date within one deploy.
A6. Your firewall or CDN doesn’t block bots you allow
A robots.txt allow is useless if a bot-management rule returns 403 to that agent.
Check: group a week of access logs by user agent for the agents in your policy and count status codes. Verify the requests really come from the vendor using the verification method each vendor documents. Pass: allowed, verified agents get 2xx on key pages.
B. Extractability
B1. Pages are snippet-eligible
Google names nosnippet, data-nosnippet, max-snippet and noindex as the controls that limit what its Search features, including AI features, show from a page. A template-level max-snippet:0 or a stray data-nosnippet wrapper can silently remove the answer text.
Check: scan target pages for the meta tag and header.
import re
import requests
PAGES = ["https://www.example.com/pricing", "https://www.example.com/compare/acme-vs-globex"]
BAD = re.compile(r"nosnippet|max-snippet\s*:\s*(0|[1-4]?\d)\b|noindex", re.IGNORECASE)
for url in PAGES:
r = requests.get(url, timeout=30)
header = r.headers.get("X-Robots-Tag", "")
metas = re.findall(r'<meta[^>]+name=["\']robots["\'][^>]*>', r.text, re.IGNORECASE)
wrappers = len(re.findall(r"data-nosnippet", r.text))
hits = [h for h in [header, *metas] if BAD.search(h)]
print(url, "PASS" if not hits and not wrappers else f"FAIL {hits} data-nosnippet={wrappers}")
The threshold of 50 characters in the pattern is this checklist’s choice, not a Google rule; set it to what your policy allows. Pass: no restrictive directives on pages you want cited.
B2. The answer is in the raw HTML
Google lists keeping important content in textual form among the practices for its AI features. Content rendered only by client-side JavaScript or shown only in images is a risk for any crawler that doesn’t render.
Check: fetch each target page with a plain HTTP client and search for the three facts the page exists to state (a price, a limit, a named feature). Pass: all three found in the HTML response.
B3. Structured data matches visible text
Google’s AI features page lists making sure structured data matches the visible text. Markup does not earn inclusion on its own; structured data and AI visibility reviews what the sources say.
Check: Rich Results Test for validity, then a script that extracts JSON-LD values (price, rating, name) and confirms each appears in the visible text. Pass: no value in markup that the page doesn’t show.
B4. One canonical URL per topic
Check: a crawl report of duplicate titles, duplicate H1s and conflicting canonicals among target pages. Pass: each topic resolves to one URL.
C. Answer-ready content
C1. Every prompt cluster has a target page
Check: a table mapping each prompt cluster in your set (E1) to the page you want cited. Pass: no cluster without a page. Clusters without one are your content backlog.
C2. The answer comes first
Check: for a sample of target pages, read the first two sentences under the heading that matches the question. Pass: they answer it directly, without preamble.
C3. Facts are specific and attributable
The paper that introduced GEO reported that adding statistics, quotations and citations improved source visibility on its benchmark, while keyword stuffing did not (GEO, Aggarwal et al., checked 2026-09-17); what is GEO states the scope of those results.
Check: list the claims you want engines to repeat (a price, an integration count, a certification) and confirm each target page states them as explicit, dated text. Pass: every listed claim present. Later, E3 tells you which claims engines repeat.
C4. Comparison prompts have pages
Comparison and “alternatives to X” prompts are where engines recommend brands.
Check: for your top competitor pairs, a page exists that compares on concrete attributes in a table. Pass: coverage of the pairs in your prompt set.
C5. Freshness is visible
ChatGPT sources in this API’s response can carry datePublished, so you can see the dates attached to what gets cited.
Check: target pages show a last-updated date, and the date is real (tied to a content change). Pass: within your review interval.
C6. Facts agree everywhere you control
Check: a script that extracts price, plan limits and product names from your site, docs and app store listings and diffs them. Pass: no conflicts. Conflicting first-party facts give engines a choice you don’t control.
D. Off-site corroboration
D1. You’re present on the domains engines cite
When engines recommend brands they often cite third-party pages. You find which ones by counting cited domains on prompts where you’re absent.
Check: from stored answers, list the top cited domains for prompts that don’t name you (code below). Pass: each of the top domains has an owner and an action (a listing, a review, a correction, an update to a comparison). How AI engines choose citations covers the patterns.
from collections import Counter
from urllib.parse import urlparse
def absent_domain_counts(answers, brand_regex):
"""answers: stored result objects from any engine, each with text and sources."""
counts = Counter()
for result in answers:
if brand_regex.search(result.get("text", "")):
continue
for s in result.get("sources", []):
counts[urlparse(s.get("url", "")).netloc.removeprefix("www.")] += 1
return counts.most_common(20)
D2. Profiles and listings are current
Google’s AI features page recommends keeping Business Profile and Merchant Center information up to date.
Check: diff Business Profile hours, address and categories, and Merchant Center prices, against first-party facts. Pass: no differences.
D3. You rank for the engines’ own searches
Assistants search before answering. This API returns those searches as searchQueries[] on ChatGPT (with include.searchQueries), Copilot and Grok, and search_model_queries on Perplexity.
Check: collect fan-out queries for your prompt set, then check your organic position for each.
import os
import requests
API = "https://api.answerline.dev"
HEADERS = {"Authorization": f"Bearer {os.environ['ANSWERLINE_API_KEY']}"}
DOMAIN = "example.com"
def call(path, body):
r = requests.post(f"{API}{path}", headers=HEADERS, json=body, timeout=360)
r.raise_for_status()
return r.json()["result"]
answer = call("/v1/monitor/chatgpt", {
"prompt": "best password manager for a small accounting firm",
"country": "US",
"include": {"searchQueries": True},
})
covered = []
for q in answer.get("searchQueries", []):
serp = call("/v1/monitor/google", {"query": q, "country": "US"})
ours = [o["position"] for o in serp.get("organicResults", []) if DOMAIN in o.get("link", "")]
covered.append((q, min(ours) if ours else None))
share = sum(1 for _, pos in covered if pos is not None and pos <= 10) / max(len(covered), 1)
print(covered, f"fan-out coverage: {share:.0%}")
Pass: coverage is tracked per prompt cluster and rising. Query fan-out goes deeper.
E. Measurement
E1. A versioned prompt set exists
Check: a file under version control with prompt id, text, intent, market, target page and date added. Pass: every metric is keyed by prompt id. Prompt set design covers sourcing and sizing.
E2. A baseline with repeated runs
Answers vary between runs, so a single run per prompt can’t show a change. The standard error of a rate p over n runs is sqrt(p(1 − p) / n); at p = 0.5 and n = 100 that is 0.05, roughly ±10 points at 95% confidence.
Check: compute the 95% interval width for mention rate per engine over the baseline window. Pass: the width is smaller than the change you want to detect. AI answer volatility and sampling vs census cover sizing.
E3. Accuracy is tracked
Check: for answers that name you, test each key fact from C3 (for example, the price regex) and record the wrong-fact rate. Pass: tracked per engine with an alert threshold.
E4. A control group exists
Check: a tagged subset of prompts whose target pages you don’t change during an experiment. Pass: you report the change in treated prompts minus the change in controls.
E5. First-party reports are joined in
Google includes AI features in overall search traffic in Search Console’s Performance report, within the Web search type. Bing’s AI Performance report shows citations, grounding queries and, since a June 2026 update, citation share per grounding query (Bing Search Blog, checked 2026-09-17).
Check: both exports land in the same store as your collected answers, on the same cadence. Pass: one dashboard shows citations from collected answers next to clicks and Bing citations for the same pages.
Running the measurement groups on a schedule
Groups D3 and E need answers collected on a schedule. Submit them as async tasks in batches of up to 500 with POST /v1/async/task/batch, with an idempotencyKey per prompt, engine, market and day, and a webhook URL for delivery:
[
{
"taskType": "CHATGPT",
"payload": { "prompt": "best password manager for a small accounting firm", "country": "US", "include": { "searchQueries": true } },
"idempotencyKey": "p017-chatgpt-US-2026-09-17",
"webhook": { "url": "https://hooks.example.com/answers" }
},
{
"taskType": "GEMINI",
"payload": { "prompt": "best password manager for a small accounting firm", "country": "US" },
"idempotencyKey": "p017-gemini-US-2026-09-17",
"webhook": { "url": "https://hooks.example.com/answers" }
}
]
Those two tasks cost 7 and 4 credits. A baseline of 30 prompts, 5 runs each, on those two engines is 30 × 5 × 11 = 1,650 credits; the D3 follow-up of Google checks is 3 credits per fan-out query per task. Cost planning and pricing help size a plan.
Order of work
- E1 and E2: write the prompt set and take a baseline.
- A1 to A6 and B1 to B4: fix access and extractability; these are cheap and binary.
- D3: find fan-out queries you don’t rank for; hand them to SEO.
- C1 to C6: write or fix target pages for absent clusters.
- D1 and D2: work the cited domains.
- E3 to E5: measure accuracy, compare against controls, join first-party data.
Re-run A and B on every deploy that touches templates, headers or robots.txt. To start collecting answers, follow the quickstart.
Questions
What should a GEO checklist include?
Five groups: crawler access and indexing, snippet and content extractability, answer-ready content, off-site corroboration on the domains engines cite, and measurement. Each item should have a check that returns pass or fail, so the checklist can be re-run after changes.
Which robots.txt rule matters most for ChatGPT visibility?
OAI-SearchBot. OpenAI states that sites opted out of OAI-SearchBot are not shown in ChatGPT search answers, and that robots.txt changes can take about 24 hours to be reflected. GPTBot controls training use, not search surfacing.
Can nosnippet remove a page from Google AI Overviews?
Google lists nosnippet, data-nosnippet, max-snippet and noindex as the controls that limit what is shown from a page in Search, including its AI features. A page must be indexed and eligible to show with a snippet to be a supporting link in AI Overviews or AI Mode.
How do I know whether a GEO change worked?
Measure mention rate and citation share on a fixed prompt set before and after, with several runs per prompt, and compare the change on prompts you edited content for against a control group you did not touch.
How often should I re-run the checklist?
Run the technical checks on every deploy that touches robots.txt, headers or templates, and the measurement checks on your monitoring cadence, typically weekly. Content and off-site checks fit a monthly review.