Monitors API endpoint
Creates a monitor that periodically checks a page or sitemap for changes and can notify a webhook. Free to call -- only completed check runs consume credits, at 1 credit per completed run regardless of target type or whether a change was detected. `target_type` defaults to "page" (exact-fingerprint diff of the page). "sitemap" watches the sitemap at `url` for added/removed entries instead, honoring `sitemap.include_patterns`/`exclude_patterns` (shell-style globs matched against each URL's path) and `sitemap.max_urls` (default 5000, hard cap 10000).
/monitorsRequires a dashboard JWT (Authorization: Token <jwt>) or an x-api-key.
Free to call. Only completed check runs consume credits — 1 credit per completed run, regardless of target type or whether the run actually found a change. Failed and credit-skipped runs are always free.
Request body:
url (required): the page URL to watch, or the sitemap URL when target_type is sitemap.name: optional label.target_type: page (default) or sitemap.
page diffs an exact SHA-256 fingerprint of the scraped page's markdown.sitemap diffs the set of <loc> entries in the sitemap (a <urlset>, or a one-level
<sitemapindex> whose child sitemaps are each fetched and merged).sitemap: only used when target_type is sitemap.
include_patterns / exclude_patterns: shell-style glob patterns (e.g. /products/*)
matched against each URL's path (and the full URL). include_patterns is an allowlist —
when set, only matching URLs are tracked. exclude_patterns always removes matches.max_urls: cap on tracked URLs. Defaults to 5000, hard cap 10000.cadence_minutes: how often to check, 5–10080 (7 days). Defaults to 60.enabled: defaults to true.notification.webhook_url: an https:// URL that resolves to a public address (no
private/loopback/link-local IPs).notification.webhook_secret: used to sign delivered payloads.notification.events: which events to send — change.detected, run.completed, or both.
Defaults to ["change.detected"] when omitted.Webhook payloads are signed via X-Crawlora-Signature: t=<unix>,v1=<hmac-hex>, an HMAC-SHA256
digest of "{t}.{rawBody}" using webhook_secret. Verify by recomputing the digest, comparing
it in constant time, and rejecting timestamps older than a few minutes to reject stale/replayed
deliveries. Delivery retries on 5xx/429/network errors (capped attempts, honoring a numeric
Retry-After); any other 4xx fails fast without retry.
| Parameter | Type | Required | Default | Description | Example |
|---|---|---|---|---|---|
| Authorization (header) | string | No | Dashboard JWT in the form `Token <jwt>` (use this or x-api-key) | ||
| x-api-key (header) | string | No | API key (alternative to a dashboard JWT) | ||
| request (body) | object | Yes | Monitor definition |
curl -X POST "https://api.crawlora.net/api/v1/monitors" \
-H "x-api-key: $CRAWLORA_API_KEY" \
-H "Content-Type: application/json" \
-d '{"cadence_minutes":60,"enabled":true,"name":"Pricing page","notification":{"events":["change.detected","run.completed"],"webhook_secret":"whsec_...","webhook_url":"https://example.com/webhooks/crawlora"},"sitemap":{"exclude_patterns":["/blog/*"],"include_patterns":["/products/*"],"max_urls":5000},"target_type":"page","url":"https://example.com/pricing"}'{
"cadence_minutes": 60,
"enabled": true,
"name": "Pricing page",
"notification": {
"events": [
"change.detected",
"run.completed"
],
"webhook_secret": "whsec_...",
"webhook_url": "https://example.com/webhooks/crawlora"
},
"sitemap": {
"exclude_patterns": [
"/blog/*"
],
"include_patterns": [
"/products/*"
],
"max_urls": 5000
},
"target_type": "page",
"url": "https://example.com/pricing"
}{
"code": 200,
"msg": "OK",
"data": {
"id": "66d2f0b1c2a4e5f6a7b8c9d0",
"url": "https://example.com/pricing",
"name": "Pricing page",
"target_type": "page",
"cadence_minutes": 60,
"enabled": true,
"notification": {
"webhook_url": "https://example.com/webhooks/crawlora",
"events": [
"change.detected"
]
},
"next_check_at": "2026-08-22T11:00:00Z",
"failures": 0,
"created_at": "2026-08-22T10:00:00Z",
"updated_at": "2026-08-22T10:00:00Z"
}
}Request schema
#/definitions/webmonitor.createMonitorBodyDoc
| Field | Type | Required | Enum | Bounds | Example | Description |
|---|---|---|---|---|---|---|
| cadence_minutes | integer | No | 60 | |||
| enabled | boolean | No | true | |||
| name | string | No | Pricing page | |||
| notification | webmonitor.notificationBodyDoc | No | ||||
| notification.events | array | No | change.detected,run.completed | |||
| notification.webhook_secret | string | No | whsec_... | |||
| notification.webhook_url | string | No | https://example.com/webhooks/crawlora | |||
| sitemap | webmonitor.sitemapTargetBodyDoc | No | ||||
| sitemap.exclude_patterns | array | No | /blog/* | |||
| sitemap.include_patterns | array | No | /products/* | |||
| sitemap.max_urls | integer | No | 5000 | |||
| target_type | string | No | page, sitemap | page | ||
| url | string | No | https://example.com/pricing |
Response schema
#/definitions/webmonitor.monitorResponseDoc
| Field | Type | Required | Enum | Bounds | Example | Description |
|---|---|---|---|---|---|---|
| code | integer | No | 200 | |||
| data | webmonitor.monitorDoc | No | ||||
| data.cadence_minutes | integer | No | 60 | |||
| data.created_at | string | No | 2026-08-01T00:00:00Z | |||
| data.enabled | boolean | No | true | |||
| data.failures | integer | No | 0 | |||
| data.id | string | No | 66d2f0b1c2a4e5f6a7b8c9d0 | |||
| data.last_changed_at | string | No | 2026-08-20T09:00:00Z | |||
| data.last_checked_at | string | No | 2026-08-22T10:00:00Z | |||
| data.last_error | string | No | ||||
| data.name | string | No | Pricing page | |||
| data.next_check_at | string | No | 2026-08-22T11:00:00Z | |||
| data.notification | webmonitor.notificationDoc | No | ||||
| data.notification.events | array | No | change.detected,run.completed | |||
| data.notification.webhook_url | string | No | https://example.com/webhooks/crawlora | |||
| data.sitemap | webmonitor.sitemapTargetDoc | No | ||||
| data.sitemap.exclude_patterns | array | No | /blog/* | |||
| data.sitemap.include_patterns | array | No | /products/* | |||
| data.sitemap.max_urls | integer | No | 5000 | |||
| data.target_type | string | No | page, sitemap | page | ||
| data.updated_at | string | No | 2026-08-22T10:00:00Z | |||
| data.url | string | No | https://example.com/pricing | |||
| msg | string | No | OK |
| Status | Description | Schema |
|---|---|---|
| 400 | Invalid parameters | #/definitions/app.Response |
| 401 | Authentication required | #/definitions/app.Response |
| 500 | Internal server error | #/definitions/app.Response |
Use environment variables for secrets and keep Crawlora API keys server-side.
curl -X POST "https://api.crawlora.net/api/v1/monitors" \
-H "x-api-key: $CRAWLORA_API_KEY" \
-H "Content-Type: application/json" \
-d '{"cadence_minutes":60,"enabled":true,"name":"Pricing page","notification":{"events":["change.detected","run.completed"],"webhook_secret":"whsec_...","webhook_url":"https://example.com/webhooks/crawlora"},"sitemap":{"exclude_patterns":["/blog/*"],"include_patterns":["/products/*"],"max_urls":5000},"target_type":"page","url":"https://example.com/pricing"}'