Skip to content
All skills

GEO (AI answer engines)

Claude skill atlas-geo · for Editor, Frontend

Generative Engine Optimization for the Atlas: being found, understood and cited by ChatGPT, Claude, Perplexity, Copilot and Google AI Overviews. Covers the robots.txt policy for AI bots, llms.txt and llms-full.txt, .md twins, server-rendered text for non-JS crawlers, answer-first writing, quotable sourced facts, entity clarity (Wikidata), honest freshness and geographic signals. Use when writing or templating content, adding a public page type, or changing robots, llms.txt or markdown routes.

GEO: being cited by AI answer engines

Answer engines quote pages they can fetch without JavaScript, understand as clear entities, trust (sources, named authors, honest dates) and lift as self-contained passages. Classic SEO (atlas-seo) is the base: Google's AI features need no special files, only an indexable page with good text. Everything here adds to it.

Before you start

  • Related skills: atlas-seo (canonical, sitemap, dates, JSON-LD builders), atlas-content (house style for writers), atlas-architecture (static/ISR rule), atlas-maps-data (globe, indicators), atlas-performance.
  • Decision record: ADR-021 in docs/architecture/10-rozhodnuti-adr.md (open to AI crawlers).
  • Tests: src/lib/seo/llms-robots.test.ts, src/lib/seo/markdown.test.ts, src/features/seo/documents.test.ts, e2e tests/seo.spec.ts.
  • Bot tables, llms.txt anatomy and evidence: reference.

Rules

Crawler access (robots.txt)

  • Today's policy (owner decision): every search engine, AI search/assistant bot and AI training bot is allowed. ALLOWED_BOTS in src/lib/seo/robots.ts names them in three groups, and robotsRules() gives the named group the same rules as *: allow /, disallow only /api/, /admin, /auth/ and PRIVATE_PATHS.
  • Never block a search or retrieval token: Googlebot, Bingbot, OAI-SearchBot, ChatGPT-User, Claude-SearchBot, Claude-User, PerplexityBot, Perplexity-User. Blocking OAI-SearchBot alone removes the Atlas from ChatGPT search answers.
  • Training tokens (GPTBot, ClaudeBot, CCBot, Google-Extended, Applebot-Extended, Amazonbot, Meta-ExternalAgent) stay allowed. Closing one is an owner decision; do it by moving the token into its own Disallow group, never by IP or user-agent blocking. Disallowing Google-Extended also stops Gemini grounding, but not Google Search.
  • Bots must reach robots.txt and pages: make sure Vercel Firewall or bot protection never challenges or 403s the allowed bots (Vercel team → project → Firewall).
  • A new bot token goes into the right group of ALLOWED_BOTS with its operator, and llms-robots.test.ts is updated.

Rendering for crawlers that don't run JavaScript

  • GPTBot, OAI-SearchBot, ClaudeBot, PerplexityBot and CCBot fetch HTML and do not execute JS. Main text, headings, numbers with their sources, author, dates and navigation links must be in the server-rendered HTML.
  • Every client-only view (globe, charts, embeds in VisualsCarousel, map visuals) has a text equivalent (rule in atlas-maps-data). For crawlers it must be in the server HTML: a short summary and the key numbers with source and year.
  • Public routes stay prerendered (atlas-architecture; CI guard scripts/check-static-routes.mjs). Next 16 caveat: on a dynamic route, generateMetadata output is streamed into <body> for any user agent not in htmlLimitedBots. The default list (node_modules/next/dist/shared/lib/router/utils/html-bots.js) covers Google, Bing and social bots but not GPTBot, OAI-SearchBot, ClaudeBot, Claude-SearchBot or PerplexityBot. Those bots would then miss title, canonical and description. Keep the route static. If one ever must be dynamic, set htmlLimitedBots in next.config.ts to the default pattern plus those tokens (the option replaces the default list, it doesn't extend it).
  • Keep redirects to one hop and old slugs as 308 forever (atlas-seo): AI indexes keep stale URLs for a long time.

llms.txt and llms-full.txt

  • /llms.txt and /llms-full.txt are route handlers (src/app/[locale]/llms.txt/route.ts, llms-full.txt/route.ts, both llmsRoute() from src/features/seo/feeds.ts). Content comes from getLlmsTxt / getLlmsFullTxt (src/features/seo/queries.ts); the format is built by llmsTxt / llmsFullTxt (src/lib/seo/llms.ts).
  • They read the same cached DB queries as the sitemap, so they never list a page that 404s. Never hand-edit a static llms file.
  • Shape (llmstxt.org): H1 site name, > summary, short detail paragraphs, H2 sections of - [Name](url): note, a final ## Optional section. Keep the citation-format note.
  • A new public section is added to getLlmsTxt in the same PR that adds it to the sitemap.
  • llms-full.txt is about 800 KB. Split it per section if it grows past a few MB.
  • Every page links it: the [locale] layout emits <link rel="describedby"> to llms.txt and the RSS/Atom alternates. Keep them.
  • llms.txt is a community proposal, not a standard, and Google ignores it. Keep it accurate; don't invest beyond that.

Markdown twins

  • Articles have .md twins: /topics/<slug>.md and /news/<slug>.md. src/proxy.ts rewrites them to src/app/[locale]/md/[kind]/[slug]/route.ts, which renders articleMarkdown() (src/features/seo/documents.ts) with an HTTP Link: <html url>; rel="canonical" header.
  • A page advertises its twin with pageMetadata({ markdown: true }) (<link rel="alternate" type="text/markdown">). Set it only when the twin exists: never advertise a 404.
  • Adding a twin for another page type means extending the route's kind check and generateStaticParams, the proxy's .md rewrite, a document builder in documents.ts, and markdown: true on that page, in one PR.

Answer-first content (editors and templates)

  • Open every topic, news item, country, region and issue page with an "In short" answer of 2–3 sentences that names the entity, the place and the time ("As of 2025, …").
  • For topics, editors write it in the article's SEO form, field "In short (GEO)" (geo_summary, max GEO_SUMMARY_MAX = 800 characters). EncyclopediaArticle shows it at the top of the dossier and the page feeds it to JSON-LD abstract. Any new "In short" field works the same way: visible first, then structured data.
  • Write sentences that stand alone when quoted: never start with "This", "It" or "The above".
  • Use question-shaped H2/H3 headings where natural ("Who counts as a refugee?"), each followed by a direct one- or two-sentence answer.
  • Every statistic in text has value, unit, year, named source and a link, in HTML text, not only in a chart or on the globe. Indicator cards already do this (MetricCards, SourceLink in src/components/atlas/ui.tsx).
  • Quote primary voices (treaty text, a UN body, a named expert) briefly, in <blockquote> with <cite>. Cite sources inline as real <a href> links to primary sources (UNHCR, World Bank, UNDP, V-Dem) and keep the resource list (it becomes JSON-LD citation).
  • Never stuff keywords or lists of place names: it scores below baseline in the GEO study and is a Google spam pattern.
  • Don't hide key answers in images, PDFs or closed widgets. Tabs and accordions are fine.

Entity clarity

  • One canonical name per entity everywhere: title, H1, JSON-LD name, llms.txt. Formal or other names go into alternateName (countries: nameFormal).
  • Link entities to Wikidata. Countries: countrySameAs(iso3) reads src/config/wikidata-countries.json (Wikidata + English Wikipedia). The organization's sameAs comes from src/config/organization.ts and site settings. Add sameAs for a region or issue only when a matching Wikidata item really exists.
  • Articles name their places with the same @ids: about (countries) and contentLocation (region) in articleNode.
  • Named authors with a real profile page are the strongest "who" signal; author rules are in atlas-seo.

Freshness, honestly

  • Visible "Published"/"Updated" dates, JSON-LD dates and sitemap lastmod agree and move only on real content changes. That rule lives in atlas-seo (§ Dates, § Sitemaps). Never refresh dates to look current: engines learn to ignore them.
  • Say "As of <year>" in text next to time-sensitive facts, so a quoted sentence stays true.
  • IndexNow (atlas-seo) is the fast path into Bing, which grounds Copilot and ChatGPT search.

Geographic signals

  • Place identity lives in JSON-LD, not meta tags. countryNode emits Country with ISO 3166-1 alpha-2/alpha-3 identifiers, Wikidata/Wikipedia sameAs, geo (GeoCoordinates), containedInPlace (region) and indicator values with source and year (additionalProperty). groupPlaceNode emits a region or issue with containsPlace (its countries).
  • geoMeta() (src/lib/seo/index.ts) writes geo.position, geo.region, ICBM and place:location:*. Google ignores them; they are legacy and harmless. Use them only for real places (countries, regions). Don't add new ones and don't spend effort on them.

Measuring

  • Google AI Overviews and AI Mode clicks are inside Search Console Performance → "Web"; there is no separate filter. Bing Webmaster Tools has an AI Performance report (Copilot citations). Links: atlas-seo reference.
  • Watch AI referrers (chatgpt.com, perplexity.ai, claude.ai, copilot.microsoft.com) in Vercel Analytics, and bot user agents in the Vercel logs.

Checklist

  • [ ] No search or retrieval bot blocked; robots change has an owner decision behind it.
  • [ ] Main text, numbers with sources, author and dates are in the server HTML (view source).
  • [ ] Route stays prerendered (no htmlLimitedBots workaround needed).
  • [ ] New public section is in getLlmsTxt and the sitemap.
  • [ ] markdown: true only where the .md twin returns 200.
  • [ ] Page opens with a visible "In short" answer naming entity, place and time.
  • [ ] Every statistic: value, unit, year, source, link, as text.
  • [ ] Entity names consistent; sameAs only to the right Wikidata item.
  • [ ] No keyword or place-name stuffing; no fake freshness.

Anti-patterns

  • Blocking AI bots "to protect content": the Atlas exists to be read and cited (ADR-021).
  • Putting the key numbers only on the globe or in a chart: non-JS crawlers see nothing.
  • A JSON-LD abstract or FAQ that readers can't see: a mismatch, and untrusted.
  • Making a public route dynamic: metadata streams into <body> for AI bots, and the CDN cache is lost (atlas-performance).
  • Writing for bots (place-name lists, "best atlas" phrases): measurably worse.

Known gaps (2026-10-16)

  1. Map pages carry little server-rendered text: about 1.0 k characters on /, 1.5 k on /region/east-asia, 2.0 k on /global-issue/energy-transition, 2.4 k on /country/china, against about 28 k on a topic. Add a server-rendered "In short", a key-facts block with source and year, and links to related articles.
  2. No .md twins for countries, regions and issues (/country/china.md returns 404).
  3. Only topics have an editor-written "In short" (geo_summary, rendered by EncyclopediaArticle). News, countries, regions and issues have no equivalent field yet.
  4. geoMeta is also emitted on global issues and on news (with region centroids such as 0;20); an issue is not a place. Low priority.

Sources

GEO paper, Aggarwal et al., KDD 2024 · Google, AI features and your website · Microsoft, content for AI search answers · llms.txt · Vercel, AI crawlers and JS. Full list in reference.

Files of this skill

reference.md

GEO reference

Detail for atlas-geo.

Bots by class

The Atlas allows all of these today (ALLOWED_BOTS, src/lib/seo/robots.ts).

Search and retrieval (must stay allowed)

TokenOperatorWhat it doesNotes
GooglebotGoogleSearch, AI Overviews, AI Moderobots.txt also governs Google's AI features; renders JS
BingbotMicrosoftBing, grounding for Copilot and ChatGPT searchin Next's htmlLimitedBots default
OAI-SearchBotOpenAIChatGPT search resultsa robots change takes about 24 h; IPs published by OpenAI
ChatGPT-UserOpenAIfetches a page a user asked aboutrobots.txt may not apply
Claude-SearchBotAnthropicClaude's search indexblocking reduces visibility in Claude answers
Claude-UserAnthropicfetches a page a user asked about
PerplexityBotPerplexityPerplexity search indexnot used for training
Perplexity-UserPerplexityfetches a page a user asked aboutgenerally ignores robots.txt
Applebot, DuckDuckBot, SeznamBot, YandexBot, DuckAssistBot, MistralAI-User, Amzn-SearchBototherssearch and assistantslisted in ALLOWED_BOTS

Training (owner policy: allowed)

TokenEffect of a Disallow
GPTBotout of OpenAI model training; no effect on ChatGPT search
ClaudeBotout of Anthropic training; honours Crawl-delay
Google-Extendedout of Gemini training and grounding; no effect on Google Search (a control token, no own crawler)
Applebot-Extendedout of Apple model training (control token)
CCBotout of Common Crawl, a widely used training corpus
Amazonbot, Meta-ExternalAgentout of Amazon / Meta AI use

Why allowed: the Atlas is an open-access non-profit; models that have read it are more likely to know and name it (ADR-021).

Verify a bot by its operator's published IP list, not by the user-agent string alone.

What non-JS crawlers see

CrawlerRuns JS
Googlebot (and Gemini via Google's index), Applebotyes
GPTBot, OAI-SearchBot, ChatGPT-User, ClaudeBot, PerplexityBot, CCBotno

Quick check of what a non-JS bot gets:

curl -s -A "OAI-SearchBot" https://atlas-of-todays-world.vercel.app/country/china \
  | sed -e 's/<script[^>]*>.*<\/script>//g' -e 's/<[^>]*>/ /g' | tr -s ' \n' | wc -c
curl -s -A "GPTBot" https://atlas-of-todays-world.vercel.app/country/china | grep -c '<link rel="canonical"'

The second line must print 1: metadata is in <head> (a prerendered page). On a dynamic route without htmlLimitedBots it would be streamed into <body> for this user agent.

Next 16 default htmlLimitedBots pattern (from node_modules/next/dist/shared/lib/router/utils/html-bots.js in 16.4.0) includes -Google/Google- crawlers, Chrome-Lighthouse, Slurp, DuckDuckBot, baiduspider, yandex, Bingbot, applebot, facebookexternalhit, Twitterbot, LinkedInBot, Slackbot, Discordbot, WhatsApp and a few more. Re-read the file after a Next upgrade.

llms.txt anatomy (as generated)

# Atlas of Today's World
> One-sentence summary of the organization and the site.
Publisher, licence of use, how data is sourced, "Cite as" format, language, links to
llms-full.txt, feeds and sitemap.
## Encyclopedia dossiers
- [Title](https://…/topics/slug): YYYY-MM-DD — first sentences…
## News and analysis · ## World regions · ## Global issues
## Data layers (datasets by country) · ## About the Atlas
## Optional
- [Name](url): note

Live: llms.txt (about 44 KB), llms-full.txt (about 800 KB), example twin types-of-migrants.md.

Evidence behind the writing rules

GEO study (Aggarwal et al., KDD 2024) on its GEO-bench, measured by position-adjusted word count in generative answers:

  • adding quotations from relevant sources: about +40 %
  • adding statistics: about +30 %
  • citing sources: about +28 %
  • fluency rewrites helped; keyword stuffing scored below baseline
  • gains were largest for lower-ranked sources (rank-5 sources gained about +100 %)
  • by domain: citing sources helped most for law and government and for facts; quotations for history and people and society; statistics for law and government and for debate. Results were confirmed on Perplexity.

Google (May 2025): unique, non-commodity content, important content in text, structured data that matches visible text, good images, good page experience. No special AI files or markup.

Microsoft (October 2025): assistants split pages into passages. Clear H2/H3 sections, direct one-to-two-sentence answers, self-contained sentences, lists and tables for comparisons, measurable facts; nothing key hidden in tabs, PDFs or images.

"In short" pattern

Good (template): "As of <year>, <country> <verb> <number + unit> <what>, according to <named source, linked>. <One sentence of context that is true on its own.>"

Weak: "This page explains the situation in the country and its many challenges."

Shape: entity + time + fact with number + named source; second sentence gives context; no pronoun openers; no claims the page body doesn't support.

Measuring

  • Search Console Performance: AI Overviews and AI Mode are counted under "Web".
  • Bing Webmaster Tools: AI Performance report (public preview since February 2026) shows Copilot and Bing AI citations per page.
  • Vercel Analytics (cookie-less, @vercel/analytics in src/app/layout.tsx): referrers chatgpt.com, perplexity.ai, claude.ai, copilot.microsoft.com. Open it from the Vercel team → project → Analytics.
  • Vercel logs (project → Logs): filter by user agent (OAI-SearchBot, ChatGPT-User, Claude-User, PerplexityBot, Perplexity-User) to see retrieval traffic.

Sources

  • GEO paper: https://arxiv.org/abs/2311.09735 (full text https://arxiv.org/html/2311.09735v3)
  • Google, AI features and your website: https://developers.google.com/search/docs/appearance/ai-features
  • Google, succeeding in AI search (May 2025): https://developers.google.com/search/blog/2025/05/succeeding-in-ai-search
  • Microsoft, optimizing content for AI search answers: https://about.ads.microsoft.com/en/blog/post/october-2025/optimizing-your-content-for-inclusion-in-ai-search-answers
  • Google common crawlers and Google-Extended: https://developers.google.com/search/docs/crawling-indexing/google-common-crawlers
  • OpenAI bots: https://developers.openai.com/api/docs/bots
  • Anthropic crawlers: https://support.claude.com/en/articles/8896518-does-anthropic-crawl-data-from-the-web-and-how-can-site-owners-block-the-crawler
  • Perplexity bots: https://docs.perplexity.ai/guides/bots
  • Common Crawl CCBot: https://commoncrawl.org/ccbot
  • Vercel, the rise of the AI crawler: https://vercel.com/blog/the-rise-of-the-ai-crawler
  • llms.txt proposal: https://llmstxt.org/
  • Bing, sitemaps in AI-powered search: https://blogs.bing.com/webmaster/July-2025/Keeping-Content-Discoverable-with-Sitemaps-in-AI-Powered-Search
  • Bing AI Performance report (secondary): https://searchengineland.com/bing-webmaster-tools-testing-new-ai-performance-report-468039
  • schema.org Place and Country: https://schema.org/Place, https://schema.org/Country, https://schema.org/about
  • Google, supported meta tags (geo tags ignored): https://developers.google.com/search/docs/crawling-indexing/special-tags
  • Next.js 16.4 docs: node_modules/next/dist/docs/01-app/03-api-reference/04-functions/generate-metadata.md (Streaming metadata), …/05-config/01-next-config-js/htmlLimitedBots.md

This site uses only the cookies it needs to work — no advertising or tracking cookies. Visits are counted without cookies. Privacy