> ## Documentation Index
> Fetch the complete documentation index at: https://docs.firecrawl.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Suivi des modifications

> Détecter et surveiller les changements dans le contenu web entre les extractions

<img src="https://mintcdn.com/firecrawl/vlKm1oZYK3oSRVTM/images/launch-week/lw3d12.webp?fit=max&auto=format&n=vlKm1oZYK3oSRVTM&q=85&s=cc56c24d15e1b2ed4806ddb66d0f5c3f" alt="Change Tracking" width="2400" height="1350" data-path="images/launch-week/lw3d12.webp" />

Le suivi des modifications compare le contenu actuel d’une page à celui de la dernière fois où vous l’avez extraite. Ajoutez `changeTracking` à votre tableau `formats` pour détecter si une page est nouvelle, inchangée ou modifiée et, si vous le souhaitez, obtenir un diff structuré de ce qui a changé.

* Fonctionne avec `/scrape`, `/crawl` et `/batch/scrape`
* Deux modes de diff : `git-diff` pour les changements au niveau des lignes, `json` pour la comparaison au niveau des champs
* Limité à votre équipe, et éventuellement limité à un tag que vous fournissez

<div id="how-it-works">
  ## Comment ça fonctionne
</div>

Chaque scrape avec `changeTracking` activé stocke un instantané et le compare à l’instantané précédent pour cette URL. Les instantanés sont stockés de manière persistante et n’expirent pas, ce qui garantit que les comparaisons restent précises, quel que soit le temps écoulé entre les scrapes.

