npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

next-indexnow

v1.0.0

Published

IndexNow protocol integration for Next.js applications

Downloads

216

Readme

next-indexnow

npm version License: MIT

A simple, lightweight, modern package to integrate the IndexNow protocol into your Next.js applications — notify search engines (Bing, Yandex, Seznam, and more) instantly when your content changes.

Developed and maintained by Prime Sentia.

What is IndexNow?

IndexNow is an open protocol created by Microsoft and Yandex that allows websites to easily notify search engines whenever their website content is created, updated, or deleted.

Without IndexNow, search engines can take days or weeks to discover that your content has changed because they rely on slow, scheduled web crawling. With IndexNow, you instantly "ping" search engines to let them know a URL has changed, prompting them to quickly index the fresh content.

Supported Search Engines

IndexNow shares submitted URLs across all participating search engines simultaneously. Submitting to one endpoint automatically notifies all of the following engines:

  • Microsoft Bing (and by extension, Bing Chat, Copilot, and Yahoo)
  • Yandex
  • Seznam.cz
  • Naver
  • Yep

(Note: Google is not currently participating in the IndexNow protocol, but fast indexing on Bing/Copilot is crucial for modern AI-driven search visibility).

Why it matters for AI search

AI assistants (Bing Copilot, ChatGPT search, and others) increasingly answer from freshly-indexed content. Because IndexNow pings participating engines within seconds instead of waiting days for a crawl, your latest content is far more likely to be picked up — the foundation of Generative Engine Optimization (GEO).

Installation

npm install next-indexnow
# or
yarn add next-indexnow
# or
pnpm add next-indexnow

Automatic Setup (Recommended)

IndexNow requires you to prove ownership of your site by serving a text file containing your API key at the root of your domain. We've completely automated this process.

Just run the following command at the root of your Next.js project:

npx next-indexnow init

What this does:

  1. Generates a secure, 32-character API key.
  2. Creates the verification file automatically in your public/ folder (e.g. public/a1b2c3d4...txt). Next.js will naturally serve this at your root domain.
  3. Injects INDEXNOW_KEY=... into your .env.local or .env file so you can access it in your code.

That's it! You are fully configured and ready to notify search engines.

(If you prefer to configure everything manually, you can generate a key using import { generateKey } from 'next-indexnow', save it to your .env manually, and serve the .txt file using Next.js Route Handlers.)

Usage

You can trigger IndexNow notifications whenever your content changes (e.g., in a webhook from your CMS, in a Server Action, or a custom API route).

Single URL Notification

import { notifyUrl } from 'next-indexnow';

async function publishArticle(slug) {
  // ... your database logic ...

  // Notify IndexNow
  const articleUrl = `https://yoursite.com/blog/${slug}`;

  await notifyUrl(articleUrl, {
    key: process.env.INDEXNOW_KEY,
  });

  console.log('IndexNow notified!');
}

Batch URLs Notification

If you update multiple pages at once, or want to send your entire sitemap, you can batch up to 10,000 URLs in a single request.

import { notifyBatch } from 'next-indexnow';

async function syncAllPages() {
  const urls = [
    'https://yoursite.com/page-1',
    'https://yoursite.com/page-2',
    // ... up to 10,000 URLs
  ];

  await notifyBatch(urls, {
    key: process.env.INDEXNOW_KEY,
    host: 'yoursite.com', // Required for batch notifications
  });
}

Automatic Sitemap Submission (Killer Feature 🚀)

