Scanner API
The scanner exposes a small JSON API. The WordPress plugin is the primary client, but anything that can send HTTP can drive it.
Authentication
Section titled “Authentication”Every endpoint except /api/health requires a bearer token matching the scanner’s WPDIFF_TOKEN:
Authorization: Bearer <token>The site parameter appearing throughout is the site’s origin URL. It namespaces storage, so one scanner can serve many sites.
GET /api/health
Section titled “GET /api/health”Unauthenticated liveness check.
{ "ok": true, "service": "wpdiff-scanner", "version": "0.2.0" }POST /api/scans
Section titled “POST /api/scans”Starts a capture run. The caller supplies the URLs; the scanner does no discovery of its own. One scan runs at a time, a second request returns 409.
{ "site": "https://example.com", "urls": ["https://example.com/", "https://example.com/about/"], "widths": [1440, 375], "hide_selectors": [".live-ticker"], "pause_libraries": ["swiper"]}Response:
{ "run_id": "20260705T101500Z", "status": "running", "url_count": 2 }Limits: 1 to 500 URLs per scan, up to 4 viewport widths, non http(s) URLs are dropped. Every URL is captured at every width, and diffs match pages per URL and width.
hide_selectors are hidden with visibility: hidden before capture (layout is preserved). pause_libraries names built-in pause recipes run in the page before capture: swiper, slick, owl, splide, flickity, aos, wow. HTML5 video is always paused. Unknown keys are ignored.
GET /api/runs?site=…
Section titled “GET /api/runs?site=…”Lists snapshots for a site, oldest first. Running and failed scans are included with their status.
[ { "id": "20260705T101500Z", "created": "2026-07-05T10:15:00+00:00", "page_count": 2, "status": "done" }]POST /api/diff
Section titled “POST /api/diff”Compares two runs. Results are cached per pair, and a debug request bypasses the normal cache.
{ "site": "https://example.com", "from_run": "20260628T101500Z", "to_run": "20260705T101500Z", "threshold": 0.95, "debug": false}Response (abridged):
{ "pair": "20260628T101500Z_vs_20260705T101500Z", "cached": false, "results": [ { "url": "https://example.com/about/", "changed": true, "baseline_boxes": [[120, 640, 480, 160]], "current_boxes": [[120, 640, 500, 180]], "baseline_annotated": "about-1a2b3c4d-baseline.png", "current_annotated": "about-1a2b3c4d-current.png" } ]}With "debug": true each compared page also carries a debug object with the pipeline stage image names, the dilation used, region counts, and per region grey scores and colour deltas.
GET /api/run-image?site=…&run=…&file=…
Section titled “GET /api/run-image?site=…&run=…&file=…”Serves a stored screenshot from a run, for example pages/about-1a2b3c4d.png.
GET /api/diff-image?site=…&pair=…&file=…
Section titled “GET /api/diff-image?site=…&pair=…&file=…”Serves a diff artifact: annotated screenshots and, after a debug diff, the pipeline stage images.
GET /api/region-image?site=…&pair=…&file=…&x=…&y=…&w=…&h=…&pad=60
Section titled “GET /api/region-image?site=…&pair=…&file=…&x=…&y=…&w=…&h=…&pad=60”Serves a cropped region of a diff artifact with pad pixels of context, used for element-level review.