IndiePulse Docs / API Reference

API Reference

Read-only REST API for programmatic access to your monitoring data. Available on Hacker plans and above.


Authentication

All API requests require a Bearer token in the Authorization header. Generate an API key in your Settings → API page.

# Example request
curl https://indiepulse.dev/api/v1/monitors \
-H "Authorization: Bearer ip_live_your_key_here"

Rate Limits

Rate limits are applied per API key. The response includes X-RateLimit-Limit-Minute and X-RateLimit-Limit-Day headers.

PlanPer MinutePer DayMax Keys
Hacker605,0002
Maker12020,0005
Founder300100,00010

Monitor States

Monitors expose two state fields. Use monitor_state for dashboards and alerting logic. Use status only when you need the raw consensus result of the latest check round.

FieldPurposeValues
statusLatest consensus check resultUP, DOWN, DEGRADED
monitor_stateOperational state machinePENDING, UP, INVESTIGATING, DOWN, RECOVERING, PAUSED, MAINTENANCE

Why two fields? A monitor can have a latest check result of UP while its operational state is still RECOVERING (waiting for sustained recovery confirmation). The state machine prevents flapping between UP and DOWN on intermittent issues.

Uptime Calculation

The uptime_percent field returned by the monitor detail endpoint uses time-based calculation:

(total_period - confirmed_incident_duration) / total_period * 100

Only confirmed incidents (type INCIDENT) reduce uptime. Performance degradations (type PERFORMANCE) do not affect the uptime percentage. Returns null when no check data exists for the period.

Event Types

IndiePulse tracks two categories of events. The type field on events distinguishes them:

TypeMeaningAffects Uptime?
INCIDENTConfirmed downtime — the monitor is unreachable or returning errorsYes
PERFORMANCESustained slow responses exceeding the per-region thresholdNo
MAINTENANCEScheduled maintenance windowNo

The stats_24h object on monitor detail splits check results into down_count (errors affecting uptime) and degraded_count (slow responses that do not affect uptime).

Endpoints

GET/api/v1/monitors

List all monitors in your workspace

Parameters: page (default: 1), per_page (default: 50, max: 100)

Example Response:

{
  "data": [
    {
      "id": "uuid",
      "name": "My Website",
      "url": "https://example.com",
      "status": "UP",
      "monitor_state": "UP",
      "monitor_type": "http",
      "check_interval": 300,
      "heartbeat_token": null,
      "heartbeat_interval": null,
      "heartbeat_grace": null,
      "last_heartbeat_at": null,
      "created_at": "2026-01-15T10:00:00Z",
      "last_checked_at": "2026-02-15T12:00:00Z"
    },
    {
      "id": "uuid",
      "name": "Nightly Backup",
      "url": null,
      "status": "UP",
      "monitor_state": "UP",
      "monitor_type": "heartbeat",
      "check_interval": null,
      "heartbeat_token": "a1b2c3d4-...",
      "heartbeat_interval": 86400,
      "heartbeat_grace": 3600,
      "last_heartbeat_at": "2026-02-15T02:00:00Z",
      "created_at": "2026-02-01T10:00:00Z",
      "last_checked_at": null
    }
  ],
  "meta": { "page": 1, "per_page": 50, "total": 3, "total_pages": 1 }
}
GET/api/v1/monitors/:id

Get monitor details with 24-hour stats

Parameters: None

Example Response:

{
  "data": {
    "id": "uuid",
    "name": "My Website",
    "url": "https://example.com",
    "status": "UP",
    "monitor_state": "UP",
    "monitor_type": "http",
    "check_interval": 300,
    "stats_24h": {
      "total_checks": 1440,
      "down_count": 0,
      "degraded_count": 12,
      "uptime_percent": 100.0
    }
  }
}

// For heartbeat monitors, stats_24h returns:
// { "total_pings": 24, "successful": 24, "failed": 0 }
GET/api/v1/monitors/:id/checks

Check history (HTTP) or ping history (heartbeat) within retention window

Parameters: page (default: 1), per_page (default: 50, max: 100)

Example Response:

// HTTP monitors return checks:
{
  "data": [
    {
      "id": "uuid",
      "status": "UP",
      "response_time": 142,
      "status_code": 200,
      "checked_at": "2026-02-15T12:00:00Z",
      "region": "iad"
    }
  ],
  "meta": { "page": 1, "per_page": 50, "total": 1440, "total_pages": 29, "retention_days": 30 }
}

// Heartbeat monitors return pings:
// "data": [{ "id": "uuid", "pinged_at": "...", "source_ip": "...", "ping_type": "success", ... }]
// "meta": { ..., "data_type": "heartbeat_pings" }
GET/api/v1/monitors/:id/domain-health

