**TL;DR.** IndexNow est une API gratuite et standardisée qui vous permet de notifier instantanément Bing, Yandex, Seznam et Naver des changements d'URL. Pour un site e-commerce, cela signifie qu'un nouveau produit apparaît sur Bing en quelques minutes au lieu de plusieurs jours. Implémentez-le comme une file d'attente vidée par un travail en arrière-plan, et non par des appels synchrones.

## Ce qu'est IndexNow

IndexNow est un protocole ouvert annoncé par Microsoft et Yandex en 2021. Les moteurs de recherche qui participent acceptent un simple HTTP POST listant les URL qui ont changé ; ils explorent ces URL en priorité.

Moteurs participants :

- Bing
- Yandex
- Seznam (Tchèque)
- Naver (Coréen)

Notamment absent : Google. Google ne participe pas à IndexNow ; pour Google, vous devez compter sur l'exploration régulière ou l'API d'indexation (qui est limitée aux offres d'emploi, aux diffusions en direct et à quelques autres types de contenu).

## Comment ça fonctionne

1. Vous générez une clé API (une chaîne aléatoire de 32 caractères ou plus).
2. Vous hébergez la clé à `https://yourdomain.com/{key}.txt` — le corps contient juste la clé. Cela vérifie que vous contrôlez le domaine.
3. Vous POSTez à `https://api.indexnow.org/indexnow` (ou à tout point de terminaison IndexNow d'un moteur participant) avec :

```json
{
  "host": "yourdomain.com",
  "key": "your-api-key",
  "keyLocation": "https://yourdomain.com/your-api-key.txt",
  "urlList": [
    "https://yourdomain.com/products/leather-bag",
    "https://yourdomain.com/products/wool-coat"
  ]
}
```

4. Le moteur met les URL en file d'attente pour exploration.

C'est tout. Pas d'authentification au-delà de la vérification de propriété du fichier clé, pas de système de jeton de limitation de fréquence.

## Quand pinguer

Pinguez IndexNow chaque fois qu'une URL indexable publiquement change de manière significative :

| Événement                             | Ping                                         |
| ------------------------------------- | -------------------------------------------- |
| Produit créé                          | Ping nouvelle URL de produit                 |
| Produit mis à jour (titre, description) | Ping URL du produit                          |
| Slug du produit changé                | Ping À LA FOIS les anciennes et nouvelles URL |
| Produit non publié ou supprimé        | Ping URL (le moteur verra 404/410 et supprimera) |
| Catégorie créée                       | Ping URL de la catégorie                     |
| Catégorie mise à jour                 | Ping URL de la catégorie                     |
| Marque ou page créée/mise à jour      | Ping l'URL                                  |
| Sitemap régénéré                      | Ping optionnel de l'URL du sitemap          |

Ne pinguez pas pour :

- Mises à jour d'inventaire qui ne changent pas le contenu.
- Changements internes uniquement (journal d'audit des prix, actions administratives).
- Importations en masse — regroupez le ping post-importation.

## File d'attente, ne pas appeler de manière synchrone

Une implémentation naïve :

```ts
// MAUVAIS : appel IndexNow synchrone lors de la sauvegarde du produit
async function saveProduct(product: Product) {
  await db.update(...);
  await fetch('https://api.indexnow.org/indexnow', { method: 'POST', body: ... });
  // ↑ ajoute 100–500ms à chaque sauvegarde
}
```

Mieux :

```ts
// BON : mettre en file d'attente, vider de manière asynchrone
async function saveProduct(product: Product) {
  await db.update(...);
  await enqueueIndexNow(product.url);
  // retourne immédiatement
}

// Tâche en arrière-plan, s'exécute toutes les 1 à 5 minutes
export const indexNowDrainTask = task({
  id: 'indexnow-drain',
  cron: '*/1 * * * *',
  run: async () => {
    const urls = await fetchPendingUrls(MAX_BATCH);
    if (urls.length === 0) return;
    await postToIndexNow(urls);
    await markAsDrained(urls);
  },
});
```

Avantages de la mise en file d'attente :

- Les mutations restent rapides.
- Les échecs ne perturbent pas les flux visibles par l'utilisateur.
- Facile à regrouper.
- Facile à enregistrer par tentative.
- Survit aux temps d'arrêt de la plateforme (la file d'attente persiste, se vide lorsque l'API est de nouveau opérationnelle).

## Gestion des changements de slug

Un cas subtil courant : un slug de produit change de `/products/old` à `/products/new`. Mettez en file d'attente LES DEUX URL :

- La nouvelle URL a besoin d'être explorée afin que le moteur l'ajoute à l'index.
- L'ancienne URL a besoin d'être explorée afin que le moteur la récupère à nouveau, voie le 301 (ou 410), et la supprime.

```ts
async function updateProductSlug(productId: string, oldSlug: string, newSlug: string) {
  await db.products.update({ id: productId, slug: newSlug });
  await writeRedirect({ from: `/products/${oldSlug}`, to: `/products/${newSlug}`, status: 301 });
  await enqueueIndexNow([`/products/${oldSlug}`, `/products/${newSlug}`]);
}
```

Pour les suppressions, écrivez une redirection 410 (table des chemins supprimés d'Ordiko) et pinguez l'URL. Le moteur voit 410 et supprime l'URL de son index de manière permanente.

## Regroupement

L'API accepte jusqu'à 10 000 URL par POST. Modèle e-commerce courant : vider 100 à 500 URL par minute, selon le volume de mutations.

```ts
const MAX_BATCH = 500;
const BATCH_INTERVAL_MS = 60_000;

export const indexNowDrainTask = task({
  id: 'indexnow-drain',
  cron: '*/1 * * * *',
  run: async () => {
    const urls = await fetchPendingUrls(MAX_BATCH);
    if (urls.length === 0) return;

    try {
      const res = await fetch('https://api.indexnow.org/indexnow', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          host: 'yourdomain.com',
          key: process.env.INDEXNOW_KEY,
          keyLocation: `https://yourdomain.com/${process.env.INDEXNOW_KEY}.txt`,
          urlList: urls,
        }),
      });

      if (res.status === 200 || res.status === 202) {
        await markAsDrained(urls, 'ok');
      } else if (res.status === 429) {
        await markAsDrained(urls, 'retry'); // remettre en file d'attente pour le prochain cycle de vidage
      } else {
        await markAsDrained(urls, 'failed', `${res.status} ${await res.text()}`);
      }
    } catch (err) {
      await markAsDrained(urls, 'failed', String(err));
    }
  },
});
```

## Journalisation

Chaque ping (succès ou échec) doit être enregistré. Schéma de table d'audit utile :

```sql
CREATE TABLE seo_revalidation_events (
  id SERIAL PRIMARY KEY,
  store_id UUID NOT NULL,
  url TEXT NOT NULL,
  step TEXT NOT NULL, -- 'indexnow' | 'revalidate_tag' | 'sitemap'
  status TEXT NOT NULL, -- 'ok' | 'failed' | 'retry'
  error TEXT,
  created_at TIMESTAMPTZ DEFAULT NOW()
);
```

Cela vous permet de répondre à la question "pourquoi ce produit n'apparaît-il pas sur Bing ?" en interrogeant la table pour l'URL du produit.

## Vérification

Bing Webmaster Tools dispose d'un tableau de bord IndexNow dédié :

1. Connectez-vous sur [bing.com/webmasters](https://www.bing.com/webmasters).
2. Ajoutez votre site.
3. Accédez à **IndexNow** dans la barre latérale.
4. Consultez les soumissions au fil du temps, les comptes de succès/échec.

Yandex Webmaster a un rapport équivalent sous **Indexation**.

## Comment Ordiko implémente IndexNow

- La colonne `stores.indexNowApiKey` stocke la clé par magasin.
- Le fichier clé est servi à `/{key}.txt` automatiquement.
- Chaque service d'entité (`product.service.ts`, `category.service.ts`, etc.) appelle `notifyIndexNowOnChange(url)` lors d'une mutation.
- File d'attente : table `pending_indexnow` avec `(storeId, url, enqueuedAt)`.
- La tâche cron de Trigger.dev `indexnow-drain.task.ts` s'exécute chaque minute.
- Chaque vidage est enregistré dans `seo_revalidation_events` avec `step: "indexnow", status: ok|failed|retry`.

## FAQ

**IndexNow fonctionne-t-il pour Google ?**
Non. Google ne participe pas à IndexNow. Pour Google, soumettez votre sitemap XML et comptez sur les cycles d'exploration réguliers, ou utilisez l'API d'indexation de Google pour des types de contenu limités (offres d'emploi, événements en direct). Pour Bing, Yandex, Seznam et Naver, IndexNow est le moyen le plus rapide de signaler des mises à jour.

**Combien d'URL puis-je soumettre par appel ?**
Jusqu'à 10 000 URL par POST. L'API retourne 200-OK pour les soumissions acceptées. Pour des volumes plus importants, regroupez et limitez le taux — le taux sûr typique est de 1 à 10 lots par minute. Le travail de vidage dans Ordiko envoie un lot par minute par défaut.

**Que se passe-t-il si je soumets trop agressivement ?**
Vous obtenez 429 Trop de demandes. L'API ne vous bannit pas — reculez, réessayez avec un retour exponentiel. Une soumission soutenue à volume élevé d'URL inchangées peut entraîner une dépriorisation mais pas un blocage.

**Comment Ordiko implémente-t-il IndexNow ?**
Chaque service d'entité (produit, catégorie, marque, page) appelle notifyIndexNowOnChange lors de la création/mise à jour/suppression/non-publication. La table de file d'attente pending_indexnow contient des entrées ; une tâche cron Trigger.dev indexnow-drain.task.ts la vide selon un calendrier. Chaque ping est enregistré dans seo_revalidation_events pour audit.