# Seguimiento de cambios (/es/features/change-tracking)

<!-- agent-signals: reading_time_min: 14 · est_tokens: 7404 · updated: 2026-07-30 -->
Related: [Búsqueda](/es/features/search.md), [Highlights de búsqueda](/es/features/search-highlights.md), [Índice de investigación](/es/features/research.md), [scraping](/es/features/scrape.md), [Scraping más rápido](/es/features/fast-scraping.md), [Raspado en lote](/es/features/batch-scrape.md)

![Seguimiento de cambios](/_assets/ffd62c1d9e17f5a7db0298256d18a5f3628831e42ec5119a75a832a7ba4f3fc9)

El seguimiento de cambios compara el contenido actual de una página con el de la última vez que la extrajiste con scraping. Añade `changeTracking` a tu array de `formats` para detectar si una página es nueva, no ha cambiado o se ha modificado y, opcionalmente, obtener un diff estructurado de lo que cambió.

* Funciona con `/scrape`, `/crawl` y `/batch/scrape`
* Dos modos de diff: `git-diff` para cambios a nivel de línea y `json` para comparación a nivel de campo
* Acotado a tu equipo y, opcionalmente, a una etiqueta que especifiques

<div id="how-it-works">
  ## Cómo funciona [#cómo-funciona]
</div>

Cada extracción con `changeTracking` habilitado almacena una captura y la compara con la captura anterior para esa URL. Las capturas se almacenan de forma persistente y no caducan, por lo que las comparaciones se mantienen precisas sin importar cuánto tiempo haya pasado entre extracciones.

| Extracción            | Resultado                                              |
| --------------------- | ------------------------------------------------------ |
| Primera vez           | `changeStatus: "new"` (no existe una versión anterior) |
| Contenido sin cambios | `changeStatus: "same"`                                 |
| Contenido modificado  | `changeStatus: "changed"` (datos de diff disponibles)  |
| Página eliminada      | `changeStatus: "removed"`                              |

La respuesta incluye estos campos en el objeto `changeTracking`:

| Campo              | Tipo                  | Descripción                                                                                                  |
| ------------------ | --------------------- | ------------------------------------------------------------------------------------------------------------ |
| `previousScrapeAt` | `string \| null`      | Marca de tiempo de la extracción anterior (`null` en la primera extracción)                                  |
| `changeStatus`     | `string`              | `"new"`, `"same"`, `"changed"` o `"removed"`                                                                 |
| `visibility`       | `string`              | `"visible"` (detectable a través de enlaces/sitemap) o `"hidden"` (la URL funciona pero ya no está enlazada) |
| `diff`             | `object \| undefined` | Diff a nivel de línea (solo presente en modo `git-diff` cuando el estado es `"changed"`)                     |
| `json`             | `object \| undefined` | Comparación a nivel de campo (solo presente en modo `json` cuando el estado es `"changed"`)                  |

<div id="basic-usage">
  ## Uso básico [#uso-básico]
</div>

Incluye tanto `markdown` como `seguimientoDeCambios` en el array `formats`. El formato `markdown` es obligatorio porque seguimientoDeCambios compara las páginas según su contenido en markdown.

<CodeGroup>
  <CodeBlockTabs defaultValue="Python" groupId="curl+node+python">
    <CodeBlockTabsList>
      <CodeBlockTabsTrigger value="Python">
        Python
      </CodeBlockTabsTrigger>

      <CodeBlockTabsTrigger value="Node">
        Node
      </CodeBlockTabsTrigger>

      <CodeBlockTabsTrigger value="cURL">
        cURL
      </CodeBlockTabsTrigger>
    </CodeBlockTabsList>

    <CodeBlockTab value="Python">
      ```python  
      from firecrawl import Firecrawl

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

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

      print(result.changeTracking)
      ```
    </CodeBlockTab>

    <CodeBlockTab value="Node">
      ```js  
      import { Firecrawl } from 'firecrawl';

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

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

      console.log(result.changeTracking);
      ```
    </CodeBlockTab>

    <CodeBlockTab value="cURL">
      ```bash  
      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", "changeTracking"]
        }'
      ```
    </CodeBlockTab>
  </CodeBlockTabs>
</CodeGroup>

<div id="response">
  ### Respuesta [#respuesta]
</div>

En el primer scraping, `changeStatus` es `"new"` y `previousScrapeAt` es `null`:

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

En scrapes posteriores, `changeStatus` indica si el contenido ha cambiado:

```json
{
  "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">
  ## Modo git-diff [#modo-git-diff]
</div>

El modo `git-diff` retorna cambios línea por línea en un formato similar a `git diff`. Pasa un objeto dentro del array `formats` con `modes: ["git-diff"]`:

<CodeGroup>
  <CodeBlockTabs defaultValue="Python" groupId="curl+node+python">
    <CodeBlockTabsList>
      <CodeBlockTabsTrigger value="Python">
        Python
      </CodeBlockTabsTrigger>

      <CodeBlockTabsTrigger value="Node">
        Node
      </CodeBlockTabsTrigger>

      <CodeBlockTabsTrigger value="cURL">
        cURL
      </CodeBlockTabsTrigger>
    </CodeBlockTabsList>

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

      if result.changeTracking.changeStatus == "changed":
          print(result.changeTracking.diff.text)
      ```
    </CodeBlockTab>

    <CodeBlockTab value="Node">
      ```js  
      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);
      }
      ```
    </CodeBlockTab>

    <CodeBlockTab value="cURL">
      ```bash  
      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"] }
          ]
        }'
      ```
    </CodeBlockTab>
  </CodeBlockTabs>
</CodeGroup>

<div id="response">
  ### Respuesta [#respuesta-1]
</div>

El objeto `diff` contiene tanto un diff en texto plano como una representación JSON estructurada:

```json
{
  "changeTracking": {
    "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" }
            ]
          }]
        }]
      }
    }
  }
}
```

El objeto estructurado `diff.json` contiene:

* `files`: matriz de archivos modificados (generalmente uno por página web)
* `chunks`: secciones de cambios dentro de un archivo
* `changes`: cambios de líneas individuales con `type` (`"add"`, `"del"` o `"normal"`), número de línea (`ln`) y `content`

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

El modo `json` extrae campos específicos tanto de la versión actual como de la versión anterior de la página usando un esquema que defines. Esto es útil para hacer un seguimiento de cambios en datos estructurados como precios, niveles de stock o metadatos sin tener que analizar un diff completo.

Pasa `modes: ["json"]` con un `schema` que defina los campos a extraer:

