---
title: "Use IndexNow with Nuxt AI Ready"
description: "Submit changed pages to IndexNow from Nuxt AI Ready runtime indexing."
canonical_url: "https://nuxtseo.com/docs/ai-ready/advanced/indexnow"
last_updated: "2026-09-15T13:13:14.750Z"
---

Nuxt AI Ready does not submit URLs to IndexNow automatically. This recipe submits pages when their indexed Markdown changes.

Use this recipe if your Nuxt site already uses Nuxt AI Ready. Read the [IndexNow guide](/learn-seo/nuxt/launch-and-listen/indexnow) first if you need an overview.

If your CMS or host already submits URLs, keep using that integration. Otherwise, this recipe uses [runtime indexing](/docs/ai-ready/guides/runtime-indexing).

The `contentChanged` flag compares converted Markdown with the stored content hash. First indexing counts as a change. Unchanged pages skip submission.

## Generate a Key

Create a 32-character key:

```bash
openssl rand -hex 16
```

Set `NUXT_INDEX_NOW_KEY`{lang="bash"} to the result in your deployment environment.

Add the key and your production URL to runtime config:

```ts [nuxt.config.ts]
export default defineNuxtConfig({
  runtimeConfig: {
    indexNowKey: '',
    indexNowSiteUrl: 'https://example.com',
  },
})
```

## Publish the Key

IndexNow uses a public text file to verify site ownership. Add this route:

```ts [server/routes/indexnow-key.txt.get.ts]
export default defineEventHandler((event) => {
  const { indexNowKey } = useRuntimeConfig(event)

  if (!indexNowKey) {
    throw createError({
      statusCode: 500,
      statusMessage: 'IndexNow key is not configured.',
    })
  }

  setResponseHeader(event, 'Content-Type', 'text/plain; charset=utf-8')
  return indexNowKey
})
```

Deploy the route before sending URLs. Check that `https://example.com/indexnow-key.txt` returns only your key.

## Submit Changed URLs

Keep the submission code in a server utility:

```ts [server/utils/indexnow.ts]
const INDEX_NOW_ENDPOINT = 'https://api.indexnow.org/indexnow'
const INDEX_NOW_LIMIT = 10_000

export async function submitIndexNow(
  paths: string[],
  config: { key: string, siteUrl: string },
) {
  if (paths.length === 0)
    return

  if (!config.key)
    throw new Error('IndexNow key is not configured.')

  const site = new URL(config.siteUrl)
  const urls = paths.map((path) => {
    const url = new URL(path, `${site.origin}/`)

    if (url.origin !== site.origin)
      throw new Error(`IndexNow URL must use ${site.origin}.`)

    url.hash = ''
    return url.href
  })
  const urlList = [...new Set(urls)]

  if (urlList.length > INDEX_NOW_LIMIT)
    throw new Error(`IndexNow accepts at most ${INDEX_NOW_LIMIT} URLs per request.`)

  await $fetch(INDEX_NOW_ENDPOINT, {
    method: 'POST',
    body: {
      host: site.host,
      key: config.key,
      keyLocation: new URL('/indexnow-key.txt', site).href,
      urlList,
    },
  })
}
```

The utility rejects URLs from another origin. Connect it to the runtime indexing hook:

```ts [server/plugins/indexnow.ts]
export default defineNitroPlugin((nitro) => {
  nitro.hooks.hook('ai-ready:page:indexed', async (ctx) => {
    if (!ctx.contentChanged)
      return

    const { indexNowKey, indexNowSiteUrl } = useRuntimeConfig()
    await submitIndexNow([ctx.route], {
      key: indexNowKey,
      siteUrl: indexNowSiteUrl,
    })
  })
})
```

Enable runtime sync or call the [manual indexing utilities](/docs/ai-ready/nitro-api/nitro-hooks#manual-indexing-utils) to run this hook.
The hook has no request event, so the utility receives its configuration explicitly.

Polling only processes pending pages. After publishing an edit, use the authenticated [reindex endpoint](/docs/ai-ready/guides/runtime-indexing#control-endpoints) to check that page again.
Expiring `runtimeSync.ttl` alone does not mark an indexed page pending.

Only indexed Markdown changes trigger submission. This hook does not detect deleted pages or changes made only during prerendering.
Metadata changes trigger submission only when they change the converted Markdown.

The module saves the content hash before this hook runs. If submission fails, re-indexing unchanged content will skip it.
Retry the failed URLs through `submitIndexNow` directly. Use persistent retry jobs if submissions must survive process restarts.

## Static Sites

A static site cannot run these server files. Put the same key in `public/indexnow-key.txt`{lang="text"}.

Use a post-deployment job with a list of changed URLs from your publishing system. Follow the [IndexNow request format](https://www.indexnow.org/documentation).

IndexNow supports recent additions, updates, and deletions. Submit deleted URLs after they return `404` or `410`.
Do not submit unchanged URLs from the full sitemap. See the [IndexNow FAQ](https://www.indexnow.org/faq).

## Responses

IndexNow accepts up to 10,000 URLs per request. This utility rejects a larger batch.

HTTP `200` confirms receipt. HTTP `202` confirms receipt while key validation is pending. Neither guarantees indexing.

If you receive `429`, wait before retrying. Honor `Retry-After` when provided; otherwise use backoff. The utility propagates failures to its caller.

See the [IndexNow FAQ](https://www.indexnow.org/faq) for status codes and troubleshooting.

## Sitemap

See the full [sitemap](/sitemap.md) for all pages.
