# indxel -- Full API Documentation
> ESLint for SEO. Infrastructure SEO developer-first.
> Version: SDK 0.1.0, CLI 0.2.0
indxel is an open-source SEO infrastructure toolkit for Next.js developers.
It ensures SEO never breaks silently by scoring every page 0-100 and gating deploys on critical errors.
---
## SDK (npm: indxel)
### Installation
```bash
npm install indxel
```
Zero runtime dependencies. Ships ESM + CJS + TypeScript declarations.
Optional peer dependency: next >= 14.
### defineSEO(config): SEOConfig
Define global SEO defaults. Returns a frozen config object used by other functions.
```typescript
import { defineSEO } from 'indxel'
export default defineSEO({
siteName: 'My SaaS',
siteUrl: 'https://mysaas.com',
titleTemplate: '%s | My SaaS', // %s is replaced by page title
defaultDescription: 'Description for pages without one.',
defaultOGImage: '/og-default.png',
locale: 'en_US',
twitter: {
handle: '@mysaas',
cardType: 'summary_large_image', // or 'summary'
},
organization: {
name: 'My SaaS Inc.',
logo: '/logo.png',
url: 'https://mysaas.com',
},
})
```
### createMetadata(page, config?): MetadataOutput
Generate a Next.js-compatible Metadata object. Use directly in generateMetadata().
```typescript
import { createMetadata } from 'indxel'
import seoConfig from '@/seo.config'
// Basic page
export function generateMetadata() {
return createMetadata({
title: 'Pricing',
description: 'Simple pricing. Start free.',
path: '/pricing',
ogImage: '/og-pricing.png', // optional, falls back to defaultOGImage
}, seoConfig)
}
// Article page
export function generateMetadata() {
return createMetadata({
title: 'How to Fix SEO',
description: 'A practical guide.',
path: '/blog/how-to-fix-seo',
article: {
publishedTime: '2026-01-15',
modifiedTime: '2026-01-20', // optional
author: 'Jane Doe',
tags: ['seo', 'nextjs'],
},
}, seoConfig)
}
```
Generates: title (with template), canonical URL, OpenGraph (title, description, image, type, locale), Twitter card, hreflang alternates, robots directives.
### generateLD(type, data): object
Generate JSON-LD structured data.
Supported types: Article, Product, FAQ, HowTo, Breadcrumb, Organization, WebPage, SoftwareApplication, WebSite.
```typescript
import { generateLD } from 'indxel'
// Article
generateLD('Article', {
headline: 'How to Fix SEO',
datePublished: '2026-01-15',
author: { name: 'Jane Doe', url: 'https://jane.dev' },
})
// FAQ
generateLD('FAQ', {
questions: [
{ question: 'What is indxel?', answer: 'ESLint for SEO.' },
],
})
// Organization
generateLD('Organization', {
name: 'My SaaS',
url: 'https://mysaas.com',
logo: 'https://mysaas.com/logo.png',
})
// Usage in JSX:
```
### validateMetadata(metadata, options?): ValidationResult
Validate metadata against 15 SEO rules. Returns 0-100 score.
```typescript
import { validateMetadata } from 'indxel'
const result = validateMetadata(metadata)
result.score // 0-100
result.grade // 'A' | 'B' | 'C' | 'D' | 'F'
result.errors // [{ id: string, message: string, weight: number }]
result.warnings // [{ id: string, message: string, weight: number }]
result.passed // [{ id: string, message: string, weight: number }]
// Strict mode: warnings become errors
validateMetadata(metadata, { strict: true })
```
Validation rules (15 rules, 100 points total):
- title-present (5pt): Page has a title tag
- title-length (10pt): Title is 50-60 characters
- description-present (5pt): Page has meta description
- description-length (10pt): Description is 120-160 characters
- og-image (10pt): OpenGraph image is set
- og-title (5pt): OpenGraph title is set
- og-description (5pt): OpenGraph description is set
- canonical-url (10pt): Canonical URL present and absolute
- structured-data-present (10pt): At least one JSON-LD block
- structured-data-valid (5pt): JSON-LD has @context and @type
- robots-not-blocking (5pt): Page not accidentally noindexed
- twitter-card (5pt): Twitter card type configured
- alternates-hreflang (5pt): Hreflang alternates declared
- viewport-meta (5pt): Viewport meta tag present
- favicon (5pt): Favicon referenced
Scoring: pass = full weight, warning = half weight, error = 0.
Grades: A >= 90, B >= 80, C >= 70, D >= 60, F < 60.
### crawlSite(url, options?): Promise
Crawl a live website. Discovers pages via internal links, validates metadata, produces cross-page analysis.
```typescript
import { crawlSite } from 'indxel'
const result = await crawlSite('https://mysite.com', {
maxPages: 100, // default: 50
maxDepth: 5, // default: 5
delay: 200, // ms between requests, default: 200
strict: false, // treat warnings as errors
ignorePatterns: ['/admin/*', '/api/*'],
onPageCrawled: (page) => console.log(page.url),
})
result.pages // CrawledPage[] - each with url, metadata, validation, h1s, wordCount, responseTimeMs, structuredDataTypes
result.averageScore // number
result.totalPages // number
result.grade // string
result.totalErrors // number
result.totalWarnings // number
result.durationMs // number
result.skippedUrls // string[]
// Cross-page analysis
result.analysis.duplicateTitles // [{ title, urls }]
result.analysis.duplicateDescriptions // [{ description, urls }]
result.analysis.h1Issues // [{ url, issue: 'missing'|'multiple', count }]
result.analysis.brokenInternalLinks // [{ from, to, status }]
result.analysis.redirects // [{ url, chain }]
result.analysis.thinContentPages // [{ url, wordCount, isAppPage }]
result.analysis.orphanPages // string[]
result.analysis.slowestPages // [{ url, responseTimeMs }]
result.analysis.structuredDataSummary // [{ type, count }]
```
### fetchSitemap(url): Promise
Fetch and parse sitemap.xml.
```typescript
import { fetchSitemap } from 'indxel'
const result = await fetchSitemap('https://mysite.com')
result.found // boolean
result.urls // [{ loc, lastmod?, changefreq?, priority? }]
result.errors // string[]
```
### compareSitemap(sitemapUrls, crawledUrls): SitemapComparison
Compare sitemap URLs against crawled pages.
```typescript
import { compareSitemap } from 'indxel'
const comparison = compareSitemap(sitemapUrls, crawledUrls)
comparison.inCrawlOnly // pages missing from sitemap
comparison.inSitemapOnly // sitemap URLs not reachable
comparison.issues // string[]
```
### fetchRobots(url): Promise
Fetch and parse robots.txt.
```typescript
import { fetchRobots } from 'indxel'
const result = await fetchRobots('https://mysite.com')
result.found // boolean
result.directives // [{ userAgent, allow, disallow }]
result.sitemapUrls // string[]
result.warnings // string[]
result.errors // string[]
```
### checkUrlsAgainstRobots(directives, urls): RobotsUrlCheck[]
Check which URLs are blocked by robots.txt rules.
```typescript
import { checkUrlsAgainstRobots } from 'indxel'
const checks = checkUrlsAgainstRobots(directives, urls)
// [{ path, blocked, blockedBy }]
```
### verifyAssets(pages): Promise
Verify referenced assets (og:image, favicon) respond correctly.
```typescript
import { verifyAssets } from 'indxel'
const result = await verifyAssets(pages)
result.totalChecked // number
result.totalBroken // number
result.checks // [{ url, type, ok, status?, error?, warning? }]
```
### researchKeywords(seed, options?): Promise
Discover keywords via Google Autocomplete. No API key required.
```typescript
import { researchKeywords } from 'indxel'
const result = await researchKeywords('nextjs seo', {
locale: 'en', // default: 'en'
country: 'us', // default: 'us'
alphabetExpansion: true, // 'seed a', 'seed b', ...
questionKeywords: true, // 'how to seed', 'what is seed', ...
prepositionKeywords: true, // 'seed for', 'seed vs', ...
timeout: 5000,
})
result.seed // string
result.suggestions // [{ keyword, source: 'autocomplete' }]
result.questions // [{ keyword, source: 'question' }]
result.longTail // [{ keyword, source: 'alphabet'|'preposition' }]
result.totalKeywords // number
```
### analyzeContentGaps(keywords, existingPages): ContentGapResult
Find keyword opportunities your existing pages are missing.
```typescript
import { analyzeContentGaps } from 'indxel'
const result = analyzeContentGaps(keywords, existingPages)
result.covered // [{ keyword, coveredBy: url }]
result.gaps // [{ keyword, source, relevance, suggestedType, suggestedPath }]
result.totalKeywords // number
result.totalCovered // number
result.totalGaps // number
result.coveragePercent // number
// Gap relevance: 'high' | 'medium' | 'low'
// Suggested types: 'landing' | 'blog' | 'faq' | 'comparison' | 'guide'
```
---
## CLI (npm: indxel-cli)
### Installation
```bash
npm install -g indxel-cli
# or use npx:
npx indxel-cli
```
### Commands
#### indxel init [--hook] [--force] [--cwd ]
Initialize SEO in a Next.js project. Creates seo.config.ts, sitemap.ts, robots.ts.
With --hook: installs a git pre-push hook that runs `indxel check --ci` before every push.
#### indxel check [--ci] [--diff] [--json] [--strict] [--cwd ]
Static analysis of Next.js source code. Scans page.tsx files, extracts metadata, validates against 15 rules.
--ci: strict mode + exit code 1 on errors (for CI/CD pipelines).
--diff: compare with previous run (stored in .indxel/last-check.json).
#### indxel crawl [--push] [--api-key ] [--max-pages ] [--max-depth ] [--delay ] [--ignore ] [--strict] [--json] [--skip-assets] [--skip-sitemap] [--skip-robots]
Crawl a live website. Audits every page's metadata, checks sitemap, robots.txt, assets.
Cross-page analysis: duplicate titles/descriptions, broken links, thin content, orphan pages, structured data summary.
--push: send results to indxel.com dashboard (requires --api-key or INDXEL_API_KEY env var).
#### indxel keywords [--site ] [--locale ] [--country ] [--max-pages ] [--json]
Research keyword opportunities via Google Autocomplete.
--site: crawl your site and compare against keywords to find content gaps.
#### indxel index [--check] [--indexnow-key ] [--api-key ] [--json]
Check indexation readiness. Free: sitemap/robots.txt diagnostic + setup instructions.
--check: verify which pages are in Google cache (requires Plus or Pro plan + --api-key).
--indexnow-key: submit URLs to Bing/Yandex/DuckDuckGo via IndexNow (requires Plus or Pro plan + --api-key).
---
## Dashboard (indxel.com)
Web dashboard for monitoring SEO scores over time. Features:
- Project management with auto-generated API keys (ix_... format)
- Score card with grade, trend, sparkline history
- Diff panel comparing last 2 crawls (fixed issues, new issues, regressions)
- Page grid sorted by worst score
- Issue list grouped by type (errors, warnings)
- Cross-page analysis (duplicate titles, H1 issues, orphan pages)
- Settings with API key display, CI/CD integration examples
- Stripe billing for plan upgrades
### API Endpoints
POST /api/cli/push -- Accept crawl results from CLI (Bearer token = project API key)
GET /api/cli/plan -- Return user's plan for API key (used by CLI to gate paid features)
GET /api/projects -- List user's projects
POST /api/projects -- Create project (enforces plan limit)
DELETE /api/projects/[id] -- Delete project (cascade deletes checks + pages)
GET /api/projects/[id]/checks -- Get checks, pages, analysis, diff for a project
POST /api/checkout -- Create Stripe checkout session
POST /api/billing/portal -- Create Stripe billing portal session
POST /api/webhooks/stripe -- Handle Stripe subscription events
### Plans
FREE: 1 project, 5 checks/month, 500 pages/crawl, basic rules
PLUS (19 EUR/month): 5 projects, unlimited checks, 1000 pages/crawl, auto-indexation, weekly monitoring, email alerts
PRO (49 EUR/month): unlimited projects, unlimited checks, 5000 pages/crawl, Slack/webhook alerts, keyword intelligence, API access, priority support
---
## TypeScript Types
Key exported types from the indxel package:
```typescript
SEOConfig // Global site SEO configuration
PageSEO // Per-page SEO options
MetadataOutput // Next.js-compatible metadata object
StructuredDataType // 'Article' | 'FAQ' | 'HowTo' | etc.
ValidationResult // Score, grade, errors, warnings, passed rules
ValidationRule // Rule definition with id, weight, check function
ValidateOptions // { strict?: boolean }
ResolvedMetadata // Flat metadata shape used by validation rules
CrawlOptions // maxPages, maxDepth, delay, strict, ignorePatterns
CrawledPage // url, metadata, validation, h1s, wordCount, etc.
CrawlResult // pages, averageScore, grade, analysis
CrawlAnalysis // duplicateTitles, brokenLinks, thinContent, etc.
SitemapResult // found, urls, errors
SitemapComparison // inCrawlOnly, inSitemapOnly, issues
RobotsResult // found, directives, sitemapUrls, warnings
RobotsUrlCheck // path, blocked, blockedBy
AssetCheckResult // totalChecked, totalBroken, checks
KeywordResearchResult // suggestions, questions, longTail, totalKeywords
KeywordResearchOptions // locale, country, alphabetExpansion, etc.
ContentGap // keyword, source, relevance, suggestedType, suggestedPath
ContentGapResult // covered, gaps, totalKeywords, coveragePercent
```
---
## Documentation Links
- Full documentation: https://indxel.com/docs
- SDK reference: https://indxel.com/docs/sdk
- Metadata (defineSEO, createMetadata): https://indxel.com/docs/sdk/metadata
- Structured Data (generateLD): https://indxel.com/docs/sdk/structured-data
- Validation (validateMetadata, 15 rules): https://indxel.com/docs/sdk/validation
- Crawling (crawlSite, sitemap, robots): https://indxel.com/docs/sdk/crawling
- Keywords (researchKeywords, contentGaps): https://indxel.com/docs/sdk/keywords
- CLI reference: https://indxel.com/docs/cli
- init: https://indxel.com/docs/cli/init
- check: https://indxel.com/docs/cli/check
- crawl: https://indxel.com/docs/cli/crawl
- keywords: https://indxel.com/docs/cli/keywords
- index: https://indxel.com/docs/cli/index-cmd
- MCP server: https://indxel.com/docs/mcp
- CI/CD guard: https://indxel.com/docs/ci
- Dashboard: https://indxel.com/docs/dashboard
- API reference: https://indxel.com/docs/api
## Links
- Website: https://indxel.com
- Documentation: https://indxel.com/docs
- npm SDK: https://www.npmjs.com/package/indxel
- npm CLI: https://www.npmjs.com/package/indxel-cli
- GitHub: https://github.com/indxel/indxel
- LLM-optimized summary: https://indxel.com/llms.txt