> ## 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.

# Rastreio de mudanças

> Detecte e monitore mudanças em conteúdo da web entre scrapes

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

O rastreio de mudanças compara o conteúdo atual de uma página com o da última vez em que você fez o scrape. Adicione `changeTracking` ao seu array de `formats` para detectar se uma página é nova, inalterada ou modificada e, opcionalmente, obter um diff estruturado do que mudou.

* Funciona com `/scrape`, `/crawl` e `/batch/scrape`
* Dois modos de diff: `git-diff` para mudanças em nível de linha e `json` para comparação em nível de campo
* Restrito à sua equipe e, opcionalmente, a uma tag que você especificar

<div id="how-it-works">
  ## Como funciona
</div>

Cada scrape com `changeTracking` habilitado armazena um snapshot e o compara com o snapshot anterior para essa URL. Snapshots são armazenados de forma persistente e não expiram, por isso as comparações permanecem precisas, independentemente de quanto tempo se passou entre os scrapes.

| Scrape              | Resultado                                              |
| ------------------- | ------------------------------------------------------ |
| Primeiro scrape     | `changeStatus: "new"` (nenhuma versão anterior existe) |
| Conteúdo inalterado | `changeStatus: "same"`                                 |
| Conteúdo modificado | `changeStatus: "changed"` (dados de diff disponíveis)  |
| Página removida     | `changeStatus: "removed"`                              |

A resposta inclui estes campos no objeto `changeTracking`:

| Campo              | Tipo                  | Descrição                                                                                             |
| ------------------ | --------------------- | ----------------------------------------------------------------------------------------------------- |
| `previousScrapeAt` | `string \| null`      | Carimbo de data e hora do scrape anterior (`null` no primeiro scrape)                                 |
| `changeStatus`     | `string`              | `"new"`, `"same"`, `"changed"` ou `"removed"`                                                         |
| `visibility`       | `string`              | `"visible"` (localizável via links/sitemap) ou `"hidden"` (a URL funciona, mas não está mais linkada) |
| `diff`             | `object \| undefined` | Diff em nível de linha (presente apenas no modo `git-diff` quando o status é `"changed"`)             |
| `json`             | `object \| undefined` | Comparação em nível de campo (presente apenas no modo `json` quando o status é `"changed"`)           |

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

Inclua tanto `markdown` quanto `changeTracking` no array `formats`. O formato `markdown` é obrigatório porque o rastreio de mudanças compara as páginas com base no conteúdo em 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", "changeTracking"]
  )

  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', 'changeTracking']
  });

  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", "changeTracking"]
    }'
  ```
</CodeGroup>

<div id="response">
  ### Resposta
</div>

No primeiro scrape, `changeStatus` é `"new"` e `previousScrapeAt` é `null`:

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

Em raspagens posteriores, `changeStatus` indica se o conteúdo foi alterado:

```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">
  ## Modo git-diff
</div>

O modo `git-diff` retorna alterações linha a linha em um formato semelhante ao `git diff`. Passe um objeto no array `formats` com `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">
  ### Resposta
</div>

O objeto `diff` contém um diff em texto simples e uma representação JSON estruturada:

```json theme={null}
{
  "rastreioDeMudanças": {
    "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" }
            ]
          }]
        }]
      }
    }
  }
}
```

O objeto estruturado `diff.json` contém:

* `files`: array de arquivos modificados (geralmente um por página da web)
* `chunks`: seções de alterações dentro de um arquivo
* `changes`: alterações individuais de linha com `type` (`"add"`, `"del"` ou `"normal"`), número da linha (`ln`) e `content`

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

O modo `json` extrai campos específicos tanto da versão atual quanto da versão anterior da página usando um schema que você define. Isso é útil para acompanhar mudanças em dados estruturados, como preços, níveis de estoque ou metadados, sem precisar analisar um diff completo.

Passe `modes: ["json"]` com um `schema` definindo os campos a serem extraídos:

<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">
  ### Resposta
</div>

Cada campo do schema é retornado com os valores `previous` e `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"
      }
    }
  }
}
```

Você também pode fornecer um `prompt` opcional para orientar a extração do LLM em conjunto com o schema.

<Info>
  O modo JSON usa extração com LLM e custa **5 créditos por página**. O rastreamento básico de alterações e o modo `git-diff` não têm custo adicional.
</Info>

<div id="using-tags">
  ## Usando tags
</div>

Por padrão, o rastreamento de mudanças compara com a raspagem mais recente da mesma URL feita pela sua equipe. Tags permitem que você mantenha **históricos de rastreamento separados** para a mesma URL, o que é útil quando você monitora a mesma página em intervalos diferentes ou em contextos distintos.

<CodeGroup>
  ```python Python theme={null}
  # Monitoramento horário (comparado com a última raspagem com tag "hourly")
  result = firecrawl.scrape(
      "https://example.com/pricing",
      formats=[
          "markdown",
          { "type": "changeTracking", "tag": "hourly" }
      ]
  )

  # Resumo diário (comparado com a última raspagem com tag "daily")
  result = firecrawl.scrape(
      "https://example.com/pricing",
      formats=[
          "markdown",
          { "type": "changeTracking", "tag": "daily" }
      ]
  )
  ```

  ```js Node theme={null}
  // Monitoramento horário (comparado com a última raspagem com tag "hourly")
  const result = await firecrawl.scrape('https://example.com/pricing', {
    formats: [
      'markdown',
      { type: 'changeTracking', tag: 'hourly' }
    ]
  });

  // Resumo diário (comparado com a última raspagem com tag "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 com rastreio de mudanças
