API Reference
Integrate PageDrifter with your applications using our REST API.
Quick Start
- Get an API Key: Go to Settings → API Keys and create a new key
- Make requests: Include your key in the X-API-Key header
- 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:
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
| Header | Description |
|---|---|
| X-RateLimit-Limit | Maximum requests per minute (60) |
| X-RateLimit-Remaining | Requests remaining in current window |
| Retry-After | Seconds 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 responsePagination
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_monitorsEndpoints
/monitorsList all monitors for the authenticated user.
Query Parameters
| page | Page number (default: 1) |
| page_size | Results per page (default: 20, max: 100) |
| is_active | Filter 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
}/monitorsCreate a new monitor.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
| url | string | Yes | URL to monitor |
| name | string | Yes | Display name |
| monitor_type | string | No | text, html, or visual (default: text) |
| check_interval_minutes | integer | No | Check frequency (plan limits apply) |
| css_selector | string | No | CSS selector to target specific element |
| use_javascript | boolean | No | Enable JS rendering (Pro+ only) |
| change_threshold | integer | No | Min % 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"
}/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
}
}/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
}'/monitors/{id}/checksGet check history for a monitor.
Query Parameters
| limit | Number of results (default: 20, max: 100) |
| offset | Skip first N results (default: 0) |
| has_change | Filter 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
}/monitors/{id}/checkTrigger 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"
}/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.
/usageGet 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.
| Code | Description | Common Causes |
|---|---|---|
| 400 | Bad Request | Invalid JSON, missing required fields |
| 401 | Unauthorized | Missing or invalid API key |
| 403 | Forbidden | Quota exceeded, feature not in plan |
| 404 | Not Found | Monitor or resource doesn't exist |
| 429 | Too Many Requests | Rate limit exceeded |
| 500 | Server Error | Internal 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: