# StockExpertAI - robots.txt
# Allow Twitterbot full access (needed for Twitter/X card previews including ?tab= URLs)
User-agent: Twitterbot
Allow: /
# Social link-preview bots — full access so og:image fetches keep working
# after the global "Disallow: /api/" below (these groups override *).
User-agent: facebookexternalhit
Allow: /
User-agent: LinkedInBot
Allow: /
User-agent: Slackbot
Allow: /
User-agent: Discordbot
Allow: /
User-agent: TelegramBot
Allow: /
User-agent: WhatsApp
Allow: /
# AI retrieval / search / citation bots — ALLOW (real user-facing AI
# search traffic, generates citations). Each of these generates ~1
# referral per ~100 crawls, which is worth the bandwidth.
# RFC 9309: a UA obeys ONLY its most-specific group — these groups do NOT
# inherit the global "User-agent: *" rules, so each must carry its own
# Disallow: /api/. Social-preview bot groups above intentionally keep full
# access — they must fetch /api/og images.
User-agent: ChatGPT-User
Allow: /
Allow: /ai/
Allow: /.well-known/ai.txt
Disallow: /api/
Disallow: /stock/*?tab=
Disallow: /stock/*&tab=
Disallow: /*?_reload=
Disallow: /*&_reload=
Disallow: /*?_v=
Disallow: /*&_v=
User-agent: OAI-SearchBot
Allow: /
Allow: /ai/
Allow: /.well-known/ai.txt
Disallow: /api/
Disallow: /stock/*?tab=
Disallow: /stock/*&tab=
Disallow: /*?_reload=
Disallow: /*&_reload=
Disallow: /*?_v=
Disallow: /*&_v=
User-agent: Claude-User
Allow: /
Allow: /ai/
Allow: /.well-known/ai.txt
Disallow: /api/
Disallow: /stock/*?tab=
Disallow: /stock/*&tab=
Disallow: /*?_reload=
Disallow: /*&_reload=
Disallow: /*?_v=
Disallow: /*&_v=
User-agent: Claude-SearchBot
Allow: /
Allow: /ai/
Allow: /.well-known/ai.txt
Disallow: /api/
Disallow: /stock/*?tab=
Disallow: /stock/*&tab=
Disallow: /*?_reload=
Disallow: /*&_reload=
Disallow: /*?_v=
Disallow: /*&_v=
User-agent: PerplexityBot
Allow: /
Allow: /ai/
Allow: /.well-known/ai.txt
Disallow: /api/
Disallow: /stock/*?tab=
Disallow: /stock/*&tab=
Disallow: /*?_reload=
Disallow: /*&_reload=
Disallow: /*?_v=
Disallow: /*&_v=
User-agent: Amazonbot
Allow: /
Allow: /ai/
Allow: /.well-known/ai.txt
Disallow: /api/
Disallow: /stock/*?tab=
Disallow: /stock/*&tab=
Disallow: /*?_reload=
Disallow: /*&_reload=
Disallow: /*?_v=
Disallow: /*&_v=
User-agent: YouBot
Allow: /
Allow: /ai/
Allow: /.well-known/ai.txt
Disallow: /api/
Disallow: /stock/*?tab=
Disallow: /stock/*&tab=
Disallow: /*?_reload=
Disallow: /*&_reload=
Disallow: /*?_v=
Disallow: /*&_v=
User-agent: cohere-ai
Allow: /
Allow: /ai/
Allow: /.well-known/ai.txt
Disallow: /api/
Disallow: /stock/*?tab=
Disallow: /stock/*&tab=
Disallow: /*?_reload=
Disallow: /*&_reload=
Disallow: /*?_v=
Disallow: /*&_v=
# Agent-category fetchers (a person's assistant fetching a page at answer
# time — the citation itself). Explicit groups so the policy is legible to
# Cloudflare's 2026-09-15 Search/Agent/Training split; same retrieval rules.
User-agent: Perplexity-User
Allow: /
Allow: /ai/
Allow: /.well-known/ai.txt
Disallow: /api/
Disallow: /stock/*?tab=
Disallow: /stock/*&tab=
Disallow: /*?_reload=
Disallow: /*&_reload=
Disallow: /*?_v=
Disallow: /*&_v=
User-agent: meta-externalfetcher
Allow: /
Allow: /ai/
Allow: /.well-known/ai.txt
Disallow: /api/
Disallow: /stock/*?tab=
Disallow: /stock/*&tab=
Disallow: /*?_reload=
Disallow: /*&_reload=
Disallow: /*?_v=
Disallow: /*&_v=
User-agent: DuckAssistBot
Allow: /
Allow: /ai/
Allow: /.well-known/ai.txt
Disallow: /api/
Disallow: /stock/*?tab=
Disallow: /stock/*&tab=
Disallow: /*?_reload=
Disallow: /*&_reload=
Disallow: /*?_v=
Disallow: /*&_v=
User-agent: MistralAI-User
Allow: /
Allow: /ai/
Allow: /.well-known/ai.txt
Disallow: /api/
Disallow: /stock/*?tab=
Disallow: /stock/*&tab=
Disallow: /*?_reload=
Disallow: /*&_reload=
Disallow: /*?_v=
Disallow: /*&_v=
User-agent: Applebot
Allow: /
Allow: /ai/
Allow: /.well-known/ai.txt
# Applebot is a real search engine with a rich-result surface (Apple Search,
# Siri, Spotlight), and robots groups do NOT cascade — so the /api/logo/
# exception in the wildcard group below does not reach it. Mirror it here or
# Apple keeps seeing a page that promises an image and blocks every one.
# The AI-answer groups deliberately do not get this:
# they consume text, and each extra allowance is extra crawl load for us.
Allow: /api/logo/
Disallow: /api/
Disallow: /stock/*?tab=
Disallow: /stock/*&tab=
Disallow: /*?_reload=
Disallow: /*&_reload=
Disallow: /*?_v=
Disallow: /*&_v=
# Groups do not cascade (RFC 9309), so the /locales/ rule in the wildcard group
# below never reaches Applebot — mirror it here. Applebot receives server-side
# rendered pages and loses nothing without the locale JSON files.
Disallow: /locales/
# AI training bots — allowed by explicit, documented owner decisions
# (OpenAI/Anthropic/Google 2026-06-10; Meta AI + Common Crawl 2026-09-03: the
# goal is to be known to the models, and Cloudflare's 2026-09-15 policy makes
# a "Training" block sweep up Googlebot/Bingbot/Applebot too). Apple-Extended,
# FacebookExternalHit-AI, Bytespider (outside /zh), Diffbot, ImagesiftBot and
# Omgili below remain blocked.
User-agent: GPTBot
Allow: /
Allow: /ai/
Allow: /.well-known/ai.txt
Disallow: /api/
Disallow: /stock/*?tab=
Disallow: /stock/*&tab=
Disallow: /*?_reload=
Disallow: /*&_reload=
Disallow: /*?_v=
Disallow: /*&_v=
User-agent: ClaudeBot
Allow: /
Allow: /ai/
Allow: /.well-known/ai.txt
Disallow: /api/
Disallow: /stock/*?tab=
Disallow: /stock/*&tab=
Disallow: /*?_reload=
Disallow: /*&_reload=
Disallow: /*?_v=
Disallow: /*&_v=
User-agent: anthropic-ai
Allow: /
Allow: /ai/
Allow: /.well-known/ai.txt
Disallow: /api/
Disallow: /stock/*?tab=
Disallow: /stock/*&tab=
Disallow: /*?_reload=
Disallow: /*&_reload=
Disallow: /*?_v=
Disallow: /*&_v=
User-agent: Google-Extended
Allow: /
Allow: /ai/
Allow: /.well-known/ai.txt
Disallow: /api/
Disallow: /stock/*?tab=
Disallow: /stock/*&tab=
Disallow: /*?_reload=
Disallow: /*&_reload=
Disallow: /*?_v=
Disallow: /*&_v=
User-agent: Applebot-Extended
Disallow: /
User-agent: meta-externalagent
Allow: /
Allow: /ai/
Allow: /.well-known/ai.txt
Disallow: /api/
Disallow: /stock/*?tab=
Disallow: /stock/*&tab=
Disallow: /*?_reload=
Disallow: /*&_reload=
Disallow: /*?_v=
Disallow: /*&_v=
User-agent: FacebookExternalHit-AI
Disallow: /
# Bytespider (ByteDance/TikTok + Doubao LLM): only allowed on /zh/* surface.
# Doubao serves the Chinese market, so the Chinese-language pages are the only
# surface where a citation can reach its audience. Confining Bytespider there
# keeps that citation surface alive while avoiding origin-heavy crawl traffic
# across the other locales.
User-agent: Bytespider
Allow: /zh/
Disallow: /
User-agent: CCBot
Allow: /
Allow: /ai/
Allow: /.well-known/ai.txt
Disallow: /api/
Disallow: /stock/*?tab=
Disallow: /stock/*&tab=
Disallow: /*?_reload=
Disallow: /*&_reload=
Disallow: /*?_v=
Disallow: /*&_v=
User-agent: Diffbot
Disallow: /
User-agent: ImagesiftBot
Disallow: /
User-agent: Omgili
Disallow: /
# ── Shared crawl policy: bingbot + all other crawlers ─────────────────
# Bing powers ChatGPT search and Microsoft Copilot, so bingbot carries an
# explicit group (added 2026-09-01). RFC 9309 groups do not cascade — an
# explicit group means bingbot ignores the wildcard group — so both groups
# below state the identical full rule set, generated from one source.
#
# Feed endpoints (/feed.xml, /feed/*) were unblocked 2026-09-01: the feed
# now serves per-item summaries instead of 456 KB of full article bodies,
# and RSS is a discovery channel for Bing and AI systems.
# Content signals are declared twice, from ONE constant: the Content-Signal
# HTTP response header on every page, and the inline Content-Signal line at
# the head of the bingbot and * groups below (contentsignals.org directive;
# strict RFC-9309 validators flag it — accepted, owner decision 2026-09-03):
# search=yes → permit building search indexes and short excerpts
# ai-input=yes → permit RAG / grounding at query time
# ai-train=yes → permit training
# Retrieval, agent AND training bots (OpenAI/Anthropic/Google/Meta/Common
# Crawl) are allowed above; Apple-Extended, Bytespider (outside /zh), Diffbot,
# ImagesiftBot and Omgili stay blocked.
# Crawlable OG cards — allowed 2026-08-15, closed 2026-08-25
# (measured crawl-budget decision).
#
# The 2026-08-15 change moved the stock share card out of /api/ to
# /og/stock/{version}/{TICKER}.png so the "Disallow: /api/" below stayed
# intact. That worked, and it also handed Googlebot the same bill the
# /api/og/ Allow had run up in June — the card generator was the problem, not
# the path it sat on.
#
# Measured over 8 days of VERIFIED Googlebot (reverse-DNS confirmed, so this
# is not the 24% of Googlebot-UA traffic that is our own tooling):
# /og/stock/* 12,085 requests = 16.8% of the entire crawl budget,
# across 6,011 distinct images, exactly 2.0 fetches each.
# At 163 KB per card that is 1.9 GB in 8 days — and Google
# has reached only 6,011 of ~36.8K tickers, so the finished
# set is ~72K requests and ~11 GB.
# Against that: zero clicks from images in 90 days, and the pages these cards
# belong to average position 58. A thumbnail on page 6 buys nothing; the
# requests it costs are requests not spent on the tens of thousands of URLs
# sitting in "Discovered - currently not indexed".
#
# Social preview bots are UNAFFECTED — Twitterbot, facebookexternalhit,
# LinkedInBot, Slackbot, Discordbot, TelegramBot and WhatsApp each have their
# own "Allow: /" group above, and RFC 9309 groups do not cascade, so this
# Disallow never reaches them. og:image keeps working everywhere a human
# actually sees it. Applebot likewise keeps its own group untouched.
#
# /api/logo/ below stays ALLOWED and that is deliberate, not an oversight:
# logos are ~2 KB against the card's 163 KB, and they are the only fetchable
# image left on a stock page, so "max-image-preview:large" still refers to
# something Google is permitted to read.
#
# Do NOT re-open this without measuring the crawl split first. It has now been
# opened and closed twice on the same reasoning (2026-06-09 /api/og/,
# 2026-08-15 /og/) — the third time will cost another quarter.
# API endpoints stay blocked — crawlers should only index HTML pages, not JSON responses.
# /api/og/ exception removed 2026-06-09: the Allow let
# Googlebot spend ~16% of its sampled crawl budget (37K fetches/7d) on OG
# share-card renders that carry X-Robots-Tag: noindex anyway. Social preview
# bots (Twitterbot, facebookexternalhit, LinkedInBot, Slackbot, Discordbot,
# TelegramBot, WhatsApp) keep og:image access via their explicit Allow: /
# groups above — robots.txt groups don't cascade, so this Disallow never
# applies to them. Do not re-add the Allow.
#
# /api/logo/ exception: logo images referenced by pages must stay fetchable.
# Every image on a stock page lived under /api/ — og:image, twitter:image,
# Article.image and the only content — so a page declaring
# max-image-preview:large had not one image Google was permitted to fetch.
# No thumbnail beside our result, no Discover eligibility.
#
# Only the LOGO path is reopened, and it is nothing like the card generator
# that caused the 2026-06-09 problem: logos are 0.7-11 KB, served from a
# 7-day process cache behind a 30-day edge cache, and there are ~36.8K of them
# total rather than one render per request. The expensive /api/og/ generator
# stays blocked. Allow wins over Disallow on the longer path (RFC 9309).
# i18n JSON bundles — /locales/ added 2026-08-15 (crawl-budget
# measurement). 4,436 files sit under this one prefix: 10 full bundles (up to 2.0 MB
# each), 10 core bundles, 4,416 per-namespace chunks. They are served by
# express.static (server/app.ts:588) and carry no X-Robots-Tag.
#
# Nothing links to them: no , no sitemap entry, no href in any HTML. They
# are reached ONLY by executing our JS — and the bot that executes it gains
# nothing, because bots receive server-rendered pages and never mount the app
# for bot UAs that received #ssr-content. The indexed DOM is 100% server HTML,
# so the i18n store never reaches it. The requests are still fired because
# client/src/i18n/config.ts calls initI18n() at module scope.
#
# Measured on production with a real Chrome under a Googlebot UA, aborting every
# /locales/ request: /stock/aapl fired 20, /tr/stock/aapl 45, /earnings 6,
# /product 5 (one being the 1.75 MB full bundle) — and the rendered text came
# back byte-identical each time, with zero raw i18n keys. The same probe under a
# human UA DID break (5 raw keys on /stock/aapl), which is what proves the bot
# result is a real negative rather than a blind probe.
#
# X-Robots-Tag: noindex is NOT a substitute — Google must keep crawling a URL to
# see a noindex, so the fetch waste would continue.
User-agent: bingbot
Content-Signal: search=yes, ai-input=yes, ai-train=yes
Allow: /
Allow: /ai/
Allow: /.well-known/ai.txt
# Markdown mirrors for AI systems (MD Mirror — AEO). Search engines index
# the HTML version (canonical) — .md responses return X-Robots-Tag: noindex.
Allow: /stock/*.md
Allow: /etf/*.md
Allow: /journal/*.md
# Multi-language markdown mirrors (coverage-gated per ticker — 404 when no translation)
Allow: /tr/stock/*.md
Allow: /es/stock/*.md
Allow: /de/stock/*.md
Allow: /fr/stock/*.md
Allow: /pt/stock/*.md
Allow: /ja/stock/*.md
Allow: /ko/stock/*.md
Allow: /zh/stock/*.md
# Prevent crawling tabbed stock page variants (canonical is /stock/ticker without ?tab=)
Disallow: /stock/*?tab=
Disallow: /stock/*&tab=
# Client-side recovery params (redirect-loop bait for strict crawlers)
Disallow: /*?_reload=
Disallow: /*&_reload=
Disallow: /*?_v=
Disallow: /*&_v=
# Private/auth-only pages
Disallow: /admin
Disallow: /settings
Disallow: /account
Disallow: /dashboard
Disallow: /portfolio-tracker
# $ end-anchor (RFC 9309): bare "/portfolio" prefix-blocked the PUBLIC,
# sitemap-submitted /portfolio-review and /portfolio-risk-guide pages
Disallow: /portfolio$
Disallow: /portfolio/
Disallow: /portfolio-analytics
Disallow: /war-room
Disallow: /login
Disallow: /signup
Disallow: /register
Disallow: /auth/
Allow: /author/
Disallow: /onboarding
Disallow: /forgot-password
Disallow: /reset-password
Disallow: /profile
Disallow: /messages
Disallow: /trading
Disallow: /order-history
Disallow: /funding
Disallow: /email-preferences
Disallow: /watchlist
Disallow: /hot-picks
Disallow: /agent-ops
# OG share cards — closed 2026-08-25 after crawl-budget measurement
# (full history in the note above the group blocks).
Disallow: /og/
# API endpoints are JSON, not pages. /api/logo/ stays fetchable — the only
# image on a stock page crawlers are permitted to read (longer-path Allow
# wins per RFC 9309). Full history above the group blocks.
Allow: /api/logo/
Disallow: /api/
# i18n JSON bundles — JS-only fetches, nothing links to them (measured
# 2026-08-15; full note above the group blocks).
Disallow: /locales/
# Asset directories
Disallow: /attached_assets/
User-agent: *
Content-Signal: search=yes, ai-input=yes, ai-train=yes
Allow: /
Allow: /ai/
Allow: /.well-known/ai.txt
# Markdown mirrors for AI systems (MD Mirror — AEO). Search engines index
# the HTML version (canonical) — .md responses return X-Robots-Tag: noindex.
Allow: /stock/*.md
Allow: /etf/*.md
Allow: /journal/*.md
# Multi-language markdown mirrors (coverage-gated per ticker — 404 when no translation)
Allow: /tr/stock/*.md
Allow: /es/stock/*.md
Allow: /de/stock/*.md
Allow: /fr/stock/*.md
Allow: /pt/stock/*.md
Allow: /ja/stock/*.md
Allow: /ko/stock/*.md
Allow: /zh/stock/*.md
# Prevent crawling tabbed stock page variants (canonical is /stock/ticker without ?tab=)
Disallow: /stock/*?tab=
Disallow: /stock/*&tab=
# Client-side recovery params (redirect-loop bait for strict crawlers)
Disallow: /*?_reload=
Disallow: /*&_reload=
Disallow: /*?_v=
Disallow: /*&_v=
# Private/auth-only pages
Disallow: /admin
Disallow: /settings
Disallow: /account
Disallow: /dashboard
Disallow: /portfolio-tracker
# $ end-anchor (RFC 9309): bare "/portfolio" prefix-blocked the PUBLIC,
# sitemap-submitted /portfolio-review and /portfolio-risk-guide pages
Disallow: /portfolio$
Disallow: /portfolio/
Disallow: /portfolio-analytics
Disallow: /war-room
Disallow: /login
Disallow: /signup
Disallow: /register
Disallow: /auth/
Allow: /author/
Disallow: /onboarding
Disallow: /forgot-password
Disallow: /reset-password
Disallow: /profile
Disallow: /messages
Disallow: /trading
Disallow: /order-history
Disallow: /funding
Disallow: /email-preferences
Disallow: /watchlist
Disallow: /hot-picks
Disallow: /agent-ops
# OG share cards — closed 2026-08-25 after crawl-budget measurement
# (full history in the note above the group blocks).
Disallow: /og/
# API endpoints are JSON, not pages. /api/logo/ stays fetchable — the only
# image on a stock page crawlers are permitted to read (longer-path Allow
# wins per RFC 9309). Full history above the group blocks.
Allow: /api/logo/
Disallow: /api/
# i18n JSON bundles — JS-only fetches, nothing links to them (measured
# 2026-08-15; full note above the group blocks).
Disallow: /locales/
# Asset directories
Disallow: /attached_assets/
Sitemap: https://www.stockexpertai.com/sitemap.xml
# Locale stock sitemaps — one per active language so Google discovers
# translated stock pages directly instead of crawling from internal links.
Sitemap: https://www.stockexpertai.com/sitemap-ja-stocks.xml
Sitemap: https://www.stockexpertai.com/sitemap-ko-stocks.xml
Sitemap: https://www.stockexpertai.com/sitemap-de-stocks.xml
Sitemap: https://www.stockexpertai.com/sitemap-es-stocks.xml
Sitemap: https://www.stockexpertai.com/sitemap-zh-stocks.xml
Sitemap: https://www.stockexpertai.com/sitemap-fr-stocks.xml
Sitemap: https://www.stockexpertai.com/sitemap-pt-stocks.xml
Sitemap: https://www.stockexpertai.com/sitemap-tr-stocks.xml
# The markdown-mirror sitemaps (sitemap-stocks-md.xml, sitemap-etfs-md.xml,
# sitemap-hubs-md.xml) are deliberately NOT advertised here (2026-08-20
# measurement): every .md URL answers X-Robots-Tag:
# noindex, so listing 37,283 of them in a Sitemap: directive told Google to
# process a sitemap made entirely of non-indexable duplicates. The .md routes
# and sitemap endpoints stay live for AI crawlers (GPTBot/bingbot/Amazonbot
# consume them at scale); discovery runs through /llms.txt, /sitemap.md,
# and Accept: text/markdown.
# Earnings POC sitemaps — quarterly earnings preview pages.
# Decoupled from translation_languages.is_active: earnings POC ships in all
# 9 supported langs at once (i18n bypass — single shared bundle). Listing
# every lang here lets Google discover translated earnings pages even
# before the cloner marks the language "active".
Sitemap: https://www.stockexpertai.com/sitemap-earnings.xml
Sitemap: https://www.stockexpertai.com/sitemap-earnings-tr.xml
Sitemap: https://www.stockexpertai.com/sitemap-earnings-de.xml
Sitemap: https://www.stockexpertai.com/sitemap-earnings-fr.xml
Sitemap: https://www.stockexpertai.com/sitemap-earnings-ko.xml
Sitemap: https://www.stockexpertai.com/sitemap-earnings-zh.xml
# Note: /sitemap.md is NOT advertised here. The sitemaps.org protocol
# expects XML-formatted sitemaps in the Sitemap: directive, and Google
# Search Console flags non-XML entries as invalid. AI agents discover
# /sitemap.md through the "Markdown Sitemap" section in /llms.txt, which
# is the correct channel for markdown-formatted discovery resources.
# AI/LLM discovery (see https://llmstxt.org)
# LLMs-txt and LLMs-full-txt are non-standard directives that cause
# Lighthouse to flag robots.txt as invalid. AI bots discover these
# via /.well-known/llms.txt or tags instead.
# LLMs-txt: https://www.stockexpertai.com/llms.txt
# LLMs-full-txt: https://www.stockexpertai.com/llms-full.txt