</div>

Adicione rastreio de mudanças às operações de crawl para monitorar um site inteiro em busca de alterações. Passe o formato `changeTracking` dentro de `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">
  ## Raspagem em lote com rastreamento de mudanças
</div>

Use a [raspagem em lote](/pt-BR/features/batch-scrape) para monitorar um conjunto específico de URLs:

<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">
  ## Agendando o rastreamento de alterações
</div>

O rastreamento de alterações é mais útil quando você faz scraping em uma agenda regular. Você pode automatizar isso com cron, agendadores em nuvem ou ferramentas de workflow.

<div id="cron-job">
  ### Cron job
</div>

Crie um script que extraia dados de uma URL e envie alertas quando houver alterações:

```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'
  # Envie alerta por e-mail, Slack, etc.
fi
```

Agende isso com `crontab -e`:

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

| Agendamento              | Expressão     |
| ------------------------ | ------------- |
| A cada hora              | `0 * * * *`   |
| A cada 6 horas           | `0 */6 * * *` |
| Diariamente às 9h        | `0 9 * * *`   |
| Toda segunda-feira às 8h | `0 8 * * 1`   |

<div id="cloud-and-serverless-schedulers">
  ### Agendadores em ambientes cloud e serverless
</div>

* **AWS**: regra do EventBridge acionando uma função Lambda
* **GCP**: Cloud Scheduler acionando uma Cloud Function
* **Vercel / Netlify**: funções serverless acionadas por cron
* **GitHub Actions**: workflows agendados com gatilhos `schedule` e `cron`

<div id="workflow-automation">
  ### Automação de fluxos de trabalho
</div>

Plataformas no-code como **n8n**, **Zapier** e **Make** podem chamar a API do Firecrawl em intervalos programados e enviar os resultados para o Slack, e-mail ou bancos de dados. Veja os [guias de automação de fluxos de trabalho](/pt-BR/developer-guides/workflow-automation/n8n).

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

Para operações assíncronas como crawl e batch scrape, use [webhooks](/pt-BR/webhooks/overview) para receber resultados de rastreio de mudanças assim que chegarem, em vez de fazer 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>

O payload do evento `crawl.page` inclui o objeto `changeTracking` para cada página:

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

Para obter detalhes sobre a configuração de webhooks (cabeçalhos, metadados, eventos, tentativas de nova entrega, verificação de assinatura), consulte a [documentação de webhooks](/pt-BR/webhooks/overview).

<div id="configuration-reference">
  ## Referência de configuração
</div>

O conjunto completo de opções disponível ao passar um objeto no formato `changeTracking`:

| Parâmetro | Tipo       | Padrão        | Descrição                                                                        |
| --------- | ---------- | ------------- | -------------------------------------------------------------------------------- |
| `type`    | `string`   | (obrigatório) | Deve ser `"changeTracking"`                                                      |
| `modes`   | `string[]` | `[]`          | Modos de diff a serem habilitados: `"git-diff"`, `"json"` ou ambos               |
| `schema`  | `object`   | (nenhum)      | JSON Schema para comparação em nível de campo (obrigatório para o modo `json`)   |
| `prompt`  | `string`   | (nenhum)      | Prompt personalizado para orientar a extração pelo LLM (usado com o modo `json`) |
| `tag`     | `string`   | `null`        | Identificador separado para o histórico de rastreamento                          |

<div id="data-models">
  ### Modelos de dados
</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">
  ## Detalhes importantes
</div>

<Warning>
  O formato `markdown` deve sempre ser incluído junto com `changeTracking`. O rastreioDeMudanças compara páginas com base no conteúdo em markdown.
</Warning>

* **Retenção de snapshots**: Snapshots são armazenados de forma persistente e não expiram. Uma raspagem feita meses após a anterior ainda será comparada corretamente com o snapshot anterior.
* **Escopo**: As comparações são limitadas à sua equipe. Sua primeira raspagem de qualquer URL retorna `"new"`, mesmo que outros usuários já a tenham raspado.
* **Correspondência de URL**: Raspagens anteriores são associadas com base na URL de origem exata, ID da equipe, formato `markdown` e `tag`. Mantenha as URLs consistentes entre raspagens.
* **Consistência de parâmetros**: Usar configurações diferentes de `includeTags`, `excludeTags` ou `onlyMainContent` em raspagens da mesma URL produz comparações não confiáveis.
* **Algoritmo de comparação**: O algoritmo é resistente a mudanças em espaços em branco e na ordem do conteúdo. URLs de origem de iframes são ignoradas para lidar com a aleatoriedade de captcha/antibot.
* **Cache**: Solicitações com `changeTracking` contornam o cache do índice. O parâmetro `maxAge` é ignorado.
* **Tratamento de erros**: Monitore o campo `warning` nas respostas e esteja preparado para o caso de o objeto `changeTracking` estar ausente (isso pode ocorrer se a consulta ao banco de dados para a raspagem anterior exceder o tempo limite).

<div id="billing">
  ## Cobrança
</div>

| Modo                        | Custo                                             |
| --------------------------- | ------------------------------------------------- |
| Rastreio básico de mudanças | Sem custo adicional (créditos padrão de scraping) |
| `git-diff` mode             | Sem custo adicional                               |
| `json` mode                 | 5 créditos por página                             |

> Você é um agente de IA que precisa de uma chave de API do Firecrawl? Consulte [firecrawl.dev/agent-onboarding/SKILL.md](https://www.firecrawl.dev/agent-onboarding/SKILL.md) para instruções de integração automatizada.
