メインコンテンツへスキップ
変更追跡 Change Tracking(変更追跡)は、ページの現在のコンテンツを最後にスクレイプしたときの状態と比較します。ページが新規か、変更なし (unchanged) か、変更あり (modified) かを検出するために changeTrackingformats 配列に追加し、必要に応じて「何が変わったか」の構造化された差分を取得できます。
  • /scrape/crawl/batch/scrape で動作
  • 2 つの diff モード: 行単位の変更用 git-diff、フィールド単位の比較用 json
  • チームごとにスコープされ、必要に応じて渡したタグごとにもスコープ可能

仕組み

changeTracking が有効なすべてのスクレイプ実行時にスナップショットが保存され、その URL に対する前回のスナップショットと比較されます。
ScrapeResult
First timechangeStatus: "new"(前のバージョンが存在しない)
Content unchangedchangeStatus: "same"(コンテンツに変更なし)
Content modifiedchangeStatus: "changed"(コンテンツが変更され、差分データあり)
Page removedchangeStatus: "removed"(ページが削除された)
レスポンスには、changeTracking オブジェクト内に次のフィールドが含まれます。
FieldTypeDescription
previousScrapeAtstring | null前回スクレイプのタイムスタンプ(初回スクレイプ時は null
changeStatusstring"new""same""changed"、または "removed"
visibilitystring"visible"(リンクやサイトマップ経由で検出可能)または "hidden"(URL は有効だが、もはやリンクされていない)
diffobject | undefined行レベルの差分(ステータスが "changed" のときに git-diff モードでのみ含まれる)
jsonobject | undefinedフィールドレベルの比較(ステータスが "changed" のときに json モードでのみ含まれる)

基本的な使い方

formats 配列には markdownchangeTracking の両方を指定します。変更追跡ではページ同士を markdown コンテンツとして比較するため、markdown フォーマットは必須です。
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)

レスポンス

初回のスクレイプでは、changeStatus"new"previousScrapeAtnull になります。
{
  "success": true,
  "data": {
    "markdown": "# Pricing\n\nStarter: $9/mo\nPro: $29/mo...",
    "changeTracking": {
      "previousScrapeAt": null,
      "changeStatus": "new",
      "visibility": "visible"
    }
  }
}
以降のスクレイプでは、changeStatus にコンテンツが変更されたかどうかが反映されます。
{
  "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"
    }
  }
}

Git-diff モード

git-diff モードは、git diff に似た形式で行単位の変更差分を返します。formats 配列内に modes: ["git-diff"] を含むオブジェクトを指定します。
result = firecrawl.scrape(
    "https://example.com/pricing",
    formats=[
        "markdown",
        {
            "type": "changeTracking",
            "modes": ["git-diff"]
        }
    ]
)

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

レスポンス

diff オブジェクトには、プレーンテキスト形式の diff と構造化された 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" }
            ]
          }]
        }]
      }
    }
  }
}
構造化された diff.json オブジェクトには次の要素が含まれます:
  • files: 変更されたファイルの配列(通常はウェブページごとに1つ)
  • chunks: ファイル内の変更箇所を表すセクション
  • changes: 個々の行ごとの変更。type"add""del"、または "normal")、行番号(ln)、および content を含む

JSONモード

json モードは、定義したスキーマを使って、ページの現在のバージョンと前回のバージョンの両方から特定のフィールドを抽出します。これは、ページ全体の差分を解析することなく、価格、在庫レベル、メタデータなどの構造化データの変更を追跡するのに便利です。 抽出するフィールドを定義する schema とともに modes: ["json"] を渡します:
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']}")

レスポンス

スキーマ内の各フィールドは、previouscurrent の値を含めて返されます。
{
  "changeTracking": {
    "previousScrapeAt": "2025-06-05T08:00:00.000+00:00",
    "changeStatus": "changed",
    "visibility": "visible",
    "json": {
      "price": {
        "previous": "$19.99",
        "current": "$24.99"
      },
      "availability": {
        "previous": "在庫あり",
        "current": "在庫あり"
      }
    }
  }
}
また、任意の prompt を渡して、スキーマとあわせて LLM による抽出をガイドすることもできます。
JSONモードは LLM 抽出を使用し、1ページあたり5クレジットかかります。基本的な変更追跡と git-diff モードには追加コストは発生しません。

タグの使用

デフォルトでは、変更追跡(change tracking)は、チームによって同じ URL に対して実行された直近のスクレイプ結果と比較します。タグを使うと、同じ URL に対して別々の追跡履歴を維持できます。同じページを異なる間隔や異なるコンテキストで監視したい場合に便利です。
# 毎時監視(直近の「hourly」スクレイプ結果と比較)
result = firecrawl.scrape(
    "https://example.com/pricing",
    formats=[
        "markdown",
        { "type": "changeTracking", "tag": "hourly" }
    ]
)

# 日次サマリー(直近の「daily」スクレイプ結果と比較)
result = firecrawl.scrape(
    "https://example.com/pricing",
    formats=[
        "markdown",
        { "type": "changeTracking", "tag": "daily" }
    ]
)

change tracking を利用したクロール

サイト全体の変更を監視するために、クロール処理に change tracking を追加します。scrapeOptions 内で changeTracking フォーマットを指定します。
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}")

changeTracking を使ったバッチスクレイプ

特定の URL 群を監視するには、batch scrape を使用します:
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"]}]
)

変更追跡のスケジュール設定

変更追跡は、定期的にスクレイピングを行う場合に最も効果を発揮します。cron、クラウドスケジューラ、ワークフローツールなどで自動化できます。

Cronジョブ

URLをスクレイピングし、変更を検知したらアラートを送るスクリプトを作成します。
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'
  # メール、Slackなどでアラートを送信
fi
crontab -e でスケジュールを設定します:
0 */6 * * * /path/to/check-pricing.sh >> /var/log/price-monitor.log 2>&1
スケジュール表現
毎時0 * * * *
6時間ごと0 */6 * * *
毎日午前9時0 9 * * *
毎週月曜日午前8時0 8 * * 1

クラウドおよびサーバーレスのスケジューラー

  • AWS: EventBridge ルールによる Lambda 関数のトリガー
  • GCP: Cloud Scheduler による Cloud Function のトリガー
  • Vercel / Netlify: Cron によってトリガーされるサーバーレス関数
  • GitHub Actions: schedule および cron トリガーによるスケジュールされたワークフロー

ワークフロー自動化

n8nZapierMake のようなノーコードプラットフォームから、スケジュール実行で Firecrawl API を呼び出し、結果を Slack、メール、データベースなどに送信できます。ワークフロー自動化ガイドも参照してください。

Webhooks

crawl や batch scrape のような非同期処理では、ポーリングするのではなく webhooks を使用して、到着しだい changeTracking の結果を受け取れます。
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"]
    }
)
crawl.page イベントのペイロードには、各ページごとに changeTracking オブジェクトが含まれています。
{
  "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"
      }
    }
  }]
}
Webhook の構成の詳細(ヘッダー、メタデータ、イベント、再試行、署名検証)については、Webhook ドキュメントを参照してください。

設定リファレンス

changeTracking フォーマットオブジェクトを渡すときに利用できるオプション一覧:
ParameterTypeDefaultDescription
typestring(required)"changeTracking" でなければなりません
modesstring[][]有効化する差分モード: "git-diff""json"、またはその両方
schemaobject(none)フィールド単位の比較用の JSON Schema(json モードでは必須)
promptstring(none)LLM による抽出を制御するためのカスタムプロンプト(json モードで使用)
tagstringnull独立したトラッキング履歴用の識別子

データモデル

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 }>;
}

重要な詳細

changeTracking を使う場合は、必ず markdown フォーマットも同時に指定してください。変更追跡は、ページをその markdown コンテンツに基づいて比較します。
  • スコープ: 比較はチーム内に限定されます。任意の URL をチームとして初めてスクレイプした場合は、他のユーザーが同じ URL をスクレイプしていても "new" が返されます。
  • URL のマッチング: 過去のスクレイプは、ソース URL、チーム ID、markdown フォーマット、tag が完全一致したものと照合されます。スクレイプ間で URL を一貫させてください。
  • パラメータの一貫性: 同じ URL に対して異なる includeTagsexcludeTagsonlyMainContent 設定を使うと、比較結果が信頼できなくなります。
  • 比較アルゴリズム: このアルゴリズムは、空白やコンテンツ順序の変化に強く設計されています。CAPTCHA やボット対策によるランダム化に対応するため、iframe のソース URL は無視されます。
  • キャッシュ: changeTracking を指定したリクエストは、インデックス キャッシュをバイパスします。maxAge パラメータは無視されます。
  • エラー処理: レスポンス内の warning フィールドを監視し、changeTracking オブジェクトが存在しない可能性を考慮して処理してください(これは、前回スクレイプのデータベース検索がタイムアウトした場合に発生することがあります)。

料金

モード料金
基本的な変更追跡追加料金なし(通常のスクレイプクレジットを使用)
git-diff モード追加料金なし
json モード1ページあたり 5 クレジット