<CodeGroup>
  <CodeBlockTabs defaultValue="Python" groupId="curl+node+python">
    <CodeBlockTabsList>
      <CodeBlockTabsTrigger value="Python">
        Python
      </CodeBlockTabsTrigger>

      <CodeBlockTabsTrigger value="Node">
        Node
      </CodeBlockTabsTrigger>

      <CodeBlockTabsTrigger value="cURL">
        cURL
      </CodeBlockTabsTrigger>
    </CodeBlockTabsList>

    <CodeBlockTab value="Python">
      ```python  
      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']}")
      ```
    </CodeBlockTab>

    <CodeBlockTab value="Node">
      ```js  
      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}`);
      }
      ```
    </CodeBlockTab>

    <CodeBlockTab value="cURL">
      ```bash  
      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" }
                }
              }
            }
          ]
        }'
      ```
    </CodeBlockTab>
  </CodeBlockTabs>
</CodeGroup>

<div id="response">
  ### Respuesta [#respuesta-2]
</div>

Cada campo del esquema se devuelve con valores `previous` y `current`:

```json
{
  "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"
      }
    }
  }
}
```

También puedes proporcionar un `prompt` opcional para guiar la extracción mediante el LLM junto con el esquema.

<Info>
  El modo JSON usa extracción con LLM y cuesta **5 créditos por página**. El seguimiento básico de cambios y el modo `git-diff` no tienen costo adicional.
</Info>

<div id="using-tags">
  ## Uso de etiquetas [#uso-de-etiquetas]
</div>

De forma predeterminada, el seguimiento de cambios se compara con el scrape más reciente de la misma URL realizado por tu equipo. Las etiquetas te permiten mantener **historiales de seguimiento independientes** para la misma URL, lo cual es útil cuando supervisas la misma página en diferentes intervalos o en distintos contextos.

<CodeGroup>
  <CodeBlockTabs defaultValue="Python" groupId="curl+node+python">
    <CodeBlockTabsList>
      <CodeBlockTabsTrigger value="Python">
        Python
      </CodeBlockTabsTrigger>

      <CodeBlockTabsTrigger value="Node">
        Node
      </CodeBlockTabsTrigger>

      <CodeBlockTabsTrigger value="cURL">
        cURL
      </CodeBlockTabsTrigger>
    </CodeBlockTabsList>

    <CodeBlockTab value="Python">
      ```python  
      # Monitoreo por hora (comparado con el último scrape con etiqueta "hourly")
      result = firecrawl.scrape(
          "https://example.com/pricing",
          formats=[
              "markdown",
              { "type": "changeTracking", "tag": "hourly" }
          ]
      )

      # Resumen diario (comparado con el último scrape con etiqueta "daily")
      result = firecrawl.scrape(
          "https://example.com/pricing",
          formats=[
              "markdown",
              { "type": "changeTracking", "tag": "daily" }
          ]
      )
      ```
    </CodeBlockTab>

    <CodeBlockTab value="Node">
      ```js  
      // Monitoreo por hora (comparado con el último scrape con etiqueta "hourly")
      const result = await firecrawl.scrape('https://example.com/pricing', {
        formats: [
          'markdown',
          { type: 'changeTracking', tag: 'hourly' }
        ]
      });

      // Resumen diario (comparado con el último scrape con etiqueta "daily")
      const result2 = await firecrawl.scrape('https://example.com/pricing', {
        formats: [
          'markdown',
          { type: 'changeTracking', tag: 'daily' }
        ]
      });
      ```
    </CodeBlockTab>

    <CodeBlockTab value="cURL">
      ```bash  
      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" }
          ]
        }'
      ```
    </CodeBlockTab>
  </CodeBlockTabs>
</CodeGroup>

<div id="crawl-with-change-tracking">
  ## Rastreo con seguimiento de cambios [#rastreo-con-seguimiento-de-cambios]
</div>

Añade seguimiento de cambios a las operaciones de rastreo para supervisar un sitio completo en busca de cambios. Pasa el formato `changeTracking` dentro de `scrapeOptions`:

<CodeGroup>
  <CodeBlockTabs defaultValue="Python" groupId="curl+node+python">
    <CodeBlockTabsList>
      <CodeBlockTabsTrigger value="Python">
        Python
      </CodeBlockTabsTrigger>

      <CodeBlockTabsTrigger value="Node">
        Node
      </CodeBlockTabsTrigger>

      <CodeBlockTabsTrigger value="cURL">
        cURL
      </CodeBlockTabsTrigger>
    </CodeBlockTabsList>

    <CodeBlockTab value="Python">
      ```python  
      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}")
      ```
    </CodeBlockTab>

    <CodeBlockTab value="Node">
      ```js  
      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}`);
      }
      ```
    </CodeBlockTab>

    <CodeBlockTab value="cURL">
      ```bash  
      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"]
          }
        }'
      ```
    </CodeBlockTab>
  </CodeBlockTabs>
</CodeGroup>

<div id="batch-scrape-with-change-tracking">
  ## Extracción por lotes con seguimiento de cambios [#extracción-por-lotes-con-seguimiento-de-cambios]
</div>

Usa [batch scrape](/es/features/batch-scrape) para supervisar un conjunto específico de URL:

<CodeGroup>
  <CodeBlockTabs defaultValue="Python" groupId="curl+node+python">
    <CodeBlockTabsList>
      <CodeBlockTabsTrigger value="Python">
        Python
      </CodeBlockTabsTrigger>

      <CodeBlockTabsTrigger value="Node">
        Node
      </CodeBlockTabsTrigger>

      <CodeBlockTabsTrigger value="cURL">
        cURL
      </CodeBlockTabsTrigger>
    </CodeBlockTabsList>

    <CodeBlockTab value="Python">
      ```python  
      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"]}]
      )
      ```
    </CodeBlockTab>

    <CodeBlockTab value="Node">
      ```js  
      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'] }]
        }
      });
      ```
    </CodeBlockTab>

    <CodeBlockTab value="cURL">
      ```bash  
      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"] }
          ]
        }'
      ```
    </CodeBlockTab>
  </CodeBlockTabs>
</CodeGroup>

<div id="scheduling-change-tracking">
  ## Programar el seguimiento de cambios [#programar-el-seguimiento-de-cambios]
</div>

El seguimiento de cambios es más útil cuando haces scraping a intervalos regulares. Puedes automatizarlo con cron, planificadores en la nube o herramientas de orquestación de flujos de trabajo.

<div id="cron-job">
  ### Tarea cron [#tarea-cron]
</div>

Crea un script que haga scraping de una URL y notifique cuando haya cambios:

```bash title="check-pricing.sh"
#!/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'
  # Envía alerta por correo electrónico, Slack, etc.
fi
```

Programa la tarea con `crontab -e`:

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

| Programación                | Expresión     |
| --------------------------- | ------------- |
| Cada hora                   | `0 * * * *`   |
| Cada 6 horas                | `0 */6 * * *` |
| Todos los días a las 09:00  | `0 9 * * *`   |
| Todos los lunes a las 08:00 | `0 8 * * 1`   |

<div id="cloud-and-serverless-schedulers">
  ### Planificadores en la nube y sin servidor [#planificadores-en-la-nube-y-sin-servidor]
</div>

* **AWS**: regla de EventBridge que invoca una función Lambda
* **GCP**: Cloud Scheduler que invoca una Cloud Function
* **Vercel / Netlify**: funciones serverless activadas mediante cron
* **GitHub Actions**: flujos de trabajo programados con los desencadenadores `schedule` y `cron`

<div id="workflow-automation">
  ### Automatización de flujos de trabajo [#automatización-de-flujos-de-trabajo]
</div>

Plataformas sin código como **n8n**, **Zapier** y **Make** pueden llamar periódicamente a la API de Firecrawl y enrutar los resultados a Slack, correo electrónico o bases de datos. Consulta las [guías sobre automatización de flujos de trabajo](/es/developer-guides/workflow-automation/n8n).

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

Para operaciones asíncronas como `crawl` y *batch scrape*, usa [webhooks](/es/webhooks/overview) para recibir resultados de seguimientoDeCambios a medida que llegan en lugar de hacer *polling*.

<CodeGroup>
  <CodeBlockTabs defaultValue="Python" groupId="curl+node+python">
    <CodeBlockTabsList>
      <CodeBlockTabsTrigger value="Python">
        Python
      </CodeBlockTabsTrigger>

      <CodeBlockTabsTrigger value="Node">
        Node
      </CodeBlockTabsTrigger>

      <CodeBlockTabsTrigger value="cURL">
        cURL
      </CodeBlockTabsTrigger>
    </CodeBlockTabsList>

    <CodeBlockTab value="Python">
      ```python  
      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"]
          }
      )
      ```
    </CodeBlockTab>

    <CodeBlockTab value="Node">
      ```js  
      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']
        }
      });
      ```
    </CodeBlockTab>

    <CodeBlockTab value="cURL">
      ```bash  
      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"]
          }
        }'
      ```
    </CodeBlockTab>
  </CodeBlockTabs>
</CodeGroup>

El payload del evento `crawl.page` incluye el objeto `changeTracking` para cada página:

```json
{
  "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"
      }
    }
  }]
}
```

Para más detalles sobre la configuración de webhooks (encabezados, metadatos, eventos, reintentos, verificación de firma), consulta la [documentación de webhooks](/es/webhooks/overview).

