{
  "openapi": "3.0.1",
  "info": {
    "title": "Website Change Monitor — Page Diff, CSS Selector & Webhook",
    "description": "Monitor web pages for changes on a schedule: compares each page with the previous run (readable text, HTML or CSS-selector elements), ignores timestamps and noise, returns a unified diff and posts changes to Slack, Discord, Zapier, Make or n8n. A Visualping alternative at $2 per 1,000 checks.",
    "version": "0.1",
    "x-build-id": "cxSDhD7Gbogo1hv1M"
  },
  "servers": [
    {
      "url": "https://api.apify.com/v2"
    }
  ],
  "paths": {
    "/acts/actor-forge~website-change-monitor/run-sync-get-dataset-items": {
      "post": {
        "operationId": "run-sync-get-dataset-items-actor-forge-website-change-monitor",
        "x-openai-isConsequential": false,
        "summary": "Executes an Actor, waits for its completion, and returns Actor's dataset items in response.",
        "tags": [
          "Run Actor"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/inputSchema"
              }
            }
          }
        },
        "parameters": [
          {
            "name": "token",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Enter your Apify token here"
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/acts/actor-forge~website-change-monitor/runs": {
      "post": {
        "operationId": "runs-sync-actor-forge-website-change-monitor",
        "x-openai-isConsequential": false,
        "summary": "Executes an Actor and returns information about the initiated run in response.",
        "tags": [
          "Run Actor"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/inputSchema"
              }
            }
          }
        },
        "parameters": [
          {
            "name": "token",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Enter your Apify token here"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/runsResponseSchema"
                }
              }
            }
          }
        }
      }
    },
    "/acts/actor-forge~website-change-monitor/run-sync": {
      "post": {
        "operationId": "run-sync-actor-forge-website-change-monitor",
        "x-openai-isConsequential": false,
        "summary": "Executes an Actor, waits for completion, and returns the OUTPUT from Key-value store in response.",
        "tags": [
          "Run Actor"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/inputSchema"
              }
            }
          }
        },
        "parameters": [
          {
            "name": "token",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Enter your Apify token here"
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "inputSchema": {
        "type": "object",
        "required": [
          "urls"
        ],
        "properties": {
          "urls": {
            "title": "URLs to monitor",
            "type": "array",
            "description": "Pages to watch, one per line (up to 5,000; duplicates removed). Each run compares every page with what it saw on the previous run of the same monitor. Bare domains like example.com/pricing work too.",
            "items": {
              "type": "string"
            }
          },
          "monitorName": {
            "title": "Monitor name",
            "maxLength": 48,
            "type": "string",
            "description": "Snapshots are saved in a named key-value store called change-monitor-<name>, which persists between runs. Use one name per schedule (e.g. competitor-prices, legal-pages) so separate watch lists keep separate baselines. Letters, numbers and dashes.",
            "default": "default"
          },
          "mode": {
            "title": "What to compare",
            "enum": [
              "text",
              "html",
              "selector"
            ],
            "type": "string",
            "description": "Text catches what a reader would notice and ignores layout changes. HTML also catches changes to links, images and markup (scripts and styles are excluded). Selector watches only the parts you name in CSS selectors, e.g. a price or a table.",
            "default": "text"
          },
          "cssSelector": {
            "title": "CSS selectors (selector mode)",
            "type": "array",
            "description": "For mode \"selector\": CSS selectors of the parts to watch, e.g. .price, #pricing-table, main h2. The text of every matching element is compared. A page where no selector matches is reported as an error (free) and its snapshot is kept.",
            "items": {
              "type": "string"
            }
          },
          "ignoreSelectors": {
            "title": "Ignore page parts (CSS selectors)",
            "type": "array",
            "description": "CSS selectors for parts of the page to remove before comparing, like ads, a related-posts box, a live chat widget or a visitor counter. Example: .ad, #related, .view-count.",
            "items": {
              "type": "string"
            }
          },
          "ignorePatterns": {
            "title": "Ignore text patterns (regex)",
            "type": "array",
            "description": "Regular expressions for text to strip before comparing (case-insensitive; ^ and $ match line starts/ends). Example: Visitors: \\d+ or ^Ad #\\d+.*$. Use /pattern/flags to choose flags yourself.",
            "items": {
              "type": "string"
            }
          },
          "ignoreDatesAndTimes": {
            "title": "Ignore dates and times",
            "type": "boolean",
            "description": "Strip clocks, dates and relative times (2026-10-19, 10:42 am, Oct 19 2026, 19/10/2026, 5 minutes ago) before comparing, so a page whose only change is its timestamp is not reported as changed. Prices and counts are not touched.",
            "default": true
          },
          "ignoreWhitespace": {
            "title": "Ignore whitespace",
            "type": "boolean",
            "description": "Collapse spaces and tabs and drop blank lines before comparing, so re-indented or re-flowed pages are not reported as changed.",
            "default": true
          },
          "minChangeChars": {
            "title": "Minimum change size (characters)",
            "minimum": 0,
            "maximum": 1000000,
            "type": "integer",
            "description": "Report a page as changed only when at least this many characters were added plus removed. Smaller changes are reported as unchanged and the old baseline is kept, so small edits add up until they cross the threshold. 0 = any change counts.",
            "default": 0
          },
          "includeDiff": {
            "title": "Include diff",
            "type": "boolean",
            "description": "Add a unified diff (lines starting with - were removed, + were added, with 2 lines of context) to each changed row, capped at about 20 KB. Added/removed line counts are always included.",
            "default": true
          },
          "outputOnlyChanges": {
            "title": "Only output changes and errors",
            "type": "boolean",
            "description": "Leave unchanged pages out of the dataset (they are still checked and their snapshot is refreshed). Baselines, changes and errors are always output.",
            "default": false
          },
          "webhookUrl": {
            "title": "Webhook URL",
            "type": "string",
            "description": "Optional. At the end of the run, POST a summary of the changes here. Slack and Discord incoming-webhook URLs get a readable message with the changed lines; any other URL (Zapier, Make, n8n, your server) gets JSON with counts, each change (URL, title, line counts, diff) and errors. Private and internal addresses are blocked."
          },
          "webhookOnlyOnChanges": {
            "title": "Send webhook only when something changed",
            "type": "boolean",
            "description": "On by default: no message on runs where nothing changed. Turn off to get a summary after every run (useful as a heartbeat).",
            "default": true
          },
          "respectRobotsTxt": {
            "title": "Respect robots.txt",
            "type": "boolean",
            "description": "Skip pages that the site's robots.txt disallows for crawlers (reported as a free error row). Turn off only for sites you own or are allowed to monitor.",
            "default": true
          },
          "maxUrls": {
            "title": "Max URLs per run",
            "minimum": 1,
            "maximum": 5000,
            "type": "integer",
            "description": "Safety cap on how many URLs one run checks; extra URLs are skipped.",
            "default": 500
          },
          "requestTimeoutSecs": {
            "title": "Page timeout (seconds)",
            "minimum": 5,
            "maximum": 120,
            "type": "integer",
            "description": "How long to wait for each page before reporting it as an error (free). Transient failures (timeouts, 429, 5xx) are retried twice.",
            "default": 30
          },
          "maxConcurrency": {
            "title": "Parallel pages",
            "minimum": 1,
            "maximum": 50,
            "type": "integer",
            "description": "How many pages to check at once. At most 2 pages per site are fetched at the same time.",
            "default": 10
          },
          "proxyConfiguration": {
            "title": "Proxy",
            "type": "object",
            "description": "Off by default (direct requests from Apify's datacenter). Use Apify Proxy (datacenter groups) for sites that rate-limit or block a single IP, or your own proxy URLs. Residential and SERP proxy groups are not supported: pages are fetched with plain HTTP, and bot-protected sites usually need a browser anyway.",
            "default": {
              "useApifyProxy": false
            }
          }
        }
      },
      "runsResponseSchema": {
        "type": "object",
        "properties": {
          "data": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string"
              },
              "actId": {
                "type": "string"
              },
              "userId": {
                "type": "string"
              },
              "startedAt": {
                "type": "string",
                "format": "date-time",
                "example": "2025-01-08T00:00:00.000Z"
              },
              "finishedAt": {
                "type": "string",
                "format": "date-time",
                "example": "2025-01-08T00:00:00.000Z"
              },
              "status": {
                "type": "string",
                "example": "READY"
              },
              "meta": {
                "type": "object",
                "properties": {
                  "origin": {
                    "type": "string",
                    "example": "API"
                  },
                  "userAgent": {
                    "type": "string"
                  }
                }
              },
              "stats": {
                "type": "object",
                "properties": {
                  "inputBodyLen": {
                    "type": "integer",
                    "example": 2000
                  },
                  "rebootCount": {
                    "type": "integer",
                    "example": 0
                  },
                  "restartCount": {
                    "type": "integer",
                    "example": 0
                  },
                  "resurrectCount": {
                    "type": "integer",
                    "example": 0
                  },
                  "computeUnits": {
                    "type": "integer",
                    "example": 0
                  }
                }
              },
              "options": {
                "type": "object",
                "properties": {
                  "build": {
                    "type": "string",
                    "example": "latest"
                  },
                  "timeoutSecs": {
                    "type": "integer",
                    "example": 300
                  },
                  "memoryMbytes": {
                    "type": "integer",
                    "example": 1024
                  },
                  "diskMbytes": {
                    "type": "integer",
                    "example": 2048
                  }
                }
              },
              "buildId": {
                "type": "string"
              },
              "defaultKeyValueStoreId": {
                "type": "string"
              },
              "defaultDatasetId": {
                "type": "string"
              },
              "defaultRequestQueueId": {
                "type": "string"
              },
              "buildNumber": {
                "type": "string",
                "example": "1.0.0"
              },
              "containerUrl": {
                "type": "string"
              },
              "usage": {
                "type": "object",
                "properties": {
                  "ACTOR_COMPUTE_UNITS": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATASET_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATASET_WRITES": {
                    "type": "integer",
                    "example": 0
                  },
                  "KEY_VALUE_STORE_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "KEY_VALUE_STORE_WRITES": {
                    "type": "integer",
                    "example": 1
                  },
                  "KEY_VALUE_STORE_LISTS": {
                    "type": "integer",
                    "example": 0
                  },
                  "REQUEST_QUEUE_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "REQUEST_QUEUE_WRITES": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATA_TRANSFER_INTERNAL_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATA_TRANSFER_EXTERNAL_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_SERPS": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_UNBLOCKER_UNITS": {
                    "type": "integer",
                    "example": 0
                  }
                }
              },
              "usageTotalUsd": {
                "type": "number",
                "example": 0.00005
              },
              "usageUsd": {
                "type": "object",
                "properties": {
                  "ACTOR_COMPUTE_UNITS": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATASET_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATASET_WRITES": {
                    "type": "integer",
                    "example": 0
                  },
                  "KEY_VALUE_STORE_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "KEY_VALUE_STORE_WRITES": {
                    "type": "number",
                    "example": 0.00005
                  },
                  "KEY_VALUE_STORE_LISTS": {
                    "type": "integer",
                    "example": 0
                  },
                  "REQUEST_QUEUE_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "REQUEST_QUEUE_WRITES": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATA_TRANSFER_INTERNAL_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATA_TRANSFER_EXTERNAL_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_SERPS": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_UNBLOCKER_UNITS": {
                    "type": "integer",
                    "example": 0
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}