Domain expiry + DNS record data for a monitor

Parameters: None

Example Response:

{
  "data": {
    "domain": {
      "monitored_domain": "example.com",
      "registrar": "Cloudflare, Inc.",
      "expiry_date": "2027-08-13T00:00:00Z",
      "days_remaining": 543,
      "transfer_locked": true,
      "check_timestamp": "2026-02-15T05:00:00Z"
    },
    "dns_baselines": [
      { "record_type": "A", "record_values": ["93.184.216.34"], "ttl": 300, "dnssec_valid": true }
    ],
    "dns_changes": [
      { "record_type": "MX", "change_type": "modified", "previous_values": ["..."], "new_values": ["..."] }
    ]
  }
}
GET/POST/api/v1/heartbeat/:token

Send a heartbeat ping. Use from cron jobs, scripts, or CI/CD pipelines.

Parameters: None (token is in the URL path)

Example Response:

{
  "ok": true,
  "monitor_id": "uuid",
  "name": "Nightly Backup"
}
POST/api/v1/heartbeat/:token/fail

Signal a failure. Triggers immediate incident creation.

Parameters: None

Example Response:

{
  "ok": true,
  "monitor_id": "uuid",
  "ping_type": "fail"
}
GET/api/v1/events

List events (incidents and performance events). Filterable by type and status.

Parameters: page, per_page, type (INCIDENT, PERFORMANCE, MAINTENANCE), status (e.g. "Resolved")

Example Response:

{
  "data": [
    {
      "id": "uuid",
      "type": "INCIDENT",
      "title": "API Gateway Down",
      "status": "Resolved",
      "severity": "critical",
      "created_at": "2026-02-14T08:00:00Z",
      "resolved_at": "2026-02-14T08:45:00Z",
      "monitor_id": "uuid"
    },
    {
      "id": "uuid",
      "type": "PERFORMANCE",
      "title": "api.example.com degraded performance",
      "status": "Resolved",
      "severity": "minor",
      "created_at": "2026-02-14T10:00:00Z",
      "resolved_at": "2026-02-14T10:15:00Z",
      "monitor_id": "uuid"
    }
  ],
  "meta": { "page": 1, "per_page": 50, "total": 12, "total_pages": 1 }
}
GET/api/v1/events/:id

Get event details. Incidents include their update timeline.

Parameters: None

Example Response:

{
  "data": {
    "id": "uuid",
    "type": "INCIDENT",
    "title": "API Gateway Down",
    "status": "Resolved",
    "severity": "critical",
    "created_at": "2026-02-14T08:00:00Z",
    "resolved_at": "2026-02-14T08:45:00Z",
    "monitor_id": "uuid",
    "updates": [
      { "id": "uuid", "status": "Investigating", "message": "Looking into it", "created_at": "..." },
      { "id": "uuid", "status": "Resolved", "message": "Fixed", "created_at": "..." }
    ]
  }
}
GET/api/v1/status

Workspace status summary with monitor, heartbeat, and domain health counts

Parameters: None

Example Response:

{
  "data": {
    "monitors": { "total": 10, "up": 9, "down": 1, "unknown": 0 },
    "heartbeats": { "total": 3, "healthy": 3, "missed": 0 },
    "domains": { "monitored": 5, "all_healthy": true, "expiring_soon": [] },
    "active_incidents": [
      {
        "id": "uuid",
        "type": "INCIDENT",
        "title": "API issues",
        "status": "Investigating",
        "severity": "major",
        "created_at": "..."
      }
    ],
    "active_performance_events": [
      {
        "id": "uuid",
        "type": "PERFORMANCE",
        "title": "api.example.com degraded performance",
        "status": "Investigating",
        "created_at": "..."
      }
    ],
    "overall_status": "degraded"
  }
}

Code Examples

curl

# Get workspace status
curl -s https://indiepulse.dev/api/v1/status \
  -H "Authorization: Bearer ip_live_your_key_here" | jq .

# Get a specific monitor with 24h stats
curl -s https://indiepulse.dev/api/v1/monitors/MONITOR_ID \
  -H "Authorization: Bearer ip_live_your_key_here" | jq '.data.stats_24h'

JavaScript (fetch)

const headers = { 'Authorization': 'Bearer ip_live_your_key_here' };

// List monitors with their operational state
const res = await fetch('https://indiepulse.dev/api/v1/monitors', { headers });
const { data: monitors } = await res.json();

