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 URL
  • selector (string, required) – CSS selector (exactly one)
  • interval_seconds (int, required) – min 300, max 86400
  • extract (string, optional) – text (default) | html | attr
  • attr_name (string, required if extract=attr) – attribute name, e.g. href
  • normalize (bool, default true) – normalize whitespace
  • ignore_case (bool, default false) – case-insensitive compare
  • js_rendering (bool, default false) – render with headless browser before extraction
  • screenshot (bool, default false) – capture screenshot on change (stored as artifact)
  • webhook_url (string, optional) – where to POST change events
  • webhook_secret (string, optional) – used to sign webhook payload
  • active (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
  • 400 invalid_params
  • 401 unauthorized
  • 402 insufficient_credits
  • 403 plan_inactive
  • 404 not_found
  • 409 limit_reached
  • 429 rate_limited
  • 500 server_error