Instead of manually keeping track of URLs, you can point next-indexnow directly to your XML sitemap. It will automatically fetch the sitemap, extract all URLs (with zero heavy XML dependencies), de-duplicate them, split them into chunks of 10,000 (IndexNow's limit), and submit everything at once.

It transparently handles sitemap-index files (recursing into child sitemaps), gzip-compressed sitemaps (.xml.gz), CDATA-wrapped entries and multi-line <loc> values — so it works with the output of next-sitemap and the App Router's generateSitemaps().

import { notifySitemap } from 'next-indexnow';

async function syncEntireWebsite() {
  const result = await notifySitemap('https://yoursite.com/sitemap.xml', {
    key: process.env.INDEXNOW_KEY,
    host: 'yoursite.com', // Required
  });

  console.log(`Submitted ${result.submitted} URLs (skipped ${result.skipped}).`);
}

The result object

notifyUrl, notifyBatch and notifySitemap all resolve to a structured IndexNowResult:

interface IndexNowResult {
  ok: boolean; // every request returned 2xx
  submitted: number; // URLs actually sent
  skipped: number; // duplicates / off-host URLs
  responses: {
    ok: boolean;
    status: number;
    statusText: string;
    urls: string[];
    attempts: number;
  }[];
}

Note that IndexNow returns 202 Accepted ("validation pending") for valid submissions — this is treated as success. On a non-retryable failure (e.g. 403 invalid key, 422 host mismatch) a typed IndexNowError is thrown, carrying status, statusText, retryable and the raw responseBody.

Next.js helpers (next-indexnow/next)

Fire-and-forget after the response: notifyAfter

Calling await notifyUrl(...) inside a Server Action blocks the response on a network round-trip. notifyAfter schedules the ping with Next.js's after() so it runs after the response is sent, and never surfaces indexing errors to the user.

import { notifyAfter } from 'next-indexnow/next';

export async function publishPost(slug: string) {
  // ...persist your changes...
  await notifyAfter(`https://yoursite.com/blog/${slug}`, {
    key: process.env.INDEXNOW_KEY!,
  });
}

Revalidate + notify in one call: revalidateAndNotify

On-demand revalidation is the canonical "this content changed, re-index it" signal. revalidateAndNotify revalidates the path (or tag) and then notifies IndexNow about the absolute URL.

import { revalidateAndNotify } from 'next-indexnow/next';

await revalidateAndNotify('/blog/my-post', {
  baseUrl: 'https://yoursite.com',
  key: process.env.INDEXNOW_KEY!,
  // tag: 'posts', // optionally revalidate a cache tag instead of a path
});

notifyAfter resolves as soon as the work is scheduled — it does not wait for the IndexNow round-trip to finish.

Pages Router key file: createIndexNowApiHandler

If you use the Pages Router, serve the key file from pages/api/[key].ts (add a rewrite so it's reachable at the domain root):

import { createIndexNowApiHandler } from 'next-indexnow/next';

export default createIndexNowApiHandler(process.env.INDEXNOW_KEY!);

Verifying your key file

A missing or mismatched key file is the #1 cause of 403/422 rejections. After deploying, confirm it's live:

import { verifyKeyFile } from 'next-indexnow';

const result = await verifyKeyFile({ key: process.env.INDEXNOW_KEY!, host: 'yoursite.com' });
if (!result.ok) {
  console.error(`Key file check failed at ${result.url}: ${result.reason}`);
}

Or from the command line: npx next-indexnow verify (reads INDEXNOW_HOST), or pass the full URL: npx next-indexnow verify https://yoursite.com/<key>.txt.

CLI

npx next-indexnow init                  # generate a key, write public/<key>.txt, update .env
npx next-indexnow submit <url> [url2…]  # submit one or more URLs (reads INDEXNOW_KEY)
npx next-indexnow sitemap <url>         # submit every URL in a sitemap (or sitemap index)
npx next-indexnow verify [keyLocation]  # check the key file is live (reads INDEXNOW_HOST)

The submit/sitemap/verify commands read INDEXNOW_KEY (and optionally INDEXNOW_HOST) from the environment or your .env.local/.env. This makes next-indexnow sitemap https://yoursite.com/sitemap.xml ideal as a postbuild step in CI.

Options

All notify functions accept an options object:

  • key (string, required): Your IndexNow API key. Validated locally before any request.
  • host (string, required for batch/sitemap): Your website's host (e.g., www.example.com). www and non-www count as different hosts.
  • keyLocation (string, optional): Full URL to your key file if it's not hosted at the exact root with the exact key name.
  • endpoint (string, optional): A known engine name ('indexnow' | 'bing' | 'yandex' | 'seznam' | 'naver' | 'yep') or a raw host. Defaults to 'indexnow' (api.indexnow.org), which already propagates to every participating engine within ~10s — so per-engine targeting is rarely necessary.
  • maxRetries (number, optional): Retry attempts for transient failures (429/5xx). Defaults to 3; honors the Retry-After header.
  • retryDelayMs (number, optional): Base delay for exponential backoff between retries. Defaults to 1000.
  • timeoutMs (number, optional): Per-request timeout via AbortController. Defaults to 10000; set to 0 to disable. A timeout is not retried (so worst-case blocking stays bounded), but other transient network errors are.
  • onHostMismatch ('throw' | 'skip', optional): What to do with URLs that don't belong to host. Defaults to 'throw'; use 'skip' to drop them and count them in result.skipped.
  • since (Date, optional, sitemap only): Only submit URLs whose <lastmod> is at or after this date (child sitemaps older than since are skipped without being fetched). URLs without a <lastmod> are always included. Great for incremental cron runs. A date-only <lastmod> (YYYY-MM-DD) is treated as end of that UTC day, and a datetime without a timezone is interpreted as UTC, so filtering is deterministic across deployments.

Runtime: requires Node 20+ (for the Web Crypto global used by generateKey), the Edge runtime, or a browser. The core notify functions are dependency-free and Edge-compatible.

Targeting a specific search engine

Submitting once to the default endpoint already shares your URLs with every participating engine, but you can target one directly by name (or import the SEARCH_ENGINES map):

import { notifyUrl, SEARCH_ENGINES } from 'next-indexnow';

await notifyUrl('https://yoursite.com/page', { key, endpoint: 'bing' });
// SEARCH_ENGINES === { indexnow, bing, yandex, seznam, naver, yep }

Example

A minimal App Router wiring (key file, a notify Server Action, sitemap + postbuild submission) lives in examples/app-router.

Contributing

This is an open-source project by Prime Sentia, and we welcome contributions from the community!

If you'd like to help improve next-indexnow:

  1. Fork the repository.
  2. Create a new branch (git checkout -b feature/my-amazing-feature).
  3. Make your changes and commit them (git commit -m 'feat: add my amazing feature').
  4. Push to the branch (git push origin feature/my-amazing-feature).
  5. Open a Pull Request.

If you find a bug or have a feature request, please open an issue.

License

MIT


Built and maintained by Prime Sentia.