| Scrape           | Résultat                                                   |
| ---------------- | ---------------------------------------------------------- |
| Première fois    | `changeStatus: "new"` (aucune version précédente n'existe) |
| Contenu inchangé | `changeStatus: "same"`                                     |
| Contenu modifié  | `changeStatus: "changed"` (données de diff disponibles)    |
| Page supprimée   | `changeStatus: "removed"`                                  |

La réponse inclut ces champs dans l'objet `changeTracking` :

| Champ              | Type                  | Description                                                                                                     |
| ------------------ | --------------------- | --------------------------------------------------------------------------------------------------------------- |
| `previousScrapeAt` | `string \| null`      | Horodatage du scrape précédent (`null` lors du premier scrape)                                                  |
| `changeStatus`     | `string`              | `"new"`, `"same"`, `"changed"` ou `"removed"`                                                                   |
| `visibility`       | `string`              | `"visible"` (accessible via des liens / le sitemap) ou `"hidden"` (l'URL fonctionne mais n'est plus référencée) |
| `diff`             | `object \| undefined` | Diff ligne par ligne (présent uniquement en mode `git-diff` lorsque le statut est `"changed"`)                  |
| `json`             | `object \| undefined` | Comparaison au niveau des champs (présente uniquement en mode `json` lorsque le statut est `"changed"`)         |

<div id="basic-usage">
  ## Utilisation de base
</div>

Incluez à la fois `markdown` et `suiviDesModifications` dans le tableau `formats`. Le format `markdown` est obligatoire, car la fonctionnalité de suivi des modifications compare les pages à partir de leur contenu Markdown.

<CodeGroup>
  ```python Python theme={null}
  from firecrawl import Firecrawl

  firecrawl = Firecrawl(api_key="fc-YOUR-API-KEY")

  result = firecrawl.scrape(
      "https://example.com/pricing",
      formats=["markdown", "suiviDesModifications"]
  )

  print(result.changeTracking)
  ```

  ```js Node theme={null}
  import { Firecrawl } from 'firecrawl';

  const firecrawl = new Firecrawl({ apiKey: "fc-YOUR-API-KEY" });

  const result = await firecrawl.scrape('https://example.com/pricing', {
    formats: ['markdown', 'suiviDesModifications']
  });

  console.log(result.changeTracking);
  ```

  ```bash cURL theme={null}
  curl -s -X POST "https://api.firecrawl.dev/v2/scrape" \
    -H "Authorization: Bearer $FIRECRAWL_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "url": "https://example.com/pricing",
      "formats": ["markdown", "suiviDesModifications"]
    }'
  ```
</CodeGroup>

<div id="response">
  ### Réponse
</div>

Lors du premier scrape, `changeStatus` est `"new"` et `previousScrapeAt` est `null` :

```json theme={null}
{
  "success": true,
  "data": {
    "markdown": "# Pricing\n\nStarter: $9/mo\nPro: $29/mo...",
    "changeTracking": {
      "previousScrapeAt": null,
      "changeStatus": "new",
      "visibility": "visible"
    }
  }
}
```

Lors des extractions ultérieures, `changeStatus` indique si le contenu a été modifié :

```json theme={null}
{
  "success": true,
  "data": {
    "markdown": "# Pricing\n\nStarter: $12/mo\nPro: $39/mo...",
    "changeTracking": {
      "previousScrapeAt": "2025-06-01T10:00:00.000+00:00",
      "changeStatus": "changed",
      "visibility": "visible"
    }
  }
}
```

<div id="git-diff-mode">
  ## Mode git-diff
</div>

Le mode `git-diff` retourne les modifications ligne par ligne dans un format similaire à celui de `git diff`. Indiquez un objet dans le tableau `formats` avec `modes: ["git-diff"]` :

<CodeGroup>
  ```python Python theme={null}
  result = firecrawl.scrape(
      "https://example.com/pricing",
      formats=[
          "markdown",
          {
              "type": "changeTracking",
              "modes": ["git-diff"]
          }
      ]
  )

  if result.changeTracking.changeStatus == "changed":
      print(result.changeTracking.diff.text)
  ```

  ```js Node theme={null}
  const result = await firecrawl.scrape('https://example.com/pricing', {
    formats: [
      'markdown',
      { type: 'changeTracking', modes: ['git-diff'] }
    ]
  });

  if (result.changeTracking.changeStatus === 'changed') {
    console.log(result.changeTracking.diff.text);
  }
  ```

  ```bash cURL theme={null}
  curl -s -X POST "https://api.firecrawl.dev/v2/scrape" \
    -H "Authorization: Bearer $FIRECRAWL_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "url": "https://example.com/pricing",
      "formats": [
        "markdown",
        { "type": "changeTracking", "modes": ["git-diff"] }
      ]
    }'
  ```
</CodeGroup>

<div id="response">
  ### Réponse
</div>

L'objet `diff` contient à la fois un diff en texte brut et une représentation JSON structurée :

```json theme={null}
{
  "suiviDesModifications": {
    "previousScrapeAt": "2025-06-01T10:00:00.000+00:00",
    "changeStatus": "changed",
    "visibility": "visible",
    "diff": {
      "text": "@@ -1,3 +1,3 @@\n # Pricing\n-Starter: $9/mo\n-Pro: $29/mo\n+Starter: $12/mo\n+Pro: $39/mo",
      "json": {
        "files": [{
          "chunks": [{
            "content": "@@ -1,3 +1,3 @@",
            "changes": [
              { "type": "normal", "content": "# Pricing" },
              { "type": "del", "ln": 2, "content": "Starter: $9/mo" },
              { "type": "del", "ln": 3, "content": "Pro: $29/mo" },
              { "type": "add", "ln": 2, "content": "Starter: $12/mo" },
              { "type": "add", "ln": 3, "content": "Pro: $39/mo" }
            ]
          }]
        }]
      }
    }
  }
}
```

L'objet structuré `diff.json` contient :

* `files` : tableau des fichiers modifiés (généralement un seul par page web)
* `chunks` : sections de modifications au sein d'un fichier
* `changes` : modifications de lignes individuelles avec un `type` (`"add"`, `"del"` ou `"normal"`), un numéro de ligne (`ln`) et un `content`

<div id="json-mode">
  ## mode JSON
</div>

Le mode `json` extrait des champs spécifiques à partir de la version actuelle et de la version précédente de la page à l’aide d’un schéma que vous définissez. C’est utile pour suivre les changements dans des données structurées comme les prix, les niveaux de stock ou les métadonnées, sans analyser un diff complet.

Passez `modes: ["json"]` avec un `schema` définissant les champs à extraire :

<CodeGroup>
  ```python Python theme={null}
  result = firecrawl.scrape(
      "https://example.com/product/widget",
      formats=[
          "markdown",
          {
              "type": "changeTracking",
              "modes": ["json"],
              "schema": {
                  "type": "object",
                  "properties": {
                      "price": { "type": "string" },
                      "availability": { "type": "string" }
                  }
              }
          }
      ]
  )

  if result.changeTracking.changeStatus == "changed":
      changes = result.changeTracking.json
      print(f"Price: {changes['price']['previous']} → {changes['price']['current']}")
  ```

  ```js Node theme={null}
  const result = await firecrawl.scrape('https://example.com/product/widget', {
    formats: [
      'markdown',
      {
        type: 'changeTracking',
        modes: ['json'],
        schema: {
          type: 'object',
          properties: {
            price: { type: 'string' },
            availability: { type: 'string' }
          }
        }
      }
    ]
  });

  if (result.changeTracking.changeStatus === 'changed') {
    const changes = result.changeTracking.json;
    console.log(`Price: ${changes.price.previous} → ${changes.price.current}`);
  }
  ```

  ```bash cURL theme={null}
  curl -s -X POST "https://api.firecrawl.dev/v2/scrape" \
    -H "Authorization: Bearer $FIRECRAWL_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "url": "https://example.com/product/widget",
      "formats": [
        "markdown",
        {
          "type": "changeTracking",
          "modes": ["json"],
          "schema": {
            "type": "object",
            "properties": {
              "price": { "type": "string" },
              "availability": { "type": "string" }
            }
          }
        }
      ]
    }'
  ```
</CodeGroup>

<div id="response">
  ### Réponse
</div>

Chaque champ du schéma est renvoyé avec les valeurs `previous` et `current` :

```json theme={null}
{
  "changeTracking": {
    "previousScrapeAt": "2025-06-05T08:00:00.000+00:00",
    "changeStatus": "changed",
    "visibility": "visible",
    "json": {
      "price": {
        "previous": "$19.99",
        "current": "$24.99"
      },
      "availability": {
        "previous": "In Stock",
        "current": "In Stock"
      }
    }
  }
}
```

Vous pouvez également transmettre un `prompt` facultatif pour guider l'extraction par le LLM en complément du schéma.

<Info>
  Le mode JSON utilise l'extraction par LLM et coûte **5 crédits par page**. Le suivi des modifications de base et le mode `git-diff` n'entraînent aucun coût supplémentaire.
</Info>

<div id="using-tags">
  ## Utilisation des tags
</div>

Par défaut, le suivi des modifications compare les résultats au scrape le plus récent de la même URL effectué par votre équipe. Les tags vous permettent de maintenir **des historiques de suivi distincts** pour une même URL, ce qui est utile lorsque vous surveillez la même page à des intervalles ou dans des contextes différents.

<CodeGroup>
  ```python Python theme={null}
  # Surveillance horaire (comparée au dernier scrape « hourly »)
  result = firecrawl.scrape(
      "https://example.com/pricing",
      formats=[
          "markdown",
          { "type": "changeTracking", "tag": "hourly" }
      ]
  )

  # Résumé quotidien (comparé au dernier scrape « daily »
  result = firecrawl.scrape(
      "https://example.com/pricing",
      formats=[
          "markdown",
          { "type": "changeTracking", "tag": "daily" }
      ]
  )
  ```

  ```js Node theme={null}
  // Surveillance horaire (comparée au dernier scrape « hourly »)
  const result = await firecrawl.scrape('https://example.com/pricing', {
    formats: [
      'markdown',
      { type: 'changeTracking', tag: 'hourly' }
    ]
  });

  // Résumé quotidien (comparé au dernier scrape « daily »)
  const result2 = await firecrawl.scrape('https://example.com/pricing', {
    formats: [
      'markdown',
      { type: 'changeTracking', tag: 'daily' }
    ]
  });
  ```

  ```bash cURL theme={null}
  curl -s -X POST "https://api.firecrawl.dev/v2/scrape" \
    -H "Authorization: Bearer $FIRECRAWL_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "url": "https://example.com/pricing",
      "formats": [
        "markdown",
        { "type": "changeTracking", "tag": "hourly" }
      ]
    }'
  ```
</CodeGroup>

<div id="crawl-with-change-tracking">
  ## Crawl avec suivi des modifications
</div>

Ajoutez le suivi des modifications aux opérations de crawl pour surveiller l'ensemble d'un site et détecter les changements. Indiquez le format `changeTracking` dans `scrapeOptions` :

<CodeGroup>
  ```python Python theme={null}
  result = firecrawl.crawl(
      "https://example.com",
      limit=50,
      scrape_options={
          "formats": ["markdown", "changeTracking"]
      }
  )

  for page in result.data:
      status = page.changeTracking.changeStatus
      url = page.metadata.url
      print(f"{url}: {status}")
  ```

  ```js Node theme={null}
  const result = await firecrawl.crawl('https://example.com', {
    limit: 50,
    scrapeOptions: {
      formats: ['markdown', 'changeTracking']
    }
  });

  for (const page of result.data) {
    console.log(`${page.metadata.url}: ${page.changeTracking.changeStatus}`);
  }
  ```

  ```bash cURL theme={null}
  curl -s -X POST "https://api.firecrawl.dev/v2/crawl" \
    -H "Authorization: Bearer $FIRECRAWL_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "url": "https://example.com",
      "limit": 50,
      "scrapeOptions": {
        "formats": ["markdown", "changeTracking"]
      }
    }'
  ```
</CodeGroup>

<div id="batch-scrape-with-change-tracking">
  ## Scrape par lot avec suivi des modifications
</div>

Utilisez le [scrape par lot](/fr/features/batch-scrape) pour surveiller un ensemble précis d’URL :

<CodeGroup>
  ```python Python theme={null}
  result = firecrawl.batch_scrape(
      [
          "https://example.com/pricing",
          "https://example.com/product/widget",
          "https://example.com/blog/latest"
      ],
      formats=["markdown", {"type": "changeTracking", "modes": ["git-diff"]}]
  )
  ```

  ```js Node theme={null}
  const result = await firecrawl.batchScrape([
    'https://example.com/pricing',
    'https://example.com/product/widget',
    'https://example.com/blog/latest'
  ], {
    options: {
      formats: ['markdown', { type: 'changeTracking', modes: ['git-diff'] }]
    }
  });
  ```

  ```bash cURL theme={null}
  curl -s -X POST "https://api.firecrawl.dev/v2/batch/scrape" \
    -H "Authorization: Bearer $FIRECRAWL_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "urls": [
        "https://example.com/pricing",
        "https://example.com/product/widget",
        "https://example.com/blog/latest"
      ],
      "formats": [
        "markdown",
        { "type": "changeTracking", "modes": ["git-diff"] }
      ]
    }'
  ```
</CodeGroup>

<div id="scheduling-change-tracking">
  ## Planification du suivi des modifications
</div>

Le suivi des modifications est particulièrement utile lorsque vous effectuez du scraping à intervalles réguliers. Vous pouvez l'automatiser à l'aide de cron, d'ordonnanceurs cloud ou d'outils de workflow.

<div id="cron-job">
  ### Tâche cron
</div>

Créez un script qui surveille une URL et envoie une alerte en cas de changement :

```bash check-pricing.sh theme={null}
#!/bin/bash
RESPONSE=$(curl -s -X POST "https://api.firecrawl.dev/v2/scrape" \
  -H "Authorization: Bearer $FIRECRAWL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://competitor.com/pricing",
    "formats": [
      "markdown",
      {
        "type": "changeTracking",
        "modes": ["json"],
        "schema": {
          "type": "object",
          "properties": {
            "starter_price": { "type": "string" },
            "pro_price": { "type": "string" }
          }
        }
      }
    ]
  }')

STATUS=$(echo "$RESPONSE" | jq -r '.data.changeTracking.changeStatus')

if [ "$STATUS" = "changed" ]; then
  echo "$RESPONSE" | jq '.data.changeTracking.json'
  # Envoyer une alerte par e-mail, Slack, etc.
fi
```

Programmez-le avec `crontab -e` :

```bash theme={null}
0 */6 * * * /path/to/check-pricing.sh >> /var/log/price-monitor.log 2>&1
```

| Planification        | Expression    |
| -------------------- | ------------- |
| Toutes les heures    | `0 * * * *`   |
| Toutes les 6 heures  | `0 */6 * * *` |
| Tous les jours à 9 h | `0 9 * * *`   |
| Chaque lundi à 8 h   | `0 8 * * 1`   |

<div id="cloud-and-serverless-schedulers">
  ### Planificateurs cloud et serverless
</div>

* **AWS** : règle EventBridge qui déclenche une fonction Lambda
* **GCP** : Cloud Scheduler qui déclenche une Cloud Function
* **Vercel / Netlify** : fonctions serverless déclenchées par des tâches cron
* **GitHub Actions** : workflows planifiés avec les déclencheurs `schedule` et `cron`

<div id="workflow-automation">
  ### Automatisation des workflows
</div>

Des plateformes no-code comme **n8n**, **Zapier** et **Make** peuvent appeler l'API Firecrawl à intervalles réguliers et envoyer les résultats vers Slack, l'email ou des bases de données. Consultez les [guides d'automatisation de workflows](/fr/developer-guides/workflow-automation/n8n).

<div id="webhooks">
  ## Webhooks
</div>

Pour les opérations asynchrones comme `crawl` et `batch scrape`, utilisez des [webhooks](/fr/webhooks/overview) pour recevoir les résultats de suivi des modifications dès qu'ils sont disponibles, au lieu d'effectuer du polling.

<CodeGroup>
  ```python Python theme={null}
  job = firecrawl.start_crawl(
      "https://example.com",
      limit=50,
      scrape_options={
          "formats": [
              "markdown",
              {"type": "changeTracking", "modes": ["git-diff"]}
          ]
      },
      webhook={
          "url": "https://your-server.com/firecrawl-webhook",
          "headers": {"Authorization": "Bearer your-webhook-secret"},
          "events": ["crawl.page", "crawl.completed"]
      }
  )
  ```

  ```js Node theme={null}
  const { id } = await firecrawl.startCrawl('https://example.com', {
    limit: 50,
    scrapeOptions: {
      formats: [
        'markdown',
        { type: 'changeTracking', modes: ['git-diff'] }
      ]
    },
    webhook: {
      url: 'https://your-server.com/firecrawl-webhook',
      headers: { Authorization: 'Bearer your-webhook-secret' },
      events: ['crawl.page', 'crawl.completed']
    }
  });
  ```

  ```bash cURL theme={null}
  curl -s -X POST "https://api.firecrawl.dev/v2/crawl" \
    -H "Authorization: Bearer $FIRECRAWL_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "url": "https://example.com",
      "limit": 50,
      "scrapeOptions": {
        "formats": [
          "markdown",
          { "type": "changeTracking", "modes": ["git-diff"] }
        ]
      },
      "webhook": {
        "url": "https://your-server.com/firecrawl-webhook",
        "headers": { "Authorization": "Bearer your-webhook-secret" },
        "events": ["crawl.page", "crawl.completed"]
      }
    }'
  ```
</CodeGroup>

Le payload de l'événement `crawl.page` inclut l'objet `changeTracking` pour chaque page :

```json theme={null}
{
  "success": true,
  "type": "crawl.page",
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "data": [{
    "markdown": "# Pricing\n\nStarter: $12/mo...",
    "metadata": {
      "title": "Pricing",
      "url": "https://example.com/pricing",
      "statusCode": 200
    },
    "changeTracking": {
      "previousScrapeAt": "2025-06-05T12:00:00.000+00:00",
      "changeStatus": "changed",
      "visibility": "visible",
      "diff": {
        "text": "@@ -2,1 +2,1 @@\n-Starter: $9/mo\n+Starter: $12/mo"
      }
    }
  }]
}
```

Pour plus de détails sur la configuration des webhooks (en-têtes, métadonnées, événements, réessais, vérification de la signature), consultez la [documentation sur les webhooks](/fr/webhooks/overview).

<div id="configuration-reference">
  ## Référence de configuration
</div>

Ensemble complet des options disponibles lorsque vous passez un objet de format `changeTracking` :

| Paramètre | Type       | Valeur par défaut | Description                                                                           |
| --------- | ---------- | ----------------- | ------------------------------------------------------------------------------------- |
| `type`    | `string`   | (requis)          | Doit être `"changeTracking"`                                                          |
| `modes`   | `string[]` | `[]`              | Modes de comparaison (diff) à activer : `"git-diff"`, `"json"`, ou les deux           |
| `schema`  | `object`   | (aucune)          | JSON Schema pour la comparaison au niveau des champs (requis pour le mode `json`)     |
| `prompt`  | `string`   | (aucun)           | Prompt personnalisé pour guider l’extraction par le LLM (utilisé avec le mode `json`) |
| `tag`     | `string`   | `null`            | Identifiant distinct pour l’historique de suivi des modifications                     |

<div id="data-models">
  ### Modèles de données
</div>

<CodeGroup>
  ```ts TypeScript theme={null}
  interface ChangeTrackingResult {
    previousScrapeAt: string | null;
    changeStatus: "new" | "same" | "changed" | "removed";
    visibility: "visible" | "hidden";
    diff?: {
      text: string;
      json: {
        files: Array<{
          from: string | null;
          to: string | null;
          chunks: Array<{
            content: string;
            changes: Array<{
              type: "add" | "del" | "normal";
              ln?: number;
              ln1?: number;
              ln2?: number;
              content: string;
            }>;
          }>;
        }>;
      };
    };
    json?: Record<string, { previous: any; current: any }>;
  }
  ```

  ```python Python theme={null}
  class ChangeTrackingData(BaseModel):
      previous_scrape_at: Optional[str] = None
      change_status: str  # "new" | "same" | "changed" | "removed"
      visibility: str     # "visible" | "hidden"
      diff: Optional[Dict[str, Any]] = None
      json: Optional[Dict[str, Any]] = None
  ```
</CodeGroup>

<div id="important-details">
  ## Détails importants
</div>

<Warning>
  Le format `markdown` doit toujours être inclus en même temps que `changeTracking`. Le suivi des modifications compare les pages via leur contenu markdown.
</Warning>

* **Conservation des snapshots** : Les snapshots sont stockés de manière persistante et n'expirent jamais. Un scrape effectué des mois après le précédent sera toujours correctement comparé au snapshot antérieur.
* **Portée** : Les comparaisons sont limitées à votre équipe. Votre premier scrape de n'importe quelle URL renvoie `"new"`, même si d'autres utilisateurs l'ont déjà scrappée.
* **Correspondance d'URL** : Les scrapes précédents sont associés à l'URL source exacte, à l'ID d'équipe, au format `markdown` et au `tag`. Gardez les URL cohérentes entre les scrapes.
* **Cohérence des paramètres** : Utiliser des paramètres `includeTags`, `excludeTags` ou `onlyMainContent` différents entre les scrapes de la même URL produit des comparaisons peu fiables.
* **Algorithme de comparaison** : L'algorithme est résistant aux variations d'espacement et aux changements dans l'ordre du contenu. Les URL sources d'iframe sont ignorées pour gérer la variation aléatoire introduite par les systèmes captcha/antibot.
* **Mise en cache** : Les requêtes avec `changeTracking` contournent le cache d'index. Le paramètre `maxAge` est ignoré.
* **Gestion des erreurs** : Surveillez le champ `warning` dans les réponses et gérez le cas où l'objet `changeTracking` pourrait être absent (cela peut se produire si la recherche en base de données pour le scrape précédent dépasse le délai imparti).

<div id="billing">
  ## Facturation
</div>

| Mode                            | Coût                                                     |
| ------------------------------- | -------------------------------------------------------- |
| suivi des modifications de base | Aucun coût supplémentaire (crédits de scraping standard) |
| mode `git-diff`                 | Aucun coût supplémentaire                                |
| mode `json`                     | 5 crédits par page                                       |

> Êtes-vous un agent IA ayant besoin d’une clé API Firecrawl ? Consultez [firecrawl.dev/agent-onboarding/SKILL.md](https://www.firecrawl.dev/agent-onboarding/SKILL.md) pour obtenir des instructions d’onboarding automatisé.
