API

API Reference Guide

Full reference for the StatusApp REST API — authentication, endpoints, and code examples.

StatusApp Team

StatusApp API Reference Guide

Overview

The StatusApp API allows you to programmatically manage monitors, retrieve incidents, access analytics, configure webhooks, and automate your entire monitoring workflow. The API is RESTful, returns JSON responses, and uses API key authentication.

Base URL: https://ops.statusapp.io/api/v1


Authentication

API Keys

All API requests require authentication using an API key. Generate API keys from your dashboard at Profile > API tab.

API keys are stored as SHA256 hashes for security. When you create an API key, you'll see it once - make sure to copy and store it securely.

Authentication Methods

Option 1: X-API-Key Header (Recommended)

curl -H "X-API-Key: your_api_key_here" \
  https://ops.statusapp.io/api/v1/monitors

Option 2: Authorization Bearer Token

curl -H "Authorization: Bearer your_api_key_here" \
  https://ops.statusapp.io/api/v1/monitors

API Key Properties

When creating an API key, you can configure:

PropertyDescription
NameA descriptive name for the key
PermissionsComma-separated: read, write, delete
ExpirationOptional expiry date (keys can be permanent)

API Key Security Best Practices

  • Never commit API keys to source control
  • Use environment variables to store keys
  • Rotate keys regularly using expiration dates
  • Revoke unused keys immediately from dashboard
  • Use minimal permissions - only grant what's needed
  • Monitor usage - check lastUsedAt to detect unauthorized access

Rate Limits

Rate limits are based on your subscription plan:

PlanRequests per Hour
Starter1,000
Professional5,000
Business10,000
Enterprise100,000

Rate Limit Headers

Every API response includes rate limit information:

X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 856
X-RateLimit-Reset: 1737201600
HeaderDescription
X-RateLimit-LimitMaximum requests allowed per hour
X-RateLimit-RemainingRequests remaining in current window
X-RateLimit-ResetUnix timestamp when limit resets

When rate limit is exceeded, you'll receive a 429 Too Many Requests response.


CORS Support

The API supports Cross-Origin Resource Sharing (CORS) for browser-based applications:

Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
Access-Control-Allow-Headers: Content-Type, Authorization, X-API-Key
Access-Control-Max-Age: 86400

Monitors API

List All Monitors

Retrieve all monitors with optional filtering and pagination.

GET /api/v1/monitors

Query Parameters:

ParameterTypeDescription
pageintegerPage number (default: 1)
limitintegerResults per page (max: 100, default: 50)
statusstringFilter by status: UP, DOWN, DEGRADED
typestringFilter by monitor type
searchstringSearch by name or URL
sortstringSort field (default: createdAt)
orderstringSort order: asc or desc (default: desc)

Response:

{
  "monitors": [
    {
      "id": "clxyz123...",
      "name": "Production API",
      "url": "https://api.example.com",
      "type": "API",
      "interval": 300,
      "status": "UP",
      "isPaused": false,
      "isActive": true,
      "createdAt": "2025-01-01T00:00:00Z",
      "updatedAt": "2025-01-21T10:30:00Z",
      "checks": [
        {
          "id": "check_abc...",
          "status": "UP",
          "responseTime": 245,
          "statusCode": 200,
          "checkedAt": "2025-01-21T10:30:00Z",
          "workerRegion": {
            "country": "United States",
            "countryCode": "US",
            "city": "Virginia"
          }
        }
      ],
      "_count": {
        "checks": 1440,
        "incidents": 0
      }
    }
  ],
  "pagination": {
    "total": 25,
    "page": 1,
    "limit": 50,
    "totalPages": 1
  }
}

Get Monitor by ID

GET /api/v1/monitors/{monitorId}

Response: Full monitor object with all configuration details.

Create Monitor

POST /api/v1/monitors
Content-Type: application/json

Request Body:

{
  "name": "My API Monitor",
  "url": "https://api.example.com/health",
  "type": "API",
  "interval": 300,
  "timeout": 30,
  "expectedStatusCode": 200,
  "httpMethod": "GET",
  "enabledWorkerRegionIds": ["region_us_east", "region_eu_west"],
  "regressionEnabled": true,
  "regressionThreshold": 50,
  "notifications": [
    {
      "type": "EMAIL",
      "enabled": true,
      "config": { "emails": ["alerts@example.com"] }
    }
  ]
}

Monitor Types:

TypeDescriptionRequired Fields
WEBSITEWebsite availability monitoringurl
HTTPHTTP endpoint monitoringurl
HTTPSHTTPS endpoint monitoringurl
APIREST API endpoint monitoringurl, optionally httpMethod, requestHeaders, requestBody
CRONHeartbeat/scheduled job monitoringurl (auto-generated heartbeatUrl)
PINGICMP ping monitoringurl (hostname/IP)
PORTTCP/UDP port monitoringurl, port, protocol
DNSDNS record monitoringurl, dnsRecord, optionally expectedIp
SSL_CERTSSL certificate expiry monitoringurl (HTTPS URL)
DOMAINDomain expiry & health monitoringurl (domain name)
GRAPHQLGraphQL API monitoringurl, graphqlQuery
SERVERServer resource monitoringRequires agent installation

Full Schema:

FieldTypeDescription
namestringRequired. Monitor name
urlstringRequired. URL, hostname, or IP to monitor
typestringMonitor type (default: WEBSITE)
intervalintegerCheck interval in seconds (min: 60, default: 300)
timeoutintegerTimeout in seconds (5-300, default: 30)
portintegerPort number for PORT monitors (1-65535)
protocolstringProtocol for PORT monitors: TCP or UDP
expectedStatusCodeintegerExpected HTTP status code (100-599)
httpMethodstringHTTP method: GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS
requestHeadersstringJSON string of custom headers
requestBodystringRequest body for POST/PUT requests
dnsRecordstringDNS record type: A, AAAA, CNAME, MX, TXT
expectedIpstringExpected IP for DNS validation
gracePeriodintegerGrace period for CRON monitors (60-86400 seconds)
sslCertExpiryDaysintegerDays before SSL expiry to alert (1-90)
enabledWorkerRegionIdsarrayWorker region IDs for multi-region monitoring
regressionEnabledbooleanEnable performance regression detection
regressionThresholdintegerRegression threshold percentage (25-200)
regressionPeriodintegerRegression calculation period in days (1-30)
performanceTimingsbooleanCapture detailed performance timings
apiAuthConfigobjectAPI authentication configuration (see below)
notificationsarrayNotification channel configurations

API Authentication Config (apiAuthConfig):

{
  "type": "bearer",
  "token": "your-api-token"
}
Auth TypeRequired Fields
noneNo additional fields
bearertoken
basicusername, password
api_keyapiKey, apiKeyHeader or apiKeyName, apiKeyLocation (header/query)
oauth2oauth2TokenUrl, oauth2ClientId, oauth2ClientSecret, oauth2Scope, oauth2GrantType
jwtjwtToken, optionally jwtHeader, jwtPrefix

Domain Monitoring Fields:

FieldTypeDefaultDescription
domainExpiryDaysinteger30Days before domain expiry to alert
domainMonitorDnsbooleantrueMonitor DNS record changes
domainMonitorBlacklistbooleantrueCheck domain against blacklists
domainMonitorWhoisbooleantrueMonitor WHOIS data changes

Response:

{
  "id": "clxyz789...",
  "name": "My API Monitor",
  "url": "https://api.example.com/health",
  "type": "API",
  "status": "UP",
  "createdAt": "2025-01-21T10:30:00Z",
  "heartbeatUrl": null,
  "monitorNotifications": [...]
}

Update Monitor

PUT /api/v1/monitors/{monitorId}
Content-Type: application/json

All fields are optional. Only provided fields will be updated.

Delete Monitor

DELETE /api/v1/monitors/{monitorId}

Response:

{
  "success": true,
  "message": "Monitor deleted successfully"
}

Pause/Resume Monitor

POST /api/v1/monitors/{monitorId}/pause

Toggles the monitor's paused state. When paused, no checks are performed.

Note: This endpoint toggles the current state - no request body is needed. Call it once to pause, call again to resume.

Response:

{
  "success": true,
  "isPaused": true,
  "message": "Monitor paused"
}

Or when resuming:

{
  "success": true,
  "isPaused": false,
  "message": "Monitor resumed"
}

Get Regression Settings

GET /api/v1/monitors/{monitorId}/regression

Response:

{
  "regressionEnabled": true,
  "regressionThreshold": 50,
  "regressionPeriod": 7,
  "baselineResponseTime": 250,
  "baselineCalculatedAt": "2025-01-20T00:00:00Z"
}

Update Regression Settings

PUT /api/v1/monitors/{monitorId}/regression
Content-Type: application/json

Request Body:

{
  "regressionEnabled": true,
  "regressionThreshold": 75,
  "regressionPeriod": 14
}

Heartbeat API

Heartbeat monitors (CRON type) use a unique URL to receive pings from your scheduled jobs. The heartbeat URL is automatically generated when you create a CRON monitor.

Rate Limit: Maximum 5 pings per minute per monitor.