<div id="configuration-reference">
  ## Referencia de configuración [#referencia-de-configuración]
</div>

El conjunto completo de opciones disponibles al pasar un objeto de formato `seguimientoDeCambios`:

| Parameter | Type       | Default    | Description                                                                      |
| --------- | ---------- | ---------- | -------------------------------------------------------------------------------- |
| `type`    | `string`   | (required) | Debe ser `"changeTracking"`                                                      |
| `modes`   | `string[]` | `[]`       | Modos de diff a habilitar: `"git-diff"`, `"json"` o ambos                        |
| `schema`  | `object`   | (none)     | JSON Schema para la comparación a nivel de campo (requerido para el modo `json`) |
| `prompt`  | `string`   | (none)     | Prompt personalizado para guiar la extracción del LLM (usado con el modo `json`) |
| `tag`     | `string`   | `null`     | Identificador independiente para el historial de seguimiento                     |

<div id="data-models">
  ### Modelos de datos [#modelos-de-datos]
</div>

<CodeGroup>
  <CodeBlockTabs defaultValue="TypeScript" groupId="python+typescript">
    <CodeBlockTabsList>
      <CodeBlockTabsTrigger value="TypeScript">
        TypeScript
      </CodeBlockTabsTrigger>

      <CodeBlockTabsTrigger value="Python">
        Python
      </CodeBlockTabsTrigger>
    </CodeBlockTabsList>

    <CodeBlockTab value="TypeScript">
      ```ts  
      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 }>;
      }
      ```
    </CodeBlockTab>

    <CodeBlockTab value="Python">
      ```python  
      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
      ```
    </CodeBlockTab>
  </CodeBlockTabs>
</CodeGroup>

<div id="important-details">
  ## Detalles importantes [#detalles-importantes]
</div>

<Warning>
  El formato `markdown` siempre debe incluirse junto con `changeTracking`. El seguimiento de cambios compara las páginas mediante su contenido en markdown.
</Warning>

* **Retención de snapshots**: Los snapshots se almacenan de forma persistente y no caducan. Una extracción realizada meses después de la anterior seguirá comparándose correctamente con el snapshot anterior.
* **Alcance**: Las comparaciones se limitan a tu equipo. La primera extracción de cualquier URL devuelve `"new"`, incluso si otros usuarios ya la han extraído.
* **Coincidencia de URL**: Las extracciones anteriores se asocian por la URL de origen exacta, el ID de equipo, el formato `markdown` y la `tag`. Mantén las URLs consistentes entre extracciones.
* **Consistencia de parámetros**: Usar configuraciones diferentes de `includeTags`, `excludeTags` u `onlyMainContent` en extracciones de la misma URL produce comparaciones poco fiables.
* **Algoritmo de comparación**: El algoritmo es resistente a cambios en espacios en blanco y en el orden del contenido. Se ignoran las URLs de origen de iframes para manejar la aleatorización de captcha/antibot.
* **Caché**: Las solicitudes con `changeTracking` omiten la caché del índice. Se ignora el parámetro `maxAge`.
* **Manejo de errores**: Supervisa el campo `warning` en las respuestas y contempla el caso en que el objeto `changeTracking` pueda estar ausente (esto puede ocurrir si la consulta a la base de datos para la extracción previa excede el tiempo de espera).

<div id="billing">
  ## Facturación [#facturación]
</div>

| Modo                          | Costo                                               |
| ----------------------------- | --------------------------------------------------- |
| Seguimiento básico de cambios | Sin costo adicional (créditos de scraping estándar) |
| Modo `git-diff`               | Sin costo adicional                                 |
| Modo `json`                   | 5 créditos por página                               |

> ¿Eres un agente de IA que necesita una clave de API de Firecrawl? Consulta [firecrawl.dev/agent-onboarding/SKILL.md](https://www.firecrawl.dev/agent-onboarding/SKILL.md) para ver las instrucciones de onboarding automatizado.
