Changehook API Docs
Monitor websites for changes via API.
Base URL
https://api.changehook.io/v1
Authentication
All API requests require your API key:
Authorization: Bearer ch_live_xxxxxxxxxxxxxxxxx
Your API key is sent by email after your first successful payment. Keep it secret.
Account / Credits
Check your plan status and remaining credits.
GET /v1/account
cURL
curl -s https://api.changehook.io/v1/account \
-H "Authorization: Bearer ch_live_YOUR_KEY"
Response 200
{
"email": "name@company.com",
"plan": "pro",
"plan_active": true,
"credits": {
"monthly_remaining": 8421,
"prepaid_remaining": 20000,
"total_remaining": 28421
}
}
Monitors
Create a monitor
POST /v1/monitors
Body
{
"url": "https://shop.example.com/product/123",
"selector": ".price",
"interval_seconds": 3600,
"extract": "text",
"attr_name": null,
"normalize": true,
"ignore_case": false,
"js_rendering": false,
"screenshot": false,
"webhook_url": "https://client.example.com/changehook",
"webhook_secret": "optional-secret",
"active": true
}
Fields
url(string, required) – target URLselector(string, required) – CSS selector (exactly one)interval_seconds(int, required) – min 300, max 86400extract(string, optional) –text(default) |html|attrattr_name(string, required if extract=attr) – attribute name, e.g.hrefnormalize(bool, default true) – normalize whitespaceignore_case(bool, default false) – case-insensitive comparejs_rendering(bool, default false) – render with headless browser before extractionscreenshot(bool, default false) – capture screenshot on change (stored as artifact)webhook_url(string, optional) – where to POST change eventswebhook_secret(string, optional) – used to sign webhook payloadactive(bool, default true)
cURL
curl -s https://api.changehook.io/v1/monitors \
-H "Authorization: Bearer ch_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"url":"https://shop.example.com/product/123",
"selector":".price",
"interval_seconds":3600,
"js_rendering":false,
"screenshot":false,
"webhook_url":"https://client.example.com/changehook",
"active":true
}'
Response 201
{
"id": "b7c6e5c2-aaaa-bbbb-cccc-111122223333",
"status": "active",
"next_run_at": "2025-12-27T12:00:00Z"
}
List monitors
GET /v1/monitors?limit=50&cursor=...
Response 200
{
"items": [
{
"id": "b7c6e5c2-aaaa-bbbb-cccc-111122223333",
"url": "https://shop.example.com/product/123",
"selector": ".price",
"interval_seconds": 3600,
"active": true,
"last_checked_at": "2025-12-27T10:00:00Z",
"last_changed_at": "2025-12-27T09:20:00Z"
}
],
"next_cursor": null
}
Get monitor
GET /v1/monitors/{id}
Response 200
{
"id": "b7c6e5c2-aaaa-bbbb-cccc-111122223333",
"url": "https://shop.example.com/product/123",
"selector": ".price",
"interval_seconds": 3600,
"extract": "text",
"normalize": true,
"ignore_case": false,
"js_rendering": false,
"screenshot": false,
"webhook_url": "https://client.example.com/changehook",
"active": true,
"last_value": "€19.99",
"last_checked_at": "2025-12-27T10:00:00Z",
"last_changed_at": "2025-12-27T09:20:00Z",
"fail_count": 0,
"last_error": null
}
Update monitor
PATCH /v1/monitors/{id}
Body (all optional)
{
"selector": ".price",
"interval_seconds": 900,
"js_rendering": true,
"screenshot": true,
"active": true
}
Response 200
{ "ok": true }
Delete monitor
DELETE /v1/monitors/{id}
Response 200
{ "ok": true }
Changes
Retrieve only actual changes (no noise).
GET /v1/monitors/{id}/changes?limit=50&cursor=...&since=2025-12-01T00:00:00Z
Response 200
{
"items": [
{
"id": 123,
"changed_at": "2025-12-27T09:20:00Z",
"old_value": "€19.99",
"new_value": "€17.99",
"artifact": {
"screenshot_url": null
}
}
],
"next_cursor": null
}
Test Run (Run now)
Run a check immediately and return the extracted value. Costs credits.
POST /v1/monitors/{id}/test
Body (optional)
{
"screenshot": false
}
Response 200
{
"status": "ok",
"http_status": 200,
"value": "€17.99",
"changed": false,
"checked_at": "2025-12-27T12:34:56Z",
"credits_used": 1
}
Webhooks
If webhook_url is set and a change occurs, we POST an event to your endpoint.
Webhook payload
{
"monitor_id": "b7c6e5c2-aaaa-bbbb-cccc-111122223333",
"url": "https://shop.example.com/product/123",
"selector": ".price",
"changed_at": "2025-12-27T09:20:00Z",
"old_value": "€19.99",
"new_value": "€17.99",
"artifact": {
"screenshot_url": null
}
}
Signature (optional)
If you set webhook_secret, requests include:
X-Changehook-Signature: sha256=...
Credits
Monthly credits reset each billing period. Top-up credits never expire. API access requires an active subscription.
Credit usage (MVP)
- Standard check: 1 credit
js_rendering=true: +2 credits (total 3)screenshot=true: +2 credits (total 3)js_rendering=true+screenshot=true: 5 credits total
(We can tune these numbers later, but the API stays the same.)
Errors
All errors use the same JSON format:
{
"error": "invalid_params",
"message": "selector is required"
}
Common error codes
400invalid_params401unauthorized402insufficient_credits403plan_inactive404not_found409limit_reached429rate_limited500server_error