— AI Search & SEO

How to write llms.txt for a SaaS site (with examples)

A practical guide to llms.txt and llms-full.txt for B2B SaaS sites. What they are, what is and is not known about how AI engines use them, the format, common mistakes, and a copy-paste starter you can ship today.

— TL;DR

llms.txt is a curated markdown index of your most important pages with one-line summaries. llms-full.txt is the concatenated full content. No major AI provider has confirmed using them for citations, so treat them as a cheap, young convention. Keep llms.txt under 5KB and llms-full.txt under ~120k tokens. Both ship in about 30 minutes.

If you're a B2B SaaS team that wants to be cited by ChatGPT, Claude, or Perplexity in 2026, llms.txt is one of the cheapest tasks on your AEO checklist: about 30 minutes of work. It's also one of the most misunderstood. No major AI provider has confirmed that it uses the file for citations, so treat it as a low-cost convention, not a proven lever. Many teams either skip it entirely or ship a broken version.

This piece is the practical, copy-paste version. By the end you'll know what llms.txt is, what is and is not known about how AI engines use it, the format, the common mistakes, and a complete starter file you can adapt to your site.

#What llms.txt actually is

llms.txt is a young convention proposed in late 2024. The pitch: provide a single markdown file at yourdomain.com/llms.txt that gives AI crawlers a curated, well-structured index of your site's most important content.

The structure is informal but consistent across the sites that have adopted it:

  • A title line (# Brand Name)
  • A one-line summary (blockquote with > )
  • An optional intro paragraph
  • One or more sections with ## headers and bulleted links

Each link follows the format:

- [Page Title](https://domain.com/path): One-line summary of the page.

That's it. No JSON, no schema, no special syntax. It's a markdown file because LLMs are trained on markdown and read it natively.

#What AI engines actually do with it

Nobody outside the AI providers knows for certain. No major AI provider has publicly confirmed using llms.txt for ranking or citations. Here is what can be said:

  • The main crawlers can reach it. The user agents behind ChatGPT (OAI-SearchBot and ChatGPT-User), Perplexity (PerplexityBot) and Claude (ClaudeBot) can fetch /llms.txt like any other public file, provided your robots.txt and firewall allow them
  • Whether they use it is unproven. Listing a page in llms.txt has not been shown to make it more likely to be cited
  • Google AI Overviews is not known to use it. To Google's crawlers it is a static text file like any other

The practical view: llms.txt is cheap to add and easy to keep current, so it is a reasonable bet. Write the one-line summaries with care anyway. If an AI system does read the file, the summary is what tells it whether your page is relevant to the user's question.

#The starter file

Here's a complete llms.txt for a typical B2B SaaS marketing site. Copy this, adapt the content to your site, and ship it as public/llms.txt (or generate it dynamically as we do further down).

# Acme. AI workflow automation for B2B teams

> Acme is the workflow automation platform for B2B SaaS operations teams.
> Build, monitor, and scale internal automations without engineering bottlenecks.

A small team building since 2023. Used by 1,200+ B2B SaaS teams worldwide.

## Core pages

- [Home](https://acme.com/): The product overview. Automation, AI agents, dashboards.
- [Pricing](https://acme.com/pricing): Public pricing. Starter $99/mo, Team $399/mo, Enterprise custom.
- [About](https://acme.com/about): The team, the playbook, what we believe.
- [Contact](https://acme.com/contact): Sales calls, support, partnership inquiries.

## Product

- [Workflow builder](https://acme.com/product/workflows): Drag-and-drop automation editor with code escape hatches.
- [AI agents](https://acme.com/product/agents): LLM-powered task agents with built-in tool use.
- [Integrations](https://acme.com/integrations): 350+ first-party integrations including Salesforce, Slack, Notion.

## Resources

- [Documentation](https://docs.acme.com/): Setup, API reference, integration guides.
- [Cost calculator](https://acme.com/calculator): Estimate your monthly cost based on workflow volume.
- [Templates library](https://acme.com/templates): 200+ pre-built workflow templates.

## Journal

- [How to pick the right automation platform in 2026](https://acme.com/blog/picking-automation-platform-2026): Comparison of n8n, Zapier, Make, and Acme for SaaS teams.
- [The hidden cost of Zapier at scale](https://acme.com/blog/zapier-cost-at-scale): Real numbers from teams running 100k+ executions/month.
- [Building reliable AI agents in production](https://acme.com/blog/reliable-ai-agents): Architecture patterns for agent loops that don't fall over.

## Optional

- [llms-full.txt](https://acme.com/llms-full.txt): Full markdown of every canonical page concatenated for deeper context.
- [Sitemap](https://acme.com/sitemap.xml): Machine-readable site index.

#What goes in (and what doesn't)

A common mistake is treating llms.txt like a sitemap and listing every URL. Don't do that. The point of the file is not every URL. It is the curated set of pages that represent canonical, authoritative answers to questions about your brand and product.

Include:

  • Home, About, Pricing, Contact (always)
  • Every flagship product or service page
  • Documentation root (link to it; don't list every doc)
  • 5–10 highest-value blog posts (your pillar content, not your changelog)
  • Any "definitive guide" content that you want cited

Exclude:

  • Marketing landing pages targeted at specific campaigns
  • Login, signup, settings pages
  • Author pages and tag pages
  • Most blog posts (only your pillar content goes here)
  • Legal pages (they're indexable but not citation-worthy)
  • Anything paywalled, gated, or session-dependent

Keep it short. A few kilobytes is a sensible target. If the file runs much longer, move secondary links under an ## Optional heading. The format reserves that heading for links a reader can skip when it needs a shorter context.

#How to write the one-line summaries

The summary is the part of llms.txt that does the work. It's what tells a reader, human or machine, what each page answers. Three rules:

#1. Lead with what the page answers, not what it is

Bad: Our pricing page. Good: Public pricing. Starter $99/mo, Team $399/mo, Enterprise custom.

The bad version says "this page exists." The good version says "if a user asks 'how much does Acme cost', cite this page."

#2. Include specific facts when relevant

Bad: Comparison of automation platforms. Good: Comparison of n8n, Zapier, Make, and Acme for SaaS teams.

The good version names the entities being compared. Anyone reading the line, human or machine, can tell at once whether the page covers their question.

#3. Match the language your ICP uses

Bad: Leveraging AI for revenue operations workflow optimization. Good: How RevOps teams cut manual work with AI agents.

The bad version is jargon-stuffed. The good version is the language a real RevOps person would type into ChatGPT.

#Generating llms.txt dynamically

For most SaaS sites with a content management system, the right pattern is to generate llms.txt from your content collection at request time. This way it never goes stale.

In a Next.js App Router project, that looks like:

// src/app/llms.txt/route.ts
import { listBlogPosts, listServices } from "@/lib/content"

export async function GET() {
  const [services, blog] = await Promise.all([
    listServices(),
    listBlogPosts(),
  ])

  const lines: string[] = [
    `# Acme · ${tagline}`,
    "",
    `> ${description}`,
    "",
    "## Core pages",
    `- [Home](https://acme.com/): ...`,
    `- [Pricing](https://acme.com/pricing): ...`,
    "",
    "## Services",
    ...services.map(
      (s) => `- [${s.title}](https://acme.com${s.href}): ${s.summary}`,
    ),
    "",
    "## Journal",
    ...blog.map(
      (p) => `- [${p.title}](https://acme.com/blog/${p.slug}): ${p.description}`,
    ),
  ]

  return new Response(lines.join("\n") + "\n", {
    headers: {
      "content-type": "text/plain; charset=utf-8",
      "cache-control": "public, max-age=3600, s-maxage=3600",
    },
  })
}

In Astro, the equivalent is an endpoint at src/pages/llms.txt.ts. In Hugo or Jekyll, a static file regenerated on every build.

The key constraints regardless of stack:

  • Serve as text/plain; charset=utf-8 (not text/markdown)
  • Cache for an hour or so. The file does not change often
  • Use absolute URLs in links (not relative)
  • Keep the file readable as a markdown document; that is the format the convention specifies

#What about llms-full.txt?

llms-full.txt is the long-form companion. It contains the full markdown of every canonical page concatenated in a stable order, so an AI engine can ingest the whole site without crawling page by page.

A typical llms-full.txt is 40–200KB and contains, in order:

  1. Home page content (hero, proof, services overview)
  2. About page (company narrative, team, principles)
  3. Process / how-it-works content
  4. Pricing (every tier, every offer)
  5. Each service page in detail (scope, FAQ, stack, pricing)
  6. Pillar blog posts (the 5–10 you also linked from llms.txt)
  7. Site-level FAQ
  8. Contact information

Cap it at roughly 120k tokens (~480KB) so it fits inside the context window of most current LLMs. If you're over the cap, the right move is to drop blog posts first, then case studies, then service detail. Never drop home/about/pricing. Those are load-bearing.

// src/app/llms-full.txt/route.ts (sketch)
import { loadAllContent } from "@/lib/content"

const MAX_CHARS = 480_000

export async function GET() {
  const { services, blog } = await loadAllContent()

  const sections: string[] = [
    homeSection(),
    aboutSection(),
    processSection(),
    pricingSection(),
    ...services.map(serviceSection),
    ...blog.map(blogSection),
    faqSection(),
    contactSection(),
  ]

  let body = sections.join("\n\n---\n\n") + "\n"
  if (body.length > MAX_CHARS) {
    body = body.slice(0, MAX_CHARS) + "\n\n[truncated]\n"
  }

  return new Response(body, {
    headers: { "content-type": "text/plain; charset=utf-8" },
  })
}

#Common mistakes

A few common ways to get llms.txt wrong:

#Giving every blog post equal weight

Some teams treat llms.txt as a content marketing index and list every post with a full summary. This buries the canonical pages under everything else. Lead with your main pages and a short list of pillar posts, and put the rest under ## Optional or leave them out.

#Marketing-speak summaries

Summaries written by marketers tend to be aspirational ("revolutionizing how teams collaborate") instead of descriptive ("Slack alternative for async-first teams"). A descriptive summary tells the reader what the page covers; an aspirational one does not. Write summaries the way a developer would. What does it do, who is it for.

#Forgetting llms-full.txt

llms.txt alone is limited. It's just an index. The pages it links to still need to be crawled. llms-full.txt puts the content itself in one file, so a system that reads it does not have to crawl page by page.

#Not linking from each other

llms.txt should explicitly link to llms-full.txt near the bottom (under an "Optional" or "More" section). Otherwise crawlers may not discover it.

#Hosting at the wrong path

It belongs at /llms.txt at the root of the domain. Not /docs/llms.txt, not /.well-known/llms.txt. The root is where the convention says to look.

#Wrong content type

Serve as text/plain; charset=utf-8. Some servers default to text/html for files without a known extension, and that can trip up tools that expect plain text.

#Forgetting subdomain coverage

If you have app.yourdomain.com and docs.yourdomain.com, each subdomain needs its own llms.txt if you want it covered. Most teams should focus all AEO investment on the marketing root domain and leave subdomains alone.

#Validating your llms.txt

After shipping, validate it:

  1. Curl as ChatGPT: curl -A "GPTBot" https://yourdomain.com/llms.txt. Should return your content with a 200 status.
  2. Same for Claude: curl -A "ClaudeBot" https://yourdomain.com/llms.txt
  3. Same for Perplexity: curl -A "PerplexityBot" https://yourdomain.com/llms.txt
  4. Check it's served as plain text: the Content-Type header should be text/plain; charset=utf-8.
  5. Validate the markdown: open it in a markdown previewer (VS Code's preview is fine). Every link should resolve.

If any of those fail, fix before moving on.

#How long until you see results?

There is no reliable timeline, because llms.txt is not proven to affect citations. Record a baseline of where AI assistants mention you before you ship it, then run the same checks again over the following months.

If citations change, you will see it against the baseline. If they do not, the file has cost you about 30 minutes.

#TL;DR

  1. Ship llms.txt at yourdomain.com/llms.txt. Use the starter above. Curate to 10–20 canonical pages with descriptive summaries.
  2. Ship llms-full.txt at yourdomain.com/llms-full.txt. Generate from your content collection at request time. Cap at ~120k tokens.
  3. Link them from your sitemap and from robots.txt (Sitemap: directive supports any URL, and a comment with the llms.txt paths may help a crawler or a person find them).
  4. Validate with curl + the major AI bot UAs. Verify content-type and accessibility.
  5. Don't over-engineer. A clean, curated llms.txt ships in about 30 minutes. Its effect on citations is unproven, so there is little value in perfecting it.

If you would like this checked as part of a wider technical baseline (structured data, AI crawler access, llms.txt, content structure), our AI search visibility audit covers it in one week for a fixed $1,800. A free 20-minute call is the place to start. Or, since this is a 30-minute job, just ship it yourself this week.

Common questions.

  • What is llms.txt?

    llms.txt is a young, proposed convention: a file at /llms.txt on your domain that gives AI systems a curated, markdown-formatted index of your most important pages with one-line summaries. The companion llms-full.txt contains the concatenated full content of every canonical page, so a system that reads it can take in the whole site in a single request without crawling.

  • Do AI engines actually read llms.txt in 2026?

    It is not confirmed. No major AI provider has publicly said it uses llms.txt for ranking or citations, and the file is not proven to affect whether you are cited. Adoption among site owners is growing. Because it is cheap to add and easy to maintain, it is still a reasonable thing to ship.

  • Should llms.txt be the same as my sitemap?

    No. Sitemap is for search-engine crawlers and lists every URL. llms.txt is curated. It should include only the canonical pages you want AI engines to cite, with a one-line summary each. Think of it as your editor's pick of the site, written for an AI audience.

  • What's the difference between llms.txt and llms-full.txt?

    llms.txt is the short index: a markdown table of contents with links and summaries (keep it to a few kilobytes). llms-full.txt is the long form: the concatenated full markdown of every canonical page in stable order (target: under 120k tokens, roughly 480KB). The index is meant to be read first. The full file is there for a system that wants deeper context.

  • Will writing llms.txt hurt my classic SEO?

    No. llms.txt is a plain text file at /llms.txt. Adding it doesn't change anything about how Google indexes the rest of your site. The cost of providing it is close to zero.

— Want help with this?

Get found in AI search ↗

The technical groundwork that helps ChatGPT, Perplexity and Google's AI answers understand your site and cite it: structured data, llms.txt, crawler access and content built to be quoted.

— Keep reading