# Change-Detect API — website content monitoring and change detection: noise-resistant content and structural fingerprints, human-readable diffs, batch URL snapshots — stateless building block for SEO, compliance and competitor-watch workflows

> Fetch a URL and return a noise-resistant fingerprint: content_hash (normalised main content, rotating-token-stripped), structural_hash (DOM skeleton), text_hash, title, meta_description, main_content (boilerplate-stripped), word_count, links[]. Store the returned snapshot/hash and feed it back to diff/monitor_check later.
> ReefAPI engine `change-detect` · 5 endpoints · clean JSON, no scraping or browsers to manage.

## How to call
- **Endpoint:** `POST https://api.reefapi.com/change-detect/v1/<action>` with a JSON body.
- **Auth:** header `x-api-key: <YOUR_REEFAPI_KEY>` — create one free (1,000 credits, no card): https://reefapi.com/signup
- **Response (every call):** `{ ok: boolean, data: ..., meta: { record_count, credits, ... }, error: { code, message } }` — branch on `ok`. Failed or blocked calls are free.
- **One key + one shared credit pool** across every ReefAPI API. Per-call credits are listed on each endpoint below.
- **Use it from an AI agent (MCP):** connect `https://api.reefapi.com/mcp` (remote streamable-http, `Authorization: Bearer <key>`) and your assistant can call these actions directly.

## Endpoints

### POST /change-detect/v1/snapshot — 1 credit
Fetch a URL and return a noise-resistant fingerprint: content_hash (normalised main content, rotating-token-stripped), structural_hash (DOM skeleton), text_hash, title, meta_description, main_content (boilerplate-stripped), word_count, links[]. Store the returned snapshot/hash and feed it back to diff/monitor_check later.

