API Reference

Integrate PageDrifter with your applications using our REST API.

Quick Start

  1. Get an API Key: Go to Settings → API Keys and create a new key
  2. Make requests: Include your key in the X-API-Key header
  3. Handle responses: All responses are JSON formatted

Try it now

curl -X GET "https://pagedrifter.com/api/v1/monitors" \
  -H "X-API-Key: dk_your_api_key_here"

Authentication

All API requests require authentication via an API key. Include your key in the X-API-Key header with every request.

API Key Format

API keys start with dk_ followed by a random string:

dk_a1b2c3d4e5f6g7h8i9j0...

Base URL

All API requests should be made to:

https://pagedrifter.com/api/v1

Rate Limiting

API requests are rate limited to ensure fair usage:

  • 60 requests per minute per API key
  • Rate limit headers are included in all responses
HeaderDescription
X-RateLimit-LimitMaximum requests per minute (60)
X-RateLimit-RemainingRequests remaining in current window
Retry-AfterSeconds until rate limit resets (when exceeded)

Handling Rate Limits

import time
import requests

def api_request(url, headers):
    response = requests.get(url, headers=headers)

    if response.status_code == 429:
        retry_after = int(response.headers.get("Retry-After", 60))
        print(f"Rate limited. Waiting {retry_after} seconds...")
        time.sleep(retry_after)
        return api_request(url, headers)  # Retry

    return response

Pagination

List endpoints return paginated results. Use page and page_size parameters to navigate.

Paginated Response Format

{
  "items": [...],      // Array of results
  "total": 150,        // Total number of items
  "page": 1,           // Current page number
  "page_size": 20,     // Items per page
  "pages": 8           // Total number of pages
}

Iterating Through All Pages

def get_all_monitors(api_key):
    all_monitors = []
    page = 1

    while True:
        response = requests.get(
            f"https://pagedrifter.com/api/v1/monitors?page={page}&page_size=100",
            headers={"X-API-Key": api_key}
        )
        data = response.json()
        all_monitors.extend(data["items"])

        if page >= data["pages"]:
            break
        page += 1

    return all_monitors

Endpoints

GET/monitors

List all monitors for the authenticated user.

Query Parameters

pagePage number (default: 1)
page_sizeResults per page (default: 20, max: 100)
is_activeFilter by active status (true/false)

Example Request

curl -X GET "https://pagedrifter.com/api/v1/monitors?page=1&page_size=10" \
  -H "X-API-Key: dk_your_api_key_here"

Example Response

{
  "items": [
    {
      "id": "mon_abc123",
      "name": "Competitor Pricing",
      "url": "https://example.com/pricing",
      "is_active": true,
      "monitor_type": "text",
      "check_interval_minutes": 60,
      "css_selector": "#price",
      "last_check_at": "2024-01-15T12:00:00Z",
      "last_change_at": "2024-01-14T08:30:00Z",
      "created_at": "2024-01-01T00:00:00Z"
    }
  ],
  "total": 25,
  "page": 1,
  "page_size": 10,
  "pages": 3
}
POST/monitors

Create a new monitor.

Request Body

FieldTypeRequiredDescription
urlstringYesURL to monitor
namestringYesDisplay name
monitor_typestringNotext, html, or visual (default: text)
check_interval_minutesintegerNoCheck frequency (plan limits apply)
css_selectorstringNoCSS selector to target specific element
use_javascriptbooleanNoEnable JS rendering (Pro+ only)
change_thresholdintegerNoMin % change to trigger alert (0-100)

Example Request

curl -X POST "https://pagedrifter.com/api/v1/monitors" \
  -H "X-API-Key: dk_your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com/pricing",
    "name": "Competitor Pricing",
    "monitor_type": "text",
    "check_interval_minutes": 60,
    "css_selector": "#price",
    "change_threshold": 5
  }'

Example Response (201 Created)

{
  "id": "mon_abc123",
  "name": "Competitor Pricing",
  "url": "https://example.com/pricing",
  "is_active": true,
  "monitor_type": "text",
  "check_interval_minutes": 60,
  "css_selector": "#price",
  "change_threshold": 5,
  "use_javascript": false,
  "last_check_at": null,
  "last_change_at": null,
  "created_at": "2024-01-15T12:00:00Z"
}
GET/monitors/{id}

Get details of a specific monitor including latest snapshot.

Example Response

{
  "id": "mon_abc123",
  "name": "Competitor Pricing",
  "url": "https://example.com/pricing",
  "is_active": true,
  "monitor_type": "text",
  "check_interval_minutes": 60,
  "css_selector": "#price",
  "last_check_at": "2024-01-15T12:00:00Z",
  "last_change_at": "2024-01-14T08:30:00Z",
  "latest_snapshot": {
    "content": "Premium Plan: $99/month",
    "captured_at": "2024-01-15T12:00:00Z"
  },
  "stats": {
    "total_checks": 720,
    "total_changes": 12,
    "uptime_percent": 99.8
  }
}
PATCH/monitors/{id}

Update a monitor. Only include fields you want to change.

Example Request

curl -X PATCH "https://pagedrifter.com/api/v1/monitors/mon_abc123" \
  -H "X-API-Key: dk_your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Updated Name",
    "check_interval_minutes": 30,
    "is_active": false
  }'
GET/monitors/{id}/checks

Get check history for a monitor.

Query Parameters

limitNumber of results (default: 20, max: 100)
offsetSkip first N results (default: 0)
has_changeFilter by change detected (true/false)

Example Response

{
  "checks": [
    {
      "id": "chk_xyz789",
      "checked_at": "2024-01-15T12:00:00Z",
      "status": "success",
      "has_change": true,
      "change_percent": 12.5,
      "response_time_ms": 450,
      "diff_url": "/monitors/mon_abc123/checks/chk_xyz789/diff"
    },
    {
      "id": "chk_xyz788",
      "checked_at": "2024-01-15T11:00:00Z",
      "status": "success",
      "has_change": false,
      "change_percent": 0,
      "response_time_ms": 380
    }
  ],
  "total": 720,
  "limit": 20,
  "offset": 0
}
POST/monitors/{id}/check

Trigger an immediate check for a monitor. This counts against your monthly check quota.

Example Request

curl -X POST "https://pagedrifter.com/api/v1/monitors/mon_abc123/check" \
  -H "X-API-Key: dk_your_api_key_here"

Example Response

{
  "id": "chk_xyz790",
  "checked_at": "2024-01-15T12:30:00Z",
  "status": "success",
  "has_change": true,
  "change_percent": 5.2,
  "message": "Change detected"
}
DELETE/monitors/{id}

Delete a monitor and all its check history. This action cannot be undone.

Example Request

curl -X DELETE "https://pagedrifter.com/api/v1/monitors/mon_abc123" \
  -H "X-API-Key: dk_your_api_key_here"

Example Response (204 No Content)

Successful deletion returns no body with status code 204.

GET/usage

Get current usage statistics for your account.

Example Response

{
  "plan": "pro",
  "billing_period_start": "2024-01-01T00:00:00Z",
  "billing_period_end": "2024-02-01T00:00:00Z",
  "monitors": {
    "used": 45,
    "limit": 100
  },
  "checks": {
    "used": 7250,
    "limit": 10000,
    "percent_used": 72.5
  },
  "credits": {
    "balance": 500,
    "used_this_period": 0
  }
}

Error Handling

The API uses standard HTTP status codes. Error responses include a JSON body with details.

CodeDescriptionCommon Causes
400Bad RequestInvalid JSON, missing required fields
401UnauthorizedMissing or invalid API key
403ForbiddenQuota exceeded, feature not in plan
404Not FoundMonitor or resource doesn't exist
429Too Many RequestsRate limit exceeded
500Server ErrorInternal error, please retry

Error Response Format

{
  "detail": "Monitor limit reached. Upgrade your plan for more monitors.",
  "error_code": "quota_exceeded",
  "status_code": 403
}

Handling Errors in Code

import requests

response = requests.post(url, headers=headers, json=data)

if response.status_code == 201:
    monitor = response.json()
    print(f"Created monitor: {monitor['id']}")
elif response.status_code == 400:
    error = response.json()
    print(f"Validation error: {error['detail']}")
elif response.status_code == 403:
    error = response.json()
    if error.get("error_code") == "quota_exceeded":
        print("Upgrade your plan to create more monitors")
elif response.status_code == 429:
    retry_after = response.headers.get("Retry-After", 60)
    print(f"Rate limited. Retry after {retry_after} seconds")
else:
    print(f"Unexpected error: {response.status_code}")

Best Practices

Use Pagination

Always paginate through results instead of fetching everything at once. Use page_size=100 for efficiency.

Handle Rate Limits

Implement exponential backoff when rate limited. Check the Retry-After header.

Cache Responses

Cache monitor lists and usage data locally. Avoid repeated API calls for unchanged data.

Use Webhooks

Instead of polling for changes, configure webhooks to receive real-time notifications.

Interactive Documentation

For a complete API reference with interactive testing: