Extending an SEO program to AI answers: the migration map
An SEO program already has keyword lists, market splits, a tracking schedule, competitor sets and a reporting routine, and nearly all of it carries over to AI answers. The unit of measurement changes: a position in a list becomes a mention or a citation inside a written answer, and a single daily check becomes one sample from a distribution. Below, each SEO practice is mapped to its AI-answer equivalent, followed by a pipeline that runs both on one topic list and a list of what to drop.
The map: what carries over
| SEO practice | AI-answer equivalent | Where it lives in the API response |
|---|---|---|
| Keyword list | Prompt set: the same intents, phrased as questions | Request prompt |
| Market (country, city, language) | country on every engine; ChatGPT and Gemini answer from 213 countries, Copilot, Perplexity and Grok from any country; Gemini also takes a US state; Google Search and AI Mode take location |
Request fields |
| Rank position | Mention rate and citation share per prompt set | result.text, result.sources[] |
| Position within the list | Order of first mention in the answer text | result.text |
| SERP features | Answer blocks: citation pills, product listings | result.citationPills[], result.inlineProducts[] (ChatGPT, with include.shopping) |
| Competitor rank diff | Competitor mention and citation diff | Same fields, other brands and domains |
| Scheduled rank checks | Scheduled async batches | POST /v1/async/task/batch |
| Rank tracker exports | Stored raw JSON per run | response.result of each finished task |
The operating model carries over too. A keyword × market matrix becomes a prompt × market × engine matrix, and scheduled rank checks become async batches of up to 500 tasks, collected by polling or webhooks.
What changes shape
There is no single results page
Each engine searches, reads and cites differently. ChatGPT, Gemini, Perplexity, Copilot, Grok and AI Mode each need separate columns in every report. A blended “AI visibility score” hides the engine where you are absent, which is usually what you need to act on.
Results vary per run
A ranking checked twice in an hour usually returns the same position. An AI answer checked twice can name different brands and cite different pages. Treat each run as a draw: track rates over several runs, show the spread, and only call a change when it holds across runs. AI answer volatility and sampling vs census cover how many runs you need.
Answers are prose
A ranking is a position; an answer is written text. Your brand can be recommended, mentioned in passing, cited as evidence without being named, or absent. Mentions and citations are different events, and a report needs both.
Prompts are longer than keywords
“crm small business” becomes “What’s the best CRM for a 10-person sales team that already uses Gmail?” The added context (team size, existing tools, budget) changes which brands the answer recommends, so the prompt set needs several phrasings per intent, not one.
What stays the same
- Crawlability and indexing. Google’s AI features documentation says a page must be indexed and eligible to show in Search with a snippet to appear as a supporting link in AI Overviews or AI Mode, and that no extra requirements or special optimizations are needed (checked 2026-09-17).
- Snippet controls. The same page says
nosnippet,data-nosnippet,max-snippetandnoindexgovern what Google’s AI features can show from a page. An aggressive snippet restriction added for SEO reasons also limits AI Overview and AI Mode use. - Robots.txt, with new user agents. OpenAI’s crawler documentation separates
OAI-SearchBot(sites opted out are not shown in ChatGPT search answers) fromGPTBot(training; blocking it does not affect ChatGPT search results) (checked 2026-09-17). Audit robots.txt rules that were written before these agents existed. - Content quality. Pages that answer the question directly, with specific and attributable facts, serve both rankings and citations.
Migrating a keyword program
- Export the keyword list with its metadata: intent, market, priority, and the landing page you want to win.
- Rewrite keywords as prompts. Keep one row per intent and write two or three natural phrasings. Add comparison (“X vs Y for Z”) and problem (“how do I…”) prompts; they are where assistants recommend products. Prompt set design has templates.
- Map markets to request fields. Countries map to
country. ChatGPT, Gemini, AI Mode and Google Search answer from 213 countries, and Copilot, Perplexity and Grok take anycountry; Gemini also takes a USstate. Check each engine’s supported countries withGET /v1/countries?model=chatgpt(orgemini). For Google Search and AI Mode, keep your existinggl,hlandlocationsettings. - Choose engines per segment. Not every prompt needs every engine. Commercial prompts for a consumer product might warrant both ChatGPT and Gemini; a B2B developer tool might start with ChatGPT alone.
- Keep Google Search in the same run. Request the classic results with the AI Overview (
include.aioverview) so you can compare ranking, AI Overview citation and assistant citation for the same topic. - Schedule and store. Submit weekly batches with idempotency keys derived from prompt, market, engine and date, and store every raw response. Idempotency keys explained shows the pattern.
- Rebuild the report. Replace position charts with mention rate and citation share per engine, with competitor columns. An AI visibility report template is a starting layout.
Comparing rankings with citations
The most useful early report puts SEO and AI data side by side for each topic. For every keyword, compare the domains in Google’s organic top 10 with the domains cited in the AI Overview, in Gemini and in ChatGPT. Four cases come out:
| Ranks organically | Cited in answers | Reading | Action |
|---|---|---|---|
| Yes | Yes | Covered | Protect the page; watch competitors |
| Yes | No | Engines prefer other sources | Study the cited pages’ format and claims |
| No | Yes | Cited from outside classic top 10 | Learn why; strengthen that page |
| No | No | Invisible | Content or coverage gap |
The pipeline below, in Python with requests, submits one batch with three tasks per keyword, waits for results, and prints the comparison.
import os
import time
from urllib.parse import urlparse
import requests
API = "https://api.answerline.dev"
HEADERS = {"Authorization": f"Bearer {os.environ['ANSWERLINE_API_KEY']}"}
MY_DOMAIN = "acme.com"
DAY = "2026-09-20"
topics = [
{"keyword": "invoicing software freelancers",
"prompt": "What is the best invoicing software for freelancers?"},
{"keyword": "time tracking app agencies",
"prompt": "Which time tracking app works best for a small agency?"},
]
def host(url):
h = urlparse(url).hostname or ""
return h[4:] if h.startswith("www.") else h
tasks = []
for i, t in enumerate(topics):
tasks += [
{"taskType": "GOOGLE",
"payload": {"query": t["keyword"], "country": "US", "include": {"aioverview": {"markdown": False}}},
"idempotencyKey": f"bridge-google-{i}-US-{DAY}"},
{"taskType": "GEMINI",
"payload": {"prompt": t["prompt"], "country": "US"},
"idempotencyKey": f"bridge-gemini-{i}-US-{DAY}"},
{"taskType": "CHATGPT",
"payload": {"prompt": t["prompt"], "country": "US"},
"idempotencyKey": f"bridge-chatgpt-{i}-US-{DAY}"},
]
batch = requests.post(f"{API}/v1/async/task/batch", headers=HEADERS, json=tasks, timeout=360)
batch.raise_for_status()
ids = {r["index"]: r["task"]["id"] for r in batch.json()["results"] if r["success"]}
def wait(task_id):
while True:
body = requests.get(f"{API}/v1/async/task/{task_id}", headers=HEADERS, timeout=30).json()
if body["task"]["status"] in ("COMPLETED", "FAILED"):
return body
time.sleep(10)
for i, t in enumerate(topics):
if any(i * 3 + k not in ids for k in range(3)):
print(t["keyword"], "has a task that was not created; see the batch results")
continue
google, gemini, chatgpt = (wait(ids[i * 3 + k]) for k in range(3))
if "FAILED" in (google["task"]["status"], gemini["task"]["status"], chatgpt["task"]["status"]):
print(t["keyword"], "has a failed task; resubmit later")
continue
g, a, c = google["response"]["result"], gemini["response"]["result"], chatgpt["response"]["result"]
organic = {host(r["link"]) for r in g.get("organicResults", [])[:10]}
overview = g.get("aioverview") or {}
cited = {
"ai_overview": {host(s["url"]) for s in overview.get("sources", [])},
"gemini": {host(s["url"]) for s in a.get("sources", [])},
"chatgpt": {host(s["url"]) for s in c.get("sources", [])},
}
print(t["keyword"])
print(" ranks top 10:", MY_DOMAIN in organic)
for engine, domains in cited.items():
print(f" cited in {engine}:", MY_DOMAIN in domains, "| cited but not ranking:", sorted(domains - organic)[:5])
The polling loop keeps the example short. For hundreds of keywords, add a webhook to each task instead and let results arrive; see rank tracking pipelines with batches.
Fan-out coverage
Fan-out coverage, the share of an engine’s own searches you rank for, needs those searches. ChatGPT’s searchQueries[] (with include.searchQueries, which is free) comes back empty on the answers served today, so the check cannot be run for ChatGPT. The “cited but not ranking” domains above are the link between the two programs instead. Query fan-out covers the idea.
Worked cost example
| Task per keyword | Price (Starter) |
|---|---|
GOOGLE with include.aioverview (base $0.00103, +$0.00071) |
$0.00174 |
GEMINI |
$0.0006 |
CHATGPT |
$0.0006 |
| Total per keyword per run | $0.00294 |
For example, 200 keywords run weekly is $0.588 per run and $2.35 over four runs. The tiers, and how 30-day spend lowers the price, are on the pricing page; cost planning goes further.
What to drop
- Blended visibility scores that average engines into one number with no way back to the underlying answers.
- Daily position charts without variance. For answers, a single daily point overstates precision; show rates across runs.
- Metrics that cannot be traced to a stored response. If a client asks “where did we lose that mention?”, you should be able to open the exact answer text and sources.
- Branded-only tracking. “What is Acme?” is the least informative prompt; category and comparison prompts decide consideration.
Pitfalls during the migration
- Translating keywords word for word. Prompts need natural phrasing and context, not keyword syntax.
- Changing the prompt set mid-trend. Version the set; add prompts as a new cohort so historical rates stay comparable.
- Comparing engines on different days. Run all engines for a topic in the same batch so differences are not timing artifacts.
- Ignoring failed tasks. A
FAILEDtask is not charged; resubmit it rather than counting it as “not mentioned”.
Start with the quickstart, or see rank tracking and AI visibility tracking for the two halves of the hybrid program.
Questions
Can I reuse my SEO keyword list for AI visibility tracking?
Yes, as the starting point. Rewrite each keyword as the question a buyer would ask an assistant, keep the intent grouping, and add comparison and problem prompts that keywords rarely capture.
What replaces rank position in AI answers?
Mention rate (the share of answers that name you) and citation share (your share of all cited sources), computed per engine and market over repeated runs. Where the answer is a list, the order in which brands are first mentioned in the text is a useful secondary signal.
Does technical SEO still matter for AI answers?
Yes. Google says a page must be indexed and eligible to show with a snippet to be a supporting link in AI Overviews or AI Mode, and OpenAI says sites that opt out of OAI-SearchBot are not shown in ChatGPT search answers.
How often should AI answers be tracked compared with rankings?
At least weekly per prompt, engine and market, with every raw response stored. Answers vary between runs, so trends across several runs are more reliable than any single check.
How do I connect SEO and GEO data?
Run the same topics through Google Search and the AI engines, then compare the domains that rank organically with the domains the answers cite. ChatGPT's searchQueries field comes back empty on the answers served today, so the cited domains are the link between the two.