**Parameters:**
- `url` (string, required) — The page to fingerprint. Full URL (https://example.com/page) or bare domain (example.com → https:// assumed). Only http/https; private / internal / cloud-metadata targets are SSRF-blocked. Alias: target.
- `selector` (string, optional) — Scope detection to a region instead of the whole page. Accepts a FULL CSS selector ('div.price-box > span.amount', '.product:nth-child(2)', '[data-testid=total]') OR an XPath ('//div[@id="price"]', '//table//tr[2]/td[3]') — lxml-backed. If the selector matches nothing the whole page is used and `selector_matched:false` is reported.
- `follow_redirects` (boolean, optional, default true) — Follow redirects (http→https, apex→www) before fingerprinting the final URL (default true). Each hop is independently SSRF-validated.

**Returns:** content_hash, structural_hash, text_hash, title, meta_description, main_content, normalized_text, word_count, links[], link_count, content_type, status, fetched_at, byte_size, likely_js_app, final_url, selector_matched

**Example request body:**
```json
{
  "url": "https://example.com"
}
```

### POST /change-detect/v1/diff — 1 credit
Diff a page against a prior baseline (snapshot object OR content_hash OR raw text), or compare two live URLs (url + url2). Returns changed(bool), change_ratio(0..1), change_type(text|structural|both|none), added[]/removed[]/modified[] blocks, and a human summary — all on the NOISE-RESISTANT normalised content.

**Parameters:**
- `url` (string, required) — The page to fingerprint. Full URL (https://example.com/page) or bare domain (example.com → https:// assumed). Only http/https; private / internal / cloud-metadata targets are SSRF-blocked. Alias: target.
- `baseline` (object, optional) — The prior state to diff against: a snapshot object returned by a previous `snapshot` call, OR a 64-hex content_hash, OR raw prior text/HTML. (diff needs `baseline` OR `url2`.)
- `url2` (string, optional) — Second URL — when comparing two live pages (A vs B) instead of a page against a stored baseline. SSRF-guarded like `url`.
- `selector` (string, optional) — Scope detection to a region instead of the whole page. Accepts a FULL CSS selector ('div.price-box > span.amount', '.product:nth-child(2)', '[data-testid=total]') OR an XPath ('//div[@id="price"]', '//table//tr[2]/td[3]') — lxml-backed. If the selector matches nothing the whole page is used and `selector_matched:false` is reported.
- `mode` (enum, optional, default "text") — What to compare: text (main content, default) | structural (DOM skeleton) | full (both) | links (outbound link set). [one of: text, structural, full, links]
- `follow_redirects` (boolean, optional, default true) — Follow redirects (http→https, apex→www) before fingerprinting the final URL (default true). Each hop is independently SSRF-validated.

**Returns:** changed, change_ratio, change_type, added[], removed[], modified[], added_count, removed_count, modified_count, structural_changed, summary, current{content_hash,structural_hash,...}, baseline_kind

**Example request body:**
```json
{
  "url": "https://example.com",
  "url2": "https://www.iana.org/help/example-domains"
}
```

### POST /change-detect/v1/monitor_check — 1 credit
The stateless half of monitoring: re-fetch the URL and tell you FAST whether it changed since a known hash. Pass baseline_hash (+ optional selector/mode). Returns changed(bool) + current_hash + a short diff_summary — the cheap 'did it change?' path.

**Parameters:**
- `url` (string, required) — The page to fingerprint. Full URL (https://example.com/page) or bare domain (example.com → https:// assumed). Only http/https; private / internal / cloud-metadata targets are SSRF-blocked. Alias: target.
- `baseline_hash` (string, required) — The content_hash (or text_hash) from a prior snapshot. monitor_check re-fetches the URL and tells you, fast, whether it changed since this hash. 64-hex.
- `selector` (string, optional) — Scope detection to a region instead of the whole page. Accepts a FULL CSS selector ('div.price-box > span.amount', '.product:nth-child(2)', '[data-testid=total]') OR an XPath ('//div[@id="price"]', '//table//tr[2]/td[3]') — lxml-backed. If the selector matches nothing the whole page is used and `selector_matched:false` is reported.
- `mode` (enum, optional, default "text") — What to compare: text (main content, default) | structural (DOM skeleton) | full (both) | links (outbound link set). [one of: text, structural, full, links]
- `follow_redirects` (boolean, optional, default true) — Follow redirects (http→https, apex→www) before fingerprinting the final URL (default true). Each hop is independently SSRF-validated.

**Returns:** changed, current_hash, baseline_hash, mode, hash_kind, diff_summary, final_url, selector_matched, fetched_at

**Example request body:**
```json
{
  "url": "https://example.com",
  "baseline_hash": "0000000000000000000000000000000000000000000000000000000000000000"
}
```

### POST /change-detect/v1/extract — 1 credit
Main-content extraction only — boilerplate (nav/header/footer/ads/cookie/comments) stripped → clean text + structured blocks + links. Useful to feed clean diffs or to get just the article. No fingerprint comparison.

**Parameters:**
- `url` (string, required) — The page to fingerprint. Full URL (https://example.com/page) or bare domain (example.com → https:// assumed). Only http/https; private / internal / cloud-metadata targets are SSRF-blocked. Alias: target.
- `selector` (string, optional) — Scope detection to a region instead of the whole page. Accepts a FULL CSS selector ('div.price-box > span.amount', '.product:nth-child(2)', '[data-testid=total]') OR an XPath ('//div[@id="price"]', '//table//tr[2]/td[3]') — lxml-backed. If the selector matches nothing the whole page is used and `selector_matched:false` is reported.
- `follow_redirects` (boolean, optional, default true) — Follow redirects (http→https, apex→www) before fingerprinting the final URL (default true). Each hop is independently SSRF-validated.

**Returns:** main_content, blocks[], word_count, title, meta_description, links[], link_count, content_hash, content_type, final_url, selector_matched

**Example request body:**
```json
{
  "url": "https://example.com"
}
```

### POST /change-detect/v1/batch — 1 credit
Snapshot up to 20 URLs concurrently in one call (each independently SSRF-guarded, shared concurrency). Returns a compact fingerprint per URL.

**Parameters:**
- `urls` (array, required) — List of URLs to snapshot in one call (max 20); each is independently SSRF-guarded and fetched concurrently.
- `selector` (string, optional) — Scope detection to a region instead of the whole page. Accepts a FULL CSS selector ('div.price-box > span.amount', '.product:nth-child(2)', '[data-testid=total]') OR an XPath ('//div[@id="price"]', '//table//tr[2]/td[3]') — lxml-backed. If the selector matches nothing the whole page is used and `selector_matched:false` is reported.

**Returns:** results[] (url, ok, content_hash, structural_hash, text_hash, title, word_count, link_count, likely_js_app | error), count, ok_count

**Example request body:**
```json
{
  "urls": [
    "https://example.com",
    "https://www.iana.org/domains/reserved"
  ]
}
```

## More
- Try it live, no code: https://reefapi.com/playground?engine=change-detect
- Human docs page: https://reefapi.com/docs/change-detect
- Every ReefAPI API in one file (for your AI): https://reefapi.com/llms-full.txt