for (const m of monitors) {
  console.log(`${m.name}: ${m.monitor_state}`);
  // "My Website: UP", "API Server: RECOVERING", etc.
}

// Check overall workspace health
const statusRes = await fetch('https://indiepulse.dev/api/v1/status', { headers });
const { data: status } = await statusRes.json();
console.log(`Status: ${status.overall_status}`);
// "operational", "performance_degraded", or "degraded"

Python (requests)

import requests

headers = {"Authorization": "Bearer ip_live_your_key_here"}

# Get workspace status
resp = requests.get("https://indiepulse.dev/api/v1/status", headers=headers)
data = resp.json()["data"]
print(f"Status: {data['overall_status']}")
print(f"Active incidents: {len(data['active_incidents'])}")
print(f"Performance events: {len(data['active_performance_events'])}")

# Get monitor detail with time-based uptime
resp = requests.get("https://indiepulse.dev/api/v1/monitors/MONITOR_ID", headers=headers)
monitor = resp.json()["data"]
stats = monitor["stats_24h"]
print(f"{monitor['name']}: {stats['uptime_percent']}% uptime")

Heartbeat Integration

Heartbeat monitors use a dead-man's switch pattern: your job pings IndiePulse on success, and we alert you if the ping doesn't arrive on time. Find your heartbeat URL in the monitor settings page.

crontab

# Ping after successful backup (every day at 2 AM)
0 2 * * * /usr/bin/backup.sh && curl -fsS https://indiepulse.dev/api/v1/heartbeat/YOUR_TOKEN > /dev/null

Node.js

// At the end of your scheduled job
await fetch('https://indiepulse.dev/api/v1/heartbeat/YOUR_TOKEN');

// Signal failure explicitly
await fetch('https://indiepulse.dev/api/v1/heartbeat/YOUR_TOKEN/fail', { method: 'POST' });

Python

import requests

# Success ping
requests.get("https://indiepulse.dev/api/v1/heartbeat/YOUR_TOKEN")

# Failure signal
requests.post("https://indiepulse.dev/api/v1/heartbeat/YOUR_TOKEN/fail")

Webhooks

Configure webhook endpoints in Settings → Notifications to receive real-time JSON payloads when events occur. Webhooks are sent as POST requests with a Content-Type: application/json header.

Payload Format

{
  "event_type": "incident.created",
  "timestamp": "2026-02-19T15:30:00.000Z",
  "workspace": {
    "id": "uuid",
    "name": "My Workspace"
  },
  "monitor": {
    "id": "uuid",
    "name": "API Server",
    "url": "https://api.example.com",
    "monitor_type": "http"
  },
  "event": {
    "id": "uuid",
    "title": "api.example.com is down",
    "status": "Investigating",
    "created_at": "2026-02-19T15:30:00.000Z",
    "dashboard_url": "https://app.indiepulse.io/monitors/uuid",
    "acknowledge_url": "https://app.indiepulse.io/ack?token=xxx&via=webhook"
  },
  "ai_insight": "High error rate detected across all regions..."
}

For resolved events, the event object also includes resolved_at and downtime_seconds. For heartbeat monitors, the monitor object includes a heartbeat sub-object with interval, grace, and last_ping_at fields.

Event Types

event_typeDescription
incident.createdA new downtime incident was detected
incident.resolvedAn incident has been resolved
incident.escalatedAn incident was escalated after a prolonged duration
performance.createdSustained performance degradation detected
performance.resolvedPerformance has returned to normal
digest.weeklyWeekly health digest summary
testTest event sent during webhook setup

Signature Verification

Every webhook request includes an X-IndiePulse-Signature header containing an HMAC-SHA256 signature of the raw request body, signed with your webhook signing secret. Always verify this signature to ensure the payload was sent by IndiePulse.

import crypto from 'crypto';

function verifyWebhook(body: string, signature: string, secret: string): boolean {
  const expected = crypto
    .createHmac('sha256', secret)
    .update(body)
    .digest('hex');
  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expected)
  );
}

// In your webhook handler:
const isValid = verifyWebhook(rawBody, req.headers['x-indiepulse-signature'], SIGNING_SECRET);
if (!isValid) return res.status(401).send('Invalid signature');

Error Responses

All errors return a JSON object with an error field.

StatusMeaningExample
401Invalid or missing API key{"error":"Invalid API key"}
403Key suspended or plan downgraded{"error":"API key is suspended..."}
404Resource not found{"error":"Monitor not found"}
429Rate limit exceeded{"error":"Rate limit exceeded..."}

Ready to start?

Generate your API key and start building integrations.

Go to API Settings →