Send Success Ping

Report successful job completion.

GET /api/v1/heartbeat/{heartbeatId}
POST /api/v1/heartbeat/{heartbeatId}
HEAD /api/v1/heartbeat/{heartbeatId}

All HTTP methods work identically. Use whichever is most convenient for your environment.

Optional Body (POST only, max 10KB):

{
  "message": "Backup completed: 1.2GB processed"
}

Response:

{
  "message": "OK"
}

Report Job Start

Indicate a long-running job has started.

GET /api/v1/heartbeat/{heartbeatId}/start
POST /api/v1/heartbeat/{heartbeatId}/start

Response:

{
  "message": "OK"
}

Report Job Failure

Explicitly report when a job fails.

GET /api/v1/heartbeat/{heartbeatId}/fail
POST /api/v1/heartbeat/{heartbeatId}/fail

Optional Body:

{
  "error": "Database connection timeout after 30s"
}

Log Message

Record a log message without changing monitor status.

POST /api/v1/heartbeat/{heartbeatId}/log
Content-Type: application/json

Request Body:

{
  "message": "Processing batch 5 of 10..."
}

Heartbeat Integration Examples

Cron Job (Bash):

#!/bin/bash
# Notify start
curl -s https://ops.statusapp.io/api/v1/heartbeat/abc123/start

# Run backup
if /usr/local/bin/backup.sh; then
  # Success
  curl -s https://ops.statusapp.io/api/v1/heartbeat/abc123
else
  # Failure
  curl -s -X POST https://ops.statusapp.io/api/v1/heartbeat/abc123/fail \
    -d '{"error": "Backup script failed"}'
fi

Node.js Scheduled Task:

const fetch = require('node-fetch');
const HEARTBEAT_URL = 'https://ops.statusapp.io/api/v1/heartbeat/abc123';

async function runScheduledTask() {
  // Signal start
  await fetch(`${HEARTBEAT_URL}/start`);

  try {
    await performTask();
    // Signal success
    await fetch(HEARTBEAT_URL);
  } catch (error) {
    // Signal failure
    await fetch(`${HEARTBEAT_URL}/fail`, {
      method: 'POST',
      body: JSON.stringify({ error: error.message })
    });
  }
}

Python Script:

import requests

HEARTBEAT_URL = "https://ops.statusapp.io/api/v1/heartbeat/abc123"

def run_job():
    # Signal start
    requests.get(f"{HEARTBEAT_URL}/start")

    try:
        do_work()
        # Signal success
        requests.get(HEARTBEAT_URL)
    except Exception as e:
        # Signal failure
        requests.post(f"{HEARTBEAT_URL}/fail", json={"error": str(e)})

Incidents API

List Incidents

GET /api/v1/incidents

Query Parameters:

ParameterTypeDescription
pageintegerPage number (default: 1)
limitintegerResults per page (max: 100, default: 50)
monitorIdstringFilter by monitor ID
statusstringFilter by status: open or resolved
sortstringSort field (default: startedAt)
orderstringSort order: asc or desc

Response:

{
  "incidents": [
    {
      "id": "inc_xyz789",
      "incidentNumber": "INC-2025-0042",
      "monitorId": "mon_abc123",
      "startedAt": "2025-01-21T10:00:00Z",
      "resolvedAt": "2025-01-21T10:15:00Z",
      "isResolved": true,
      "severity": "high",
      "status": "resolved",
      "rootCause": "Database connection timeout",
      "rootCauseCategory": "DATABASE_ISSUE",
      "affectedRegions": ["us-east-1", "us-west-2"],
      "checksFailed": 3,
      "checksTotal": 3,
      "mttr": 900
    }
  ],
  "pagination": {
    "total": 42,
    "page": 1,
    "limit": 50,
    "totalPages": 1
  }
}

Get Incident by ID

GET /api/v1/incidents/{incidentId}

Returns full incident details including:

  • Status update history
  • Affected regions
  • Root cause analysis
  • MTTR (Mean Time To Recovery)
  • Associated check results

Root Cause Categories:

  • DNS_FAILURE
  • NETWORK_TIMEOUT
  • SERVER_ERROR
  • APPLICATION_ERROR
  • DATABASE_ISSUE
  • SSL_CERTIFICATE
  • CONFIGURATION_ERROR
  • DEPLOYMENT_ISSUE
  • THIRD_PARTY_SERVICE
  • INFRASTRUCTURE
  • DDOS_ATTACK
  • MAINTENANCE
  • UNKNOWN

Status Pages API

List Status Pages

GET /api/v1/status-pages

Response:

{
  "statusPages": [
    {
      "id": "sp_abc123",
      "name": "Public Status",
      "slug": "status",
      "description": "Current system status",
      "customDomain": "status.example.com",
      "isPublic": true,
      "brandColor": "#22c55e",
      "createdAt": "2025-01-01T00:00:00Z"
    }
  ]
}

Get Status Page

GET /api/v1/status-pages/{statusPageId}

Returns full status page configuration including:

  • Monitor groups
  • Associated monitors
  • Display settings
  • Custom branding

Create Status Page

POST /api/v1/status-pages
Content-Type: application/json

Request Body:

{
  "name": "API Status",
  "slug": "api-status",
  "description": "Real-time API service status",
  "isPublic": true,
  "brandColor": "#3b82f6",
  "logoUrl": "https://example.com/logo.png"
}
FieldTypeDescription
namestringRequired. Status page name
slugstringRequired. URL slug (must be unique)
descriptionstringPage description
isPublicbooleanWhether page is publicly accessible
brandColorstringHex color for branding
logoUrlstringURL to logo image
faviconUrlstringURL to favicon
customDomainstringCustom domain (requires DNS setup)

Update Status Page

PUT /api/v1/status-pages/{statusPageId}
Content-Type: application/json

Delete Status Page

DELETE /api/v1/status-pages/{statusPageId}

Notification Channels API

List Notification Channels

GET /api/v1/notifications

Response:

{
  "channels": [
    {
      "id": "nc_abc123",
      "name": "Engineering Slack",
      "type": "SLACK",
      "isActive": true,
      "createdAt": "2025-01-01T00:00:00Z"
    }
  ]
}

Get Notification Channel

GET /api/v1/notifications/{channelId}

Create Notification Channel

POST /api/v1/notifications
Content-Type: application/json

Notification Types & Configuration:

Email:

{
  "name": "Team Email",
  "type": "EMAIL",
  "config": {
    "emails": ["team@example.com", "oncall@example.com"]
  }
}

SMS (requires plan with SMS feature):

{
  "name": "On-Call SMS",
  "type": "SMS",
  "config": {
    "phoneNumbers": ["+1234567890"]
  }
}

Slack:

{
  "name": "Engineering Slack",
  "type": "SLACK",
  "config": {
    "webhookUrl": "https://hooks.slack.com/services/...",
    "channel": "#alerts"
  }
}

Discord:

{
  "name": "Discord Alerts",
  "type": "DISCORD",
  "config": {
    "webhookUrl": "https://discord.com/api/webhooks/..."
  }
}

Telegram:

{
  "name": "Telegram Bot",
  "type": "TELEGRAM",
  "config": {
    "botToken": "123456:ABC...",
    "chatId": "-1001234567890"
  }
}

PagerDuty:

{
  "name": "PagerDuty",
  "type": "PAGERDUTY",
  "config": {
    "integrationKey": "your-pagerduty-integration-key",
    "severity": "critical"
  }
}

OpsGenie:

{
  "name": "OpsGenie",
  "type": "OPSGENIE",
  "config": {
    "apiKey": "your-opsgenie-api-key",
    "priority": "P1"
  }
}

Microsoft Teams:

{
  "name": "Teams Channel",
  "type": "MICROSOFT_TEAMS",
  "config": {
    "webhookUrl": "https://outlook.office.com/webhook/..."
  }
}

Custom Webhook:

{
  "name": "Custom Webhook",
  "type": "WEBHOOK",
  "config": {
    "url": "https://api.example.com/webhook",
    "headers": { "Authorization": "Bearer token" }
  }
}

Update Notification Channel

PUT /api/v1/notifications/{channelId}
Content-Type: application/json

Delete Notification Channel

DELETE /api/v1/notifications/{channelId}

Webhooks API

Configure webhooks to receive event notifications at your endpoints.

List Webhooks

GET /api/v1/webhooks

Response:

{
  "webhooks": [
    {
      "id": "wh_abc123",
      "name": "Production Webhook",
      "url": "https://api.example.com/statusapp-events",
      "events": "monitor.down,monitor.up,incident.created,incident.resolved",
      "isActive": true,
      "lastStatus": 200,
      "lastTriggeredAt": "2025-01-21T10:30:00Z"
    }
  ]
}

Get Webhook

GET /api/v1/webhooks/{webhookId}

Create Webhook

POST /api/v1/webhooks
Content-Type: application/json

Request Body:

{
  "name": "Production Webhook",
  "url": "https://api.example.com/statusapp-events",
  "events": "monitor.down,monitor.up,incident.created,incident.resolved",
  "secret": "your-webhook-secret"
}
FieldTypeDescription
namestringRequired. Webhook name
urlstringRequired. Endpoint URL
eventsstringRequired. Comma-separated event types
secretstringOptional secret for signature verification

Available Events:

  • monitor.down - Monitor detected as down
  • monitor.up - Monitor recovered
  • incident.created - New incident created
  • incident.resolved - Incident resolved

Update Webhook

PUT /api/v1/webhooks/{webhookId}
Content-Type: application/json

Delete Webhook

DELETE /api/v1/webhooks/{webhookId}

Test Webhook

Send a test payload to verify your webhook endpoint.

POST /api/v1/webhooks/{webhookId}/test

Response:

{
  "success": true,
  "statusCode": 200,
  "responseTime": 245
}

Get Webhook Deliveries

View delivery history for a webhook.

GET /api/v1/webhooks/{webhookId}/deliveries

Response:

{
  "deliveries": [
    {
      "id": "wd_abc123",
      "event": "monitor.down",
      "statusCode": 200,
      "attempts": 1,
      "deliveredAt": "2025-01-21T10:30:00Z"
    }
  ]
}

Webhook Payload Format

When an event occurs, StatusApp sends a POST request to your webhook URL:

{
  "event": "monitor.down",
  "timestamp": "2025-01-21T10:30:00Z",
  "monitor": {
    "id": "mon_abc123",
    "name": "Production API",
    "url": "https://api.example.com",
    "type": "API",
    "status": "DOWN"
  },
  "incident": {
    "id": "inc_xyz789",
    "incidentNumber": "INC-2025-0042",
    "startedAt": "2025-01-21T10:25:00Z",
    "severity": "high"
  },
  "check": {
    "statusCode": 500,
    "responseTime": 1250,
    "error": "Internal Server Error",
    "region": "us-east-1"
  }
}

Webhook Signature Verification

If you provided a secret when creating the webhook, verify authenticity using HMAC-SHA256:

const crypto = require('crypto');

function verifyWebhookSignature(payload, signature, secret) {
  const expectedSignature = crypto
    .createHmac('sha256', secret)
    .update(JSON.stringify(payload))
    .digest('hex');

  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expectedSignature)
  );
}

// In your webhook handler
app.post('/webhook', (req, res) => {
  const signature = req.headers['x-webhook-signature'];
  if (!verifyWebhookSignature(req.body, signature, WEBHOOK_SECRET)) {
    return res.status(401).send('Invalid signature');
  }
  // Process webhook...
});

Team Management API

List Team Members

GET /api/v1/team/members

Response:

{
  "members": [
    {
      "id": "tm_abc123",
      "email": "developer@example.com",
      "name": "Jane Developer",
      "role": "MEMBER",
      "status": "ACCEPTED",
      "acceptedAt": "2025-01-15T00:00:00Z"
    }
  ]
}

Get Team Member

GET /api/v1/team/members/{memberId}

Add Team Member

POST /api/v1/team/members
Content-Type: application/json

Request Body:

{
  "email": "newmember@example.com",
  "name": "New Team Member",
  "role": "MEMBER"
}

Roles:

RoleDescription
OWNERFull access (account creator)
ADMINManage team and all resources
MEMBERCreate and manage monitors
VIEWERRead-only access

Update Team Member

PUT /api/v1/team/members/{memberId}
Content-Type: application/json

Request Body:

{
  "role": "ADMIN"
}

Remove Team Member

DELETE /api/v1/team/members/{memberId}

List Pending Invitations

GET /api/v1/team/invitations

Response:

{
  "invitations": [
    {
      "id": "inv_abc123",
      "email": "pending@example.com",
      "role": "MEMBER",
      "status": "PENDING",
      "createdAt": "2025-01-20T00:00:00Z",
      "expiresAt": "2025-01-27T00:00:00Z"
    }
  ]
}

Send Invitation

POST /api/v1/team/invitations
Content-Type: application/json

Request Body:

{
  "email": "newmember@example.com",
  "role": "MEMBER"
}
FieldTypeDescription
emailstringRequired. Email address to invite
rolestringRequired. Role to assign: ADMIN, MEMBER, or VIEWER

Response:

{
  "id": "inv_xyz789",
  "email": "newmember@example.com",
  "role": "MEMBER",
  "status": "PENDING",
  "createdAt": "2025-01-21T10:30:00Z"
}

Account API

Get Account Details

GET /api/v1/account

Response:

{
  "id": "user_abc123",
  "email": "user@example.com",
  "name": "John Doe",
  "image": "https://example.com/avatar.jpg",
  "accountStatus": "ENABLED",
  "plan": "PROFESSIONAL",
  "phoneNumber": "+1234567890",
  "smsNotificationsEnabled": true,
  "language": "en",
  "timezone": "America/New_York",
  "createdAt": "2025-01-01T00:00:00Z"
}

Update Account

PUT /api/v1/account
Content-Type: application/json

Request Body:

{
  "name": "Jane Doe",
  "phoneNumber": "+0987654321",
  "smsNotificationsEnabled": true,
  "timezone": "Europe/London"
}

Get API Usage

GET /api/v1/account/usage

Query Parameters:

ParameterTypeDescription
periodstringTime period: 1h, 24h, 7d, 30d

Response:

{
  "period": "24h",
  "totalRequests": 1542,
  "requestsByEndpoint": {
    "/api/v1/monitors": 850,
    "/api/v1/incidents": 412,
    "/api/v1/heartbeat": 280
  },
  "rateLimit": {
    "limit": 5000,
    "used": 1542,
    "remaining": 3458
  }
}

List API Keys

GET /api/v1/account/api-keys

Response:

{
  "apiKeys": [
    {
      "id": "key_abc123",
      "name": "Production API Key",
      "prefix": "sk_live_abc...",
      "permissions": "read,write",
      "lastUsedAt": "2025-01-21T10:30:00Z",
      "expiresAt": null,
      "isActive": true,
      "createdAt": "2025-01-01T00:00:00Z"
    }
  ]
}

Analytics API

Get Dashboard Analytics

GET /api/v1/analytics

Response:

{
  "monitors": {
    "total": 25,
    "active": 23,
    "paused": 2,
    "up": 22,
    "down": 1,
    "degraded": 0
  },
  "incidents": {
    "open": 1,
    "resolvedToday": 3,
    "resolvedThisWeek": 12,
    "avgMttr": 845
  },
  "performance": {
    "avgResponseTime": 245,
    "p95ResponseTime": 520,
    "overallUptime": 99.95
  }
}

Get Monitor Analytics

GET /api/v1/analytics/monitors/{monitorId}

Query Parameters:

ParameterTypeDescription
periodstringTime period: 1h, 24h, 7d, 30d

Response:

{
  "monitorId": "mon_abc123",
  "period": "7d",
  "uptime": 99.95,
  "avgResponseTime": 245,
  "p50ResponseTime": 180,
  "p95ResponseTime": 520,
  "p99ResponseTime": 890,
  "totalChecks": 2016,
  "checksUp": 2015,
  "checksDown": 1,
  "incidents": 1,
  "responseTimeByRegion": {
    "us-east-1": 180,
    "eu-west-1": 245,
    "ap-southeast-1": 380
  }
}

Worker Regions API

List Available Regions

GET /api/v1/worker-regions

Response:

{
  "regions": [
    {
      "id": "region_us_east_1",
      "city": "Virginia",
      "country": "United States",
      "countryCode": "US",
      "latitude": 37.926868,
      "longitude": -78.024902,
      "isActive": true
    },
    {
      "id": "region_eu_west_1",
      "city": "Dublin",
      "country": "Ireland",
      "countryCode": "IE",
      "latitude": 53.349805,
      "longitude": -6.26031,
      "isActive": true
    }
  ]
}

Use region IDs when creating monitors with multi-region monitoring enabled.


Health & Server API

API Health Check

GET /api/v1/health

Response:

{
  "status": "healthy",
  "version": "1.0.0",
  "uptime": 864000,
  "checks": {
    "database": "healthy",
    "redis": "healthy"
  },
  "timestamp": "2025-01-21T10:30:00Z"
}

Status Values:

  • healthy - All systems operational
  • degraded - Some services impacted
  • unhealthy - Critical issues

Get API Version

GET /api/v1/server/version

Response:

{
  "version": "1.0.0",
  "apiVersion": "v1"
}

Server Metrics (Agent Push)

POST /api/v1/server/metrics
X-Server-Key: your-server-key
Content-Type: application/json

Note: This endpoint is used by StatusApp server monitoring agents to push metrics. It is NOT authenticated via API key - it uses a separate X-Server-Key header obtained when setting up server monitoring.

Request Body (from agent):

{
  "cpuUsagePercent": 45.2,
  "memoryUsedPercent": 68.5,
  "diskUsagePercent": 55.0,
  "hostname": "prod-server-01",
  "osVersion": "Ubuntu 22.04",
  "uptime": 864000
}

Response:

{
  "success": true,
  "status": "UP",
  "alerts": []
}

For setting up server monitoring, see the Server Monitoring documentation.


