CrawlPod

API reference

Every signature below was read directly from the installed package's type declarations and compiled source (v0.5.0-era exports — detector, schema, generators, express, next, ContentAnalyzer), not the README — a couple of real, small discrepancies from the README are noted where found.

The v0.6.0–v0.8.2 additions — ContentAnalyzer.audit(), ai-visibility/engines, /prompts, /measure, /citations, and /competitor — each have their own dedicated page rather than being appended here: see AI Readiness Engine, engine adapters, prompt discovery, brand measurement, citation analysis, and competitor gap analysis.

ai-visibility/detector

Zero runtime dependencies. Edge-safe.

detectBot(userAgent: string): BotInfo | null

Framework-agnostic function. Returns the matched bot's info, or null.

AI_CRAWLERS: BotInfo[]

The full list of 23 known crawlers across 13 vendors (as of the 0.8.2 registry audit, which added Meta-WebIndexer and Meta-ExternalFetcher), each with a name, company, User-Agent match pattern, and purpose ('training' | 'search' | 'indexing' | 'unknown'). Full detail, including verification status and sources, is on the crawler registry reference page — the short list:

GPTBot, ChatGPT-User, OAI-SearchBot, ClaudeBot, Claude-User, Claude-SearchBot,
PerplexityBot, Perplexity-User, Google-Extended, Googlebot, Bingbot, CCBot,
Amazonbot, Amzn-SearchBot, Amzn-User, meta-externalagent, Meta-WebIndexer,
Meta-ExternalFetcher, Applebot-Extended, Bytespider, YouBot, cohere-ai, Diffbot

Claude-Web— Anthropic's pre-2024 token — was removed in 0.4.0 after the whole registry was re-verified against vendor documentation instead of third-party SEO-blog lists; it's replaced by the current Claude-User. If you matched on Claude-Web anywhere in your own code, see the migration guide. The package README also abbreviates a few entries as "Cohere", "Meta", and "Apple" — the actual matched User-Agent substrings are cohere-ai, meta-externalagent, and Applebot-Extended, listed above as they actually appear in code.

The same registry is published as plain JSON at dist/crawlers.jsonin every release — generated from the compiled detector at build time, so it can't drift from AI_CRAWLERS. It's not in the package's exports map (so require("ai-visibility/crawlers.json") won't resolve) — it's meant for non-JS consumers to fetch over a CDN mirror at build time, e.g. cdn.jsdelivr.net/npm/ai-visibility@0.4.0/dist/crawlers.json, and vendor the result. See the crawler registry reference for the full list and the fetch-and-vendor pattern.

getUnverifiedBots(): BotInfo[]

Added in 0.4.0. Returns every crawler entry that isn't verified: true— checked directly against the installed package's runtime behavior, this is currently 4 entries: Bytespider (explicitly verified: false — no official ByteDance documentation exists at all) plus YouBot, cohere-ai, and Diffbot (no verifiedfield at all — they predate the 0.4.0 audit and haven't been re-checked yet). The package's own 0.4.0 changelog says this returns "currently just Bytespider", which undercounts it — the package README states the correct figure (4), and it matches what the installed code actually returns.

class AIBotDetector

new AIBotDetector(config?: { additionalBots?: string[]; ignoreBots?: string[] })
  .detect(userAgent: string): BotInfo | null
  .getBotNames(): string[]

class HTMLOptimizer

Strips non-essential content from HTML for AI crawler consumption — keeps semantic structure, JSON-LD, and meaningful text.

new HTMLOptimizer(options?: AIOptimizationOptions)
  .optimize(html: string): string

interface AIOptimizationOptions {
  stripJs?: boolean;          // remove <script> tags except JSON-LD
  removeAds?: boolean;
  removeTracking?: boolean;
  simplifyNav?: boolean;
  structureContent?: boolean; // front-load main content
}

ai-visibility/schema

Dependency-free except fromHTML(), which lazily loads cheerioon first call (dynamic import — importing this subpath doesn't pull cheerio in unless you actually call it). All builders except offer()/aggregateRating() return a full JSON-LD object with @context; those two return a nested node meant to be embedded via product()'s or softwareApplication()'s offers/aggregateRating fields. As of 0.4.0, this subpath re-exports every parameter type used above (ProductSchemaData, ArticleSchemaData, etc.) — before 0.4.0 only the classes/functions were re-exported, so importing a type from ai-visibility/schema required reaching into the root barrel.

class SchemaBuilder {
  static faq(items: { q: string; a: string }[]): SchemaObject
  static product(data: ProductSchemaData): SchemaObject
  static article(data: ArticleSchemaData): SchemaObject
  static organization(data: OrganizationSchemaData): SchemaObject
  static person(data: PersonSchemaData): SchemaObject
  static website(data: WebSiteSchemaData): SchemaObject
  static softwareApplication(data: SoftwareApplicationSchemaData): SchemaObject
  static breadcrumbList(items: BreadcrumbItem[], options?: { baseUrl?: string }): SchemaObject
  static definedTerm(data: DefinedTermSchemaData): SchemaObject
  static definedTermSet(data: DefinedTermSetSchemaData): SchemaObject
  static offer(data: OfferSchemaData): SchemaObject          // nested, no @context
  static aggregateRating(data: AggregateRatingSchemaData): SchemaObject  // nested, no @context
  static fromHTML(html: string, hints?: { author?: string; publisher?: string }): Promise<SchemaObject>
  static toScriptTag(schema: SchemaObject): string
  static toScriptTagMultiple(schemas: SchemaObject[]): string
}

toScriptTag()/toScriptTagMultiple() return a full <script>…</script>string for raw-template injection — it doesn't fit React's dangerouslySetInnerHTML, which expects the tag's contents, not the tag itself. In React, use the plain object the builders return:

React/Next.js
<script
  type="application/ld+json"
  dangerouslySetInnerHTML={{
    __html: JSON.stringify(SchemaBuilder.faq([{ q: "...", a: "..." }]))
  }}
/>

ai-visibility/generators

class RobotsGenerator {
  constructor(config?: RobotsConfig)
  generate(): string
  static allowAll(options?: { disallow?: string[]; sitemapUrl?: string }): string
  static blockTraining(options?: { disallow?: string[]; sitemapUrl?: string }): string
  static blockAll(options?: { disallow?: string[]; sitemapUrl?: string }): string  // new in 0.5.0
}

interface RobotsConfig {
  allowAI?: string[];
  blockAI?: string[];
  disallow?: string[];   // default: [] as of 0.3.0 — see note below
  sitemapUrl?: string;
  crawlDelay?: number;
}

Since 0.4.0, this subpath also re-exports RobotsConfig and LLMSConfig directly — no need to import config types from the root barrel. blockAll(), new in 0.5.0, is a control mechanism for blocking every known AI crawler, not a recommendation either way — the CLI's robots --preset block-all maps to it. See the CLI reference.

0.3.0 changed the default disallow list from ['/admin', '/api', '/private', '/_next', '/static'] to []— the old default meant every Next.js site using it was telling AI crawlers not to fetch the site's own JS/CSS chunks. Confirmed directly in the compiled source while migrating this site in Phase 3. If you're upgrading from 0.2.x and relied on the default, pass your own disallow list explicitly now.

class LLMSTextGenerator {
  constructor(config: LLMSConfig)
  generate(): Promise<string>
  static minimal(config: LLMSConfig): string   // no summaries, synchronous
}

interface LLMSConfig {
  siteName: string;
  description: string;
  baseUrl?: string;
  pages: { url: string; title: string; summary?: string; priority?: "high" | "medium" | "low" }[];
  contact?: { email?: string; twitter?: string; github?: string };
  autoSummarize?: boolean; // fetches each page live to extract a summary if one isn't given
}

ai-visibility/express

Node-only. Imports types from express (optional peer dependency).

createAIMiddleware(config?: AIMiddlewareConfig): (req, res, next) => void
optimizeResponseForAI(options?: AIOptimizationOptions): (req, res, next) => void

class AIVisitorLogger {
  constructor(config?: LoggerConfig)
  middleware(): (req, res, next) => void
  log(entry: CrawlerLog): void                 // manual logging, non-Express frameworks
  getLogs(filter?: { botName?: string; days?: number; url?: string }): CrawlerLog[]
  getStats(days?: number): Record<string, BotStatsSerialized>
  clearLogs(): void
}

interface LoggerConfig {
  storage?: "file" | "memory" | "both";   // default storage strategy
  logFilePath?: string;                    // default: ./logs/ai-crawler.json
  trackCrawlers?: string[];
  maxMemoryEntries?: number;
}

AIVisitorLogger's file/memorystorage doesn't persist across invocations on serverless platforms like Vercel — see /built-withfor how this site logs crawler visits instead (its own Postgres table, not this class's storage). Since 0.4.0, this subpath also re-exports AIMiddlewareConfig and AIOptimizationOptions directly.

ai-visibility/next

Edge-safe. Imports types from next/server (optional peer dependency).

createNextMiddleware(options?: NextMiddlewareOptions):
  (req: NextRequest, event?: NextFetchEvent) => NextResponse

interface NextMiddlewareOptions {
  additionalBots?: string[];
  ignoreBots?: string[];
  headerName?: string;    // response header marking a detected bot request, default 'x-ai-crawler'
  rewrite?: string | ((bot: BotInfo, req: NextRequest) => string);
  onDetect?: (bot: BotInfo, req: NextRequest) => void | Promise<void>;
}

// re-exported from ai-visibility/detector, its real home since 0.3.1:
detectAndOptimize(html: string, userAgent: string, options?: DetectAndOptimizeOptions):
  { isBot: boolean; botName: string | null; html: string }

detectAndOptimize() is framework-agnostic — HTML string and User-Agent string in, transformed HTML out, no request/response objects needed. Through 0.3.0 it was importable only from this subpath, whose module scope statically imports next/server — so merely importing it in a Nuxt, Vue, or plain Node project crashed with Cannot find module 'next/server', even though the function itself never touches Next.js. Fixed in 0.3.1 by moving it to ai-visibility/detector (the zero-dependency module, its real home); this subpath still re-exports it, so existing ai-visibility/next imports keep working unchanged.

Through 0.3.x, onDetect was not awaited internally, and the returned middleware only accepted a reqparameter — no access to Next's waitUntil — so an async onDetect (a database write, say) could be torn down mid-flight the instant the response was sent. Fixed in 0.4.0: onDetect may now return a Promise<void>, the middleware accepts the NextFetchEvent Next.js always passes as its second argument, and when both are present the promise is registered with event.waitUntil() automatically. This site still calls detectBot() from ai-visibility/detector directly rather than createNextMiddleware() — not because the built-in hook is unsafe anymore, but because its own crawler-log write needs direct control over event.waitUntil() alongside other proxy logic. See the App Router recipe.

ContentAnalyzer

Exported from the root barrel. Scores HTML for AI readability.

class ContentAnalyzer {
  static readonly SCORING_WEIGHTS: ScoringDimension[]  // new in 0.5.0, sums to 1.0
  constructor(options?: AnalyzerOptions)
  analyze(html: string, context?: AnalysisContext): Promise<AIReadabilityScore>
}

interface AnalysisContext {   // new in 0.5.0 — optional, backward compatible
  robotsTxt?: string;
  hasLlmsTxt?: boolean;
}

interface AIReadabilityScore {
  overallScore: number; // 0-100
  breakdown: {
    answerFrontLoading: number;
    factDensity: number;
    headingStructure: number;
    eeatSignals: number;
    snippability: number;
    schemaCoverage: number;
    crawlerAccessibility: number;   // new in 0.5.0
  };
  issues: { type: string; severity: "high" | "medium" | "low"; message: string; fix: string }[];
  recommendations: string[];
}

context is optional and additive — omitting it keeps every pre-0.5.0 call site working unchanged, it just means crawlerAccessibility can only check the page's own <meta name="robots"> tag instead of also parsing robotsTxt and checking hasLlmsTxt. The CLI's audit/lint supply this context automatically. Full rationale for the weights (and SCORING_WEIGHTS' published-JSON twin, dist/scoring-weights.json) is on the scoring methodology page, not repeated here.

AIVisitorLogger & Dashboard

Dashboard is exported from the root barrel (not ai-visibility/express — it only renders data you hand it, so it has no Express dependency itself):

class Dashboard {
  constructor()
  getHtml(): string
  formatData(stats, logs): DashboardData
  render(stats, logs, options?: { autoRefresh?: boolean; refreshInterval?: number }): string
  getPath(): string
}

createDashboard(): Dashboard

CLI

0.5.0 added audit/lint (scoring a live URL or local directory, with an opt-in --fail-under CI gate) and top-level robots/llms aliases, alongside the pre-existing init, analyze, generate, and logs commands. Full command-by-command detail, verified by running --help and the actual binary against a live URL and a local directory, now lives on its own page: the CLI reference — not duplicated here to avoid the two drifting apart.