Error Handling

Error Response Format

All errors return a consistent JSON format:

{
  "error": "Error message description",
  "code": "ERROR_CODE",
  "details": "Additional context if available"
}

Common Error Codes

HTTP StatusError CodeDescription
400BAD_REQUESTInvalid request parameters
401UNAUTHORIZEDMissing or invalid API key
403FORBIDDENInsufficient permissions or account not active
404NOT_FOUNDResource not found
429RATE_LIMIT_EXCEEDEDToo many requests
500INTERNAL_SERVER_ERRORServer error

Validation Errors

For validation errors (400), the response includes field-specific issues:

{
  "error": [
    {
      "code": "invalid_type",
      "expected": "string",
      "received": "undefined",
      "path": ["name"],
      "message": "Required"
    }
  ]
}

Code Examples

Node.js / JavaScript

const STATUSAPP_API_KEY = process.env.STATUSAPP_API_KEY;
const BASE_URL = 'https://ops.statusapp.io/api/v1';

// Helper function for API calls
async function statusAppAPI(endpoint, options = {}) {
  const response = await fetch(`${BASE_URL}${endpoint}`, {
    ...options,
    headers: {
      'X-API-Key': STATUSAPP_API_KEY,
      'Content-Type': 'application/json',
      ...options.headers
    }
  });

  if (!response.ok) {
    const error = await response.json();
    throw new Error(error.error || 'API request failed');
  }

  return response.json();
}

// Create a monitor
async function createMonitor(data) {
  return statusAppAPI('/monitors', {
    method: 'POST',
    body: JSON.stringify(data)
  });
}

// Get all monitors
async function getMonitors(params = {}) {
  const queryString = new URLSearchParams(params).toString();
  return statusAppAPI(`/monitors${queryString ? '?' + queryString : ''}`);
}

// Example usage
const monitor = await createMonitor({
  name: 'Production API',
  url: 'https://api.example.com/health',
  type: 'API',
  interval: 300,
  expectedStatusCode: 200
});
console.log('Created monitor:', monitor.id);

Python

import os
import requests
from typing import Optional, Dict, Any

STATUSAPP_API_KEY = os.environ['STATUSAPP_API_KEY']
BASE_URL = 'https://ops.statusapp.io/api/v1'

class StatusAppClient:
    def __init__(self, api_key: str):
        self.api_key = api_key
        self.session = requests.Session()
        self.session.headers.update({
            'X-API-Key': api_key,
            'Content-Type': 'application/json'
        })

    def _request(self, method: str, endpoint: str, **kwargs) -> Dict[Any, Any]:
        response = self.session.request(method, f'{BASE_URL}{endpoint}', **kwargs)
        response.raise_for_status()
        return response.json()

    def list_monitors(self, **params) -> Dict:
        return self._request('GET', '/monitors', params=params)

    def create_monitor(self, data: Dict) -> Dict:
        return self._request('POST', '/monitors', json=data)

    def get_monitor(self, monitor_id: str) -> Dict:
        return self._request('GET', f'/monitors/{monitor_id}')

    def delete_monitor(self, monitor_id: str) -> Dict:
        return self._request('DELETE', f'/monitors/{monitor_id}')

    def ping_heartbeat(self, heartbeat_id: str, message: Optional[str] = None) -> Dict:
        if message:
            return self._request('POST', f'/heartbeat/{heartbeat_id}',
                               json={'message': message})
        return self._request('GET', f'/heartbeat/{heartbeat_id}')

# Example usage
client = StatusAppClient(STATUSAPP_API_KEY)

# Create a monitor
monitor = client.create_monitor({
    'name': 'Production API',
    'url': 'https://api.example.com/health',
    'type': 'API',
    'interval': 300
})
print(f"Created monitor: {monitor['id']}")

# List all monitors
monitors = client.list_monitors(status='UP', limit=10)
for m in monitors['monitors']:
    print(f"  - {m['name']}: {m['status']}")

Go

package main

import (
    "bytes"
    "encoding/json"
    "fmt"
    "io"
    "net/http"
    "os"
)

const baseURL = "https://ops.statusapp.io/api/v1"

type StatusAppClient struct {
    apiKey string
    client *http.Client
}

func NewClient(apiKey string) *StatusAppClient {
    return &StatusAppClient{
        apiKey: apiKey,
        client: &http.Client{},
    }
}

func (c *StatusAppClient) request(method, endpoint string, body interface{}) ([]byte, error) {
    var reqBody io.Reader
    if body != nil {
        jsonData, err := json.Marshal(body)
        if err != nil {
            return nil, err
        }
        reqBody = bytes.NewBuffer(jsonData)
    }

    req, err := http.NewRequest(method, baseURL+endpoint, reqBody)
    if err != nil {
        return nil, err
    }

    req.Header.Set("X-API-Key", c.apiKey)
    req.Header.Set("Content-Type", "application/json")

    resp, err := c.client.Do(req)
    if err != nil {
        return nil, err
    }
    defer resp.Body.Close()

    return io.ReadAll(resp.Body)
}

func (c *StatusAppClient) CreateMonitor(data map[string]interface{}) (map[string]interface{}, error) {
    respBody, err := c.request("POST", "/monitors", data)
    if err != nil {
        return nil, err
    }

    var result map[string]interface{}
    err = json.Unmarshal(respBody, &result)
    return result, err
}

func main() {
    client := NewClient(os.Getenv("STATUSAPP_API_KEY"))

    monitor, err := client.CreateMonitor(map[string]interface{}{
        "name":     "Production API",
        "url":      "https://api.example.com/health",
        "type":     "API",
        "interval": 300,
    })

    if err != nil {
        fmt.Printf("Error: %v\n", err)
        return
    }

    fmt.Printf("Created monitor: %v\n", monitor["id"])
}

cURL Examples

List Monitors:

curl -X GET https://ops.statusapp.io/api/v1/monitors \
  -H "X-API-Key: your_api_key"

Create Monitor:

curl -X POST https://ops.statusapp.io/api/v1/monitors \
  -H "X-API-Key: your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "My API",
    "url": "https://api.example.com",
    "type": "API",
    "interval": 300
  }'

Send Heartbeat:

curl https://ops.statusapp.io/api/v1/heartbeat/your_heartbeat_id

Get Incidents:

curl -X GET "https://ops.statusapp.io/api/v1/incidents?status=open" \
  -H "X-API-Key: your_api_key"

Best Practices

1. Use Environment Variables

Never hardcode API keys:

const API_KEY = process.env.STATUSAPP_API_KEY;

2. Handle Rate Limits

Implement exponential backoff:

async function apiRequestWithRetry(fn, maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    try {
      return await fn();
    } catch (error) {
      if (error.status === 429 && i < maxRetries - 1) {
        const resetTime = error.headers?.get('X-RateLimit-Reset');
        const waitMs = resetTime
          ? Math.max(0, resetTime - Date.now())
          : Math.pow(2, i) * 1000;
        await new Promise(r => setTimeout(r, waitMs));
        continue;
      }
      throw error;
    }
  }
}

3. Cache Responses

Cache read-only data to reduce API calls:

const cache = new Map();
const CACHE_TTL = 60000; // 1 minute

async function getCachedMonitors() {
  const cached = cache.get('monitors');
  if (cached && Date.now() - cached.timestamp < CACHE_TTL) {
    return cached.data;
  }

  const data = await getMonitors();
  cache.set('monitors', { data, timestamp: Date.now() });
  return data;
}

4. Use Pagination

Handle large result sets properly:

async function getAllMonitors() {
  const allMonitors = [];
  let page = 1;
  let hasMore = true;

  while (hasMore) {
    const { monitors, pagination } = await getMonitors({ page, limit: 100 });
    allMonitors.push(...monitors);
    hasMore = page < pagination.totalPages;
    page++;
  }

  return allMonitors;
}

5. Error Handling

Always handle errors gracefully:

try {
  const monitors = await getMonitors();
  // Process monitors
} catch (error) {
  if (error.status === 401) {
    console.error('Invalid API key');
  } else if (error.status === 429) {
    console.error('Rate limited - retry later');
  } else if (error.status >= 500) {
    console.error('Server error - check StatusApp status page');
  } else {
    console.error('API error:', error.message);
  }
}

Troubleshooting

401 Unauthorized

  • Verify API key is correct and complete
  • Check API key is active (not expired or revoked)
  • Ensure proper header format (X-API-Key or Authorization: Bearer)
  • Confirm key has not expired

403 Forbidden

  • Verify API key has required permissions (read, write, delete)
  • Check account status is ENABLED
  • Confirm you're accessing resources you own (or are a team member of)

429 Rate Limit Exceeded

  • Check your plan's rate limits
  • Implement exponential backoff
  • Cache responses when possible
  • Upgrade plan if needed

500 Internal Server Error

  • Check StatusApp status page
  • Retry with exponential backoff
  • Contact support if persistent

Support

Need help with the API?


Next Steps

Start monitoring in 30 seconds

StatusApp gives you 30-second checks from 35+ global locations, instant alerts, and beautiful status pages. Free plan available.