API Reference

Programmatic access to your StatusRooster monitors, checks, incidents, and heartbeat pings.

Getting Started

Authentication

All API requests require an API key in the X-API-Key header. Sign up to get one.

curl -H "X-API-Key: sr_your_key_here" \ https://statusrooster.com/api/v1/monitors
headers = {"X-API-Key": "sr_your_key_here"} r = requests.get("https://statusrooster.com/api/v1/monitors", headers=headers)
const res = await fetch("https://statusrooster.com/api/v1/monitors", { headers: { "X-API-Key": "sr_your_key_here" } });

Response envelope: Every endpoint returns this shape:

{ "data": { ... }, // Payload (object, array, or null) "error": null, // Error string, or null on success "meta": { ... } // Optional โ€” included on list endpoints (count, limit, etc.) }

All timestamps are UTC, formatted as ISO 8601 (2026-03-03T12:30:00+00:00).

Error codes:

CodeMeaning
401Missing or invalid API key
403Monitor limit exceeded
404Resource not found
422Invalid or missing request body

Error response example:

// 403 โ€” Monitor limit exceeded { "detail": { "data": null, "error": "Free plan limited to 10 monitors. Upgrade to Pro for up to 200." } } // 422 โ€” Validation error (missing required field) { "detail": [ { "loc": ["body", "name"], "msg": "Field required", "type": "missing" } ] }

All features (SMS, Slack, webhooks, maintenance windows, custom headers, basic auth) are available on all plans. The only limit is the number of monitors: Free gets 10, Pro gets 200.

Client Setup

All code examples on this page assume these variables are defined:

# No setup โ€” pass the header with each request curl -H "X-API-Key: sr_your_key_here" https://statusrooster.com/api/v1/monitors
# pip install requests import requests API_KEY = "sr_your_key_here" BASE = "https://statusrooster.com/api/v1" headers = {"X-API-Key": API_KEY}
// Node.js or browser const API_KEY = "sr_your_key_here"; const BASE = "https://statusrooster.com/api/v1"; const headers = { "X-API-Key": API_KEY };

Request Builder

Select a monitor type, fill in fields, and get ready-to-use code.


        

Monitor Types

HTTP / HTTPS

monitor_type: "http"

Monitor websites and URLs for availability. Checks HTTP status code, response time, body keyword matching, and auto-detects SSL certificate expiry. This is the default monitor type.

pending up down
POST /api/v1/monitors Create HTTP monitor

Provision a new HTTP/HTTPS uptime monitor. Begins checking immediately with pending status until the first check completes.

FieldTypeRequiredDescription
namestringrequiredDisplay name for the monitor. Example: "Production API", "Blog Homepage"
urlstringrequiredFull URL including protocol. Must be http:// or https://. Example: "https://example.com/health". Private/internal IPs are blocked.
monitor_typestringoptional"http" โ€” this is the default and can be omitted for HTTP monitors
expected_status_codeintegeroptionalHTTP status code the checker expects. Default: 200. Common values: 200 (OK), 201 (Created), 204 (No Content), 301 (Redirect), 401 (to verify auth is enforced)
timeoutintegeroptionalMax seconds to wait for a response. Range: 1โ€“60. Default: 10. Values outside this range return a 422 error.
http_methodstringoptionalThe HTTP method the checker uses to hit your URL. Values: GET, POST, HEAD, PUT, PATCH, DELETE, OPTIONS. Default: GET. Use HEAD for fast up/down checks without downloading the body.
follow_redirectsbooleanoptionalFollow HTTP 3xx redirects. Default: true. Set to false to verify a redirect itself returns the expected 301/302 status.
basic_auth_userstringoptionalHTTP Basic Auth username. Sent as Authorization: Basic base64(user:pass) header.
basic_auth_passstringoptionalHTTP Basic Auth password. Required if basic_auth_user is set. Ignored without basic_auth_user.
bearer_tokenstringoptionalBearer token. Sent as Authorization: Bearer <token>. Example: "tok_live_abc123". If both Bearer and Basic are set, Bearer takes precedence.
request_bodystringoptionalRequest body to send. Only used with POST, PUT, PATCH, DELETE methods. Example: "{\"ping\": true}". Ignored for GET/HEAD.
request_content_typestringoptionalContent-Type header for the request body. Values: "application/json", "text/plain", "application/x-www-form-urlencoded". Only relevant when request_body is set.
custom_headersarrayoptionalCustom request headers sent with each check. Array of objects: [{"key": "X-Custom-Header", "value": "my-value"}, {"key": "Accept", "value": "text/html"}]
keywordstringoptionalMark down if keyword is missing from response body. Prefix with ! for "must NOT contain". Combine with AND / OR. Examples: "Welcome", "!maintenance", "OK AND !error", "healthy OR ready"
response_threshold_msstringoptionalMark down if response time violates this condition. Formats: "> 2000" (slower than 2s), "< 50" (suspiciously fast), "200-3000" (outside range). Value is in milliseconds.
check_intervalintegeroptionalSeconds between checks. Free plan: 60โ€“300 (default 60). Pro plan: 60โ€“300 (default 60). Common values: 60 (1 min), 120 (2 min), 300 (5 min).
alert_emailstringoptionalOverride alert email. Defaults to your account email. Example: "ops@example.com"
alert_slack_webhookstringoptionalSlack incoming webhook URL for alerts. Example: "https://hooks.slack.com/services/T00/B00/xxxx"
webhook_urlstringoptionalURL to receive POST requests on status changes (up โ†’ down, down โ†’ up). Payload includes monitor ID, status, and timestamp.
publicbooleanoptionalInclude on your public status page at /s/{slug}. Default: true. Set to false for internal monitors.
pausedbooleanoptionalCreate in paused state โ€” no checks run until resumed. Default: false
groupstringoptionalGroup tag for organizing monitors on the dashboard. Free-form text. Examples: "Production", "Staging", "Payment Services"
maintenance_windowsarrayoptionalSuppress alerts during scheduled windows. Array of objects with day ("monday"โ€“"sunday" or "daily"), start_utc, and end_utc in "HH:MM" format. Example: [{"day": "daily", "start_utc": "02:00", "end_utc": "04:00"}]
curl -X POST \ -H "X-API-Key: sr_your_key_here" \ -H "Content-Type: application/json" \ -d '{ "name": "My Website", "url": "https://example.com", "expected_status_code": 200, "timeout": 10, "keyword": "Welcome", "check_interval": 60 }' \ https://statusrooster.com/api/v1/monitors
r = requests.post( f"{BASE}/monitors", headers=headers, json={ "name": "My Website", "url": "https://example.com", "expected_status_code": 200, "timeout": 10, "keyword": "Welcome", "check_interval": 60 } ) monitor = r.json()["data"] print(f"Created {monitor['id']} โ€” status: {monitor['status']}")
const res = await fetch(`${BASE}/monitors`, { method: "POST", headers: { ...headers, "Content-Type": "application/json" }, body: JSON.stringify({ name: "My Website", url: "https://example.com", expected_status_code: 200, timeout: 10, keyword: "Welcome", check_interval: 60 }) }); const { data } = await res.json(); console.log(`Created ${data.id}`);

Response (201 Created):

{ "data": { "id": "mon_a1b2c3", "monitor_type": "http", "name": "My Website", "url": "https://example.com", "status": "pending", "expected_status_code": 200, "timeout": 10, "http_method": "GET", "follow_redirects": true, "basic_auth_user": "", "basic_auth_pass": "", "bearer_token": "", "request_body": "", "request_content_type": "", "custom_headers": [], "keyword": "Welcome", "response_threshold_ms": null, "check_interval": 60, "uptime_percent": 100.0, "checks_total": 0, "checks_failed": 0, "last_checked": null, "last_status_code": null, "last_response_ms": null, "ssl_expiry": null, "ssl_issuer": null, "alert_email": "you@example.com", "public": true, "paused": false, "group": "", "slug": "my-website-a3f8c1", "created_at": "2026-03-03T12:00:00+00:00" }, "error": null }
PUT PATCH /api/v1/monitors/{id} Update HTTP monitor

Partial update โ€” include only the fields you want to change. Omitted fields are preserved. Both PUT and PATCH are accepted and behave identically.

FieldTypeDescription
namestringDisplay name. Example: "Production API"
urlstringMonitored URL. Must include protocol (http:// or https://). Private IPs are blocked.
expected_status_codeintegerExpected HTTP status code. Common values: 200, 201, 204, 301
timeoutintegerRequest timeout in seconds. Range: 1โ€“60. Values outside this range return a 422 error.
http_methodstringHTTP method the checker uses to hit your URL. Values: GET, POST, HEAD, PUT, PATCH, DELETE, OPTIONS
follow_redirectsbooleanFollow HTTP 3xx redirects. Set false to verify redirects return the expected 301/302.
basic_auth_userstringHTTP Basic Auth username. Sent as Authorization: Basic base64(user:pass).
basic_auth_passstringHTTP Basic Auth password. Set to "" to clear. Ignored without basic_auth_user.
bearer_tokenstringBearer token. Example: "tok_live_abc123". Set to "" to clear. Takes precedence over Basic Auth.
request_bodystringRequest body for POST/PUT/PATCH/DELETE. Example: "{\"ping\": true}". Ignored for GET/HEAD.
request_content_typestringContent-Type for request body. Values: "application/json", "text/plain", "application/x-www-form-urlencoded"
custom_headersarrayCustom headers. Replaces all existing headers. Example: [{"key": "Accept", "value": "text/html"}]. Send [] to clear.
keywordstringBody keyword expression. Examples: "OK", "!error", "OK AND !maintenance". Set to "" to clear.
response_threshold_msstringResponse time condition in ms. Formats: "> 2000", "< 50", "200-3000". Set to "" to clear.
check_intervalintegerSeconds between checks. Free: 60โ€“300. Pro: 60โ€“300. Common: 60 (1 min), 300 (5 min).
alert_emailstringAlert email address. Example: "ops@example.com". Set to "" to use account default.
alert_slack_webhookstringSlack incoming webhook URL. Example: "https://hooks.slack.com/services/T00/B00/xxxx"
webhook_urlstringURL to receive POST on status changes. Set to "" to disable.
publicbooleanShow on public status page (/s/{slug}). Set false for internal monitors.
pausedbooleanPause (true) or resume (false) monitoring. Paused monitors run no checks.
groupstringGroup tag. Examples: "Production", "Staging". Set to "" to clear.
slugstringCustom status page URL slug (/s/{slug}). Auto-sanitized to lowercase alphanumeric + hyphens. Example: "my-api"
maintenance_windowsarrayReplaces all maintenance windows. day: "monday"โ€“"sunday" or "daily". Times in "HH:MM" UTC. Send [] to clear.
curl -X PUT \ -H "X-API-Key: sr_your_key_here" \ -H "Content-Type: application/json" \ -d '{"keyword":"OK AND !error","timeout":15}' \ https://statusrooster.com/api/v1/monitors/mon_a1b2c3
r = requests.put( f"{BASE}/monitors/mon_a1b2c3", headers=headers, json={ "keyword": "OK AND !error", "timeout": 15, "expected_status_code": 200 } ) updated = r.json()["data"]
const res = await fetch(`${BASE}/monitors/mon_a1b2c3`, { method: "PUT", headers: { ...headers, "Content-Type": "application/json" }, body: JSON.stringify({ keyword: "OK AND !error", timeout: 15, expected_status_code: 200 }) }); const { data } = await res.json();

Response (200 OK): Returns the full updated monitor object.

JSON / API

monitor_type: "json_api"

Monitor REST API endpoints with deep JSON response validation. Supports Authorization headers and field-level assertions using dot-notation paths, comparison operators, and existence checks.

pending up down
POST /api/v1/monitors Create JSON/API monitor

Provision a JSON/API monitor that validates HTTP status, parses the JSON response body, and evaluates field-level assertions.

FieldTypeRequiredDescription
namestringrequiredDisplay name. Example: "Health API", "Payments Service"
monitor_typestringrequiredMust be "json_api"
urlstringrequiredAPI endpoint URL. Must return JSON. Example: "https://api.example.com/v1/health". Private IPs are blocked.
expected_status_codeintegeroptionalExpected HTTP status code. Default: 200. Common values: 200, 201, 204
timeoutintegeroptionalMax seconds to wait for a response. Range: 1โ€“60. Default: 10. Values outside this range return a 422 error.
auth_headerstringoptionalFull Authorization header value. Examples: "Bearer tok_live_xxxx", "Basic dXNlcjpwYXNz", "Token my-api-key"
json_assertionsarrayoptionalArray of assertion objects to validate the JSON response (see schema below). All assertions must pass for the check to be "up".
response_threshold_msstringoptionalResponse time condition in ms. Formats: "> 2000" (slower than 2s), "< 50", "200-3000" (outside range)
check_intervalintegeroptionalSeconds between checks. Free: 60โ€“300 (default 60). Pro: 60โ€“300 (default 30). Common: 60 (1 min), 300 (5 min).
alert_emailstringoptionalOverride alert email. Example: "ops@example.com". Defaults to your account email.
alert_slack_webhookstringoptionalSlack incoming webhook URL. Example: "https://hooks.slack.com/services/T00/B00/xxxx"
webhook_urlstringoptionalURL to receive POST on status changes (up โ†’ down, down โ†’ up)
publicbooleanoptionalInclude on public status page. Default: true. Set false for internal monitors.
pausedbooleanoptionalCreate in paused state โ€” no checks run until resumed. Default: false
groupstringoptionalGroup tag. Examples: "Production", "Microservices", "Third-Party"
maintenance_windowsarrayoptionalSuppress alerts during windows. day: "monday"โ€“"sunday" or "daily". Example: [{"day": "sunday", "start_utc": "03:00", "end_utc": "05:00"}]

Assertion object schema:

FieldTypeDescription
pathstringJSON path using dot notation. Supports array indices. Examples: "status", "data.count", "data.users[0].name", "meta.pagination.total"
operatorstringComparison operator. Values: "equals", "not_equals", "contains" (substring match), "not_contains", "greater_than" (numeric), "less_than" (numeric), "exists" (field is present), "not_exists" (field is absent)
valuestringExpected value as a string. Numeric comparisons parse the string: "100". Ignored for exists/not_exists (pass ""). Examples: "ok", "true", "42"
curl -X POST \ -H "X-API-Key: sr_your_key_here" \ -H "Content-Type: application/json" \ -d '{ "monitor_type": "json_api", "name": "Health API", "url": "https://api.example.com/v1/health", "auth_header": "Bearer tok_live_xxxx", "expected_status_code": 200, "json_assertions": [ {"path": "status", "operator": "equals", "value": "ok"}, {"path": "data.version", "operator": "exists", "value": ""} ] }' \ https://statusrooster.com/api/v1/monitors
r = requests.post( f"{BASE}/monitors", headers=headers, json={ "monitor_type": "json_api", "name": "Health API", "url": "https://api.example.com/v1/health", "auth_header": "Bearer tok_live_xxxx", "expected_status_code": 200, "json_assertions": [ {"path": "status", "operator": "equals", "value": "ok"}, {"path": "data.version", "operator": "exists", "value": ""} ] } ) monitor = r.json()["data"] print(f"Created {monitor['id']}")
const res = await fetch(`${BASE}/monitors`, { method: "POST", headers: { ...headers, "Content-Type": "application/json" }, body: JSON.stringify({ monitor_type: "json_api", name: "Health API", url: "https://api.example.com/v1/health", auth_header: "Bearer tok_live_xxxx", expected_status_code: 200, json_assertions: [ { path: "status", operator: "equals", value: "ok" }, { path: "data.version", operator: "exists", value: "" } ] }) }); const { data } = await res.json();

Response (201 Created):

{ "data": { "id": "mon_d4e5f6", "monitor_type": "json_api", "name": "Health API", "url": "https://api.example.com/v1/health", "status": "pending", "auth_header": "Bearer tok_live_xxxx", "json_assertions": [ {"path": "status", "operator": "equals", "value": "ok"}, {"path": "data.version", "operator": "exists", "value": ""} ], "expected_status_code": 200, "timeout": 10, "check_interval": 60, "uptime_percent": 100.0, "checks_total": 0, "checks_failed": 0, "last_checked": null, "last_status_code": null, "last_response_ms": null, "alert_email": "you@example.com", "public": true, "paused": false, "group": "", "slug": "health-api-d4e5f6", "created_at": "2026-03-04T12:00:00+00:00" }, "error": null }

Only type-relevant fields shown. The full response includes all fields from the complete field reference.

PUT PATCH /api/v1/monitors/{id} Update JSON/API monitor

Partial update โ€” include only the fields you want to change. Omitted fields are preserved. Both PUT and PATCH are accepted and behave identically.

FieldTypeDescription
namestringDisplay name. Example: "Health API"
urlstringAPI endpoint URL. Must return JSON. Private IPs are blocked.
expected_status_codeintegerExpected HTTP status code. Common values: 200, 201, 204
timeoutintegerRequest timeout in seconds. Range: 1โ€“60. Values outside this range return a 422 error.
auth_headerstringFull Authorization header value. Examples: "Bearer tok_xxx", "Token my-key". Set to "" to clear.
json_assertionsarrayReplaces all assertions. Array of {path, operator, value} objects. Send [] to clear all assertions.
response_threshold_msstringResponse time condition in ms. Formats: "> 2000", "< 50", "200-3000". Set to "" to clear.
check_intervalintegerSeconds between checks. Free: 60โ€“300. Pro: 60โ€“300. Common: 60 (1 min), 300 (5 min).
alert_emailstringAlert email. Example: "ops@example.com". Set to "" to use account default.
alert_slack_webhookstringSlack incoming webhook URL. Example: "https://hooks.slack.com/services/T00/B00/xxxx"
webhook_urlstringURL to receive POST on status changes. Set to "" to disable.
publicbooleanShow on public status page. Set false for internal monitors.
pausedbooleanPause (true) or resume (false) monitoring.
groupstringGroup tag. Examples: "Production", "Microservices". Set to "" to clear.
slugstringCustom status page URL slug (/s/{slug}). Auto-sanitized to lowercase + hyphens. Example: "health-api"
maintenance_windowsarrayReplaces all maintenance windows. day: "monday"โ€“"sunday" or "daily". Send [] to clear.
curl -X PUT \ -H "X-API-Key: sr_your_key_here" \ -H "Content-Type: application/json" \ -d '{ "auth_header": "Bearer tok_new_xxxx", "json_assertions": [ {"path": "status", "operator": "equals", "value": "healthy"}, {"path": "data.count", "operator": "greater_than", "value": "0"} ] }' \ https://statusrooster.com/api/v1/monitors/mon_d4e5f6
r = requests.put( f"{BASE}/monitors/mon_d4e5f6", headers=headers, json={ "auth_header": "Bearer tok_new_xxxx", "json_assertions": [ {"path": "status", "operator": "equals", "value": "healthy"}, {"path": "data.count", "operator": "greater_than", "value": "0"} ] } ) updated = r.json()["data"]
const res = await fetch(`${BASE}/monitors/mon_d4e5f6`, { method: "PUT", headers: { ...headers, "Content-Type": "application/json" }, body: JSON.stringify({ auth_header: "Bearer tok_new_xxxx", json_assertions: [ { path: "status", operator: "equals", value: "healthy" }, { path: "data.count", operator: "greater_than", value: "0" } ] }) }); const { data } = await res.json();

Response (200 OK): Returns the full updated monitor object.

Heartbeat / Cron

monitor_type: "heartbeat"

Dead man's switch monitoring for cron jobs, scheduled tasks, and background workers. StatusRooster generates a unique ping URL โ€” your service hits it on schedule. If a ping is missed beyond the grace period, the monitor transitions to down and alerts fire.

pending up down
POST /api/v1/monitors Create heartbeat monitor

Provision a heartbeat monitor. No url is needed โ€” a unique ping_url is generated automatically and returned in the response. Add this URL to the end of your cron job or scheduled task.

FieldTypeRequiredDescription
namestringrequiredDisplay name. Examples: "Nightly Backup", "Invoice Cron", "Queue Worker"
monitor_typestringrequiredMust be "heartbeat"
heartbeat_intervalintegeroptionalHow often your job should ping, in seconds. Range: 60โ€“86400. Default: 300. Common values: 300 (5 min), 900 (15 min), 3600 (1 hr), 86400 (daily)
heartbeat_grace_periodintegeroptionalExtra seconds to wait before marking down. Range: 0โ€“3600. Default: 60. Use higher values for jobs with variable runtime. Example: 300 (5 min grace)
check_intervalintegeroptionalHow often StatusRooster verifies whether a ping was received on time. Free: 60โ€“300 (default 60). Pro: 60โ€“300 (default 30).
alert_emailstringoptionalOverride alert email. Example: "ops@example.com". Defaults to your account email.
alert_slack_webhookstringoptionalSlack incoming webhook URL. Example: "https://hooks.slack.com/services/T00/B00/xxxx"
webhook_urlstringoptionalURL to receive POST on status changes (up โ†’ down, down โ†’ up)
publicbooleanoptionalInclude on public status page. Default: true. Set false for internal jobs.
pausedbooleanoptionalCreate in paused state โ€” pings are accepted but not tracked. Default: false
groupstringoptionalGroup tag. Examples: "Infrastructure", "Cron Jobs", "Background Workers"
maintenance_windowsarrayoptionalSuppress alerts during windows. day: "monday"โ€“"sunday" or "daily". Example: [{"day": "daily", "start_utc": "03:00", "end_utc": "04:00"}]
curl -X POST \ -H "X-API-Key: sr_your_key_here" \ -H "Content-Type: application/json" \ -d '{ "monitor_type": "heartbeat", "name": "Nightly Backup Job", "heartbeat_interval": 86400, "heartbeat_grace_period": 300 }' \ https://statusrooster.com/api/v1/monitors
r = requests.post( f"{BASE}/monitors", headers=headers, json={ "monitor_type": "heartbeat", "name": "Nightly Backup Job", "heartbeat_interval": 86400, "heartbeat_grace_period": 300 } ) data = r.json()["data"] print(f"Ping URL: {data['ping_url']}")
const res = await fetch(`${BASE}/monitors`, { method: "POST", headers: { ...headers, "Content-Type": "application/json" }, body: JSON.stringify({ monitor_type: "heartbeat", name: "Nightly Backup Job", heartbeat_interval: 86400, heartbeat_grace_period: 300 }) }); const { data } = await res.json(); console.log(`Ping URL: ${data.ping_url}`);

Response (201 Created):

{ "data": { "id": "hb_abc789", "monitor_type": "heartbeat", "name": "Nightly Backup Job", "status": "pending", "ping_url": "https://statusrooster.com/api/ping/hb_abc789?token=aBcDeFgH...", "url": "https://statusrooster.com/api/ping/hb_abc789?token=aBcDeFgH...", "heartbeat_interval": 86400, "heartbeat_grace_period": 300, "uptime_percent": 100.0, "checks_total": 0, "checks_failed": 0, "last_checked": null, "alert_email": "you@example.com", "public": true, "paused": false, "group": "", "slug": "nightly-backup-job-abc789", "created_at": "2026-03-04T12:00:00+00:00" }, "error": null }

Only type-relevant fields shown. The full response includes all fields from the complete field reference.

๐Ÿ’ก Use the ping_url from the response directly in your crontab โ€” it already includes the token.

GET POST HEAD /api/ping/{monitor_id} Record a heartbeat ping

Records a successful ping and resets the heartbeat timer. If no ping arrives within heartbeat_interval + heartbeat_grace_period, the monitor transitions to down. Accepts GET, POST, or HEAD โ€” use whichever fits your environment.

๐Ÿ”‘ A ?token= query parameter is required. Use the ping_url from the create response โ€” it already includes the token. Returns 403 if the token is missing or invalid.

โš ๏ธ The ping URL is /api/ping/{id}?token={ping_token} โ€” note there is no /v1/ in the path. This is a lightweight endpoint, separate from the API-key-authenticated API.

# Use the ping_url from the create response (includes token) */5 * * * * curl -fsS --retry 3 https://statusrooster.com/api/ping/hb_abc789?token=YOUR_PING_TOKEN # Or with wget */5 * * * * wget -q -O /dev/null https://statusrooster.com/api/ping/hb_abc789?token=YOUR_PING_TOKEN # At the end of a deploy script curl -fsS https://statusrooster.com/api/ping/hb_abc789?token=YOUR_PING_TOKEN
# Use the ping_url from the create response (includes token) import requests requests.get("https://statusrooster.com/api/ping/hb_abc789?token=YOUR_PING_TOKEN")
// Use the ping_url from the create response (includes token) await fetch("https://statusrooster.com/api/ping/hb_abc789?token=YOUR_PING_TOKEN");

Response (200 OK):

{ "ok": true, "status": "up", "monitor": "Nightly Backup Job", "next_expected_before": 1709471700.0 }

next_expected_before is a Unix timestamp โ€” if no ping arrives before this time (plus grace period), the monitor goes down.

Response when paused (200 OK):

{ "ok": true, "status": "paused", "message": "Monitor is paused โ€” ping recorded but not tracked" }

๐Ÿ’ก Tip: Use curl -fsS --retry 3 for silent operation with automatic retries on transient network errors.

PUT PATCH /api/v1/monitors/{id} Update heartbeat monitor

Partial update โ€” include only the fields you want to change. Omitted fields are preserved. Both PUT and PATCH are accepted and behave identically.

FieldTypeDescription
namestringDisplay name. Example: "Nightly Backup"
heartbeat_intervalintegerExpected ping frequency in seconds. Range: 60โ€“86400. Common: 300 (5 min), 3600 (1 hr), 86400 (daily)
heartbeat_grace_periodintegerExtra seconds before alerting. Range: 0โ€“3600. Example: 300 (5 min grace for slow jobs)
check_intervalintegerSeconds between status checks. Free: 60โ€“300. Pro: 60โ€“300.
alert_emailstringAlert email. Example: "ops@example.com". Set to "" to use account default.
alert_slack_webhookstringSlack incoming webhook URL. Example: "https://hooks.slack.com/services/T00/B00/xxxx"
webhook_urlstringURL to receive POST on status changes. Set to "" to disable.
publicbooleanShow on public status page. Set false for internal jobs.
pausedbooleanPause (true) or resume (false). Paused monitors accept pings but don't track them.
groupstringGroup tag. Examples: "Cron Jobs", "Infrastructure". Set to "" to clear.
slugstringCustom status page URL slug (/s/{slug}). Auto-sanitized to lowercase + hyphens. Example: "nightly-backup"
maintenance_windowsarrayReplaces all maintenance windows. day: "monday"โ€“"sunday" or "daily". Send [] to clear.
curl -X PUT \ -H "X-API-Key: sr_your_key_here" \ -H "Content-Type: application/json" \ -d '{"heartbeat_interval":3600,"heartbeat_grace_period":120}' \ https://statusrooster.com/api/v1/monitors/hb_abc789
r = requests.put( f"{BASE}/monitors/hb_abc789", headers=headers, json={ "heartbeat_interval": 3600, "heartbeat_grace_period": 120 } ) updated = r.json()["data"]
const res = await fetch(`${BASE}/monitors/hb_abc789`, { method: "PUT", headers: { ...headers, "Content-Type": "application/json" }, body: JSON.stringify({ heartbeat_interval: 3600, heartbeat_grace_period: 120 }) }); const { data } = await res.json();

Response (200 OK): Returns the full updated monitor object.

SSL Certificate

monitor_type: "ssl"

Dedicated SSL/TLS certificate expiry monitoring. Connects to a domain on port 443, reads the certificate chain, and warns before expiration. The only monitor type with a warn status โ€” triggered when the certificate is within the configured threshold of expiring.

pending up warn down
POST /api/v1/monitors Create SSL certificate monitor

Provision an SSL certificate monitor. No url required โ€” only the bare domain. StatusRooster checks the certificate chain and alerts before expiration.

FieldTypeRequiredDescription
namestringrequiredDisplay name. Examples: "example.com SSL", "API Certificate"
monitor_typestringrequiredMust be "ssl"
ssl_domainstringrequiredBare hostname โ€” no protocol, no path. Examples: "example.com", "api.example.com". Do NOT include https://.
ssl_expiry_threshold_daysintegeroptionalDays before certificate expiry to trigger warn status. Range: 1โ€“90. Default: 30. Common values: 7 (tight), 14 (standard), 30 (conservative)
check_intervalintegeroptionalSeconds between checks. Free: 60โ€“300 (default 60). Pro: 60โ€“300 (default 30). SSL checks are lightweight โ€” 300 (5 min) is usually sufficient.
alert_emailstringoptionalOverride alert email. Example: "security@example.com". Defaults to your account email.
alert_slack_webhookstringoptionalSlack incoming webhook URL. Example: "https://hooks.slack.com/services/T00/B00/xxxx"
webhook_urlstringoptionalURL to receive POST on status changes (up โ†’ warn, warn โ†’ down, etc.)
publicbooleanoptionalInclude on public status page. Default: true
pausedbooleanoptionalCreate in paused state. Default: false
groupstringoptionalGroup tag. Examples: "Infrastructure", "Certificates"
maintenance_windowsarrayoptionalSuppress alerts during windows. day: "monday"โ€“"sunday" or "daily". Example: [{"day": "daily", "start_utc": "02:00", "end_utc": "04:00"}]
curl -X POST \ -H "X-API-Key: sr_your_key_here" \ -H "Content-Type: application/json" \ -d '{ "monitor_type": "ssl", "name": "example.com SSL", "ssl_domain": "example.com", "ssl_expiry_threshold_days": 14 }' \ https://statusrooster.com/api/v1/monitors
r = requests.post( f"{BASE}/monitors", headers=headers, json={ "monitor_type": "ssl", "name": "example.com SSL", "ssl_domain": "example.com", "ssl_expiry_threshold_days": 14 } ) monitor = r.json()["data"] print(f"SSL monitor: {monitor['id']}")
const res = await fetch(`${BASE}/monitors`, { method: "POST", headers: { ...headers, "Content-Type": "application/json" }, body: JSON.stringify({ monitor_type: "ssl", name: "example.com SSL", ssl_domain: "example.com", ssl_expiry_threshold_days: 14 }) }); const { data } = await res.json();

Response (201 Created):

{ "data": { "id": "ssl_def012", "monitor_type": "ssl", "name": "example.com SSL", "status": "pending", "ssl_domain": "example.com", "ssl_expiry_threshold_days": 14, "ssl_expiry": null, "ssl_issuer": null, "url": "https://example.com", "uptime_percent": 100.0, "checks_total": 0, "checks_failed": 0, "last_checked": null, "alert_email": "you@example.com", "public": true, "paused": false, "group": "", "slug": "example-com-ssl-def012", "created_at": "2026-03-04T12:00:00+00:00" }, "error": null }

Only type-relevant fields shown. The full response includes all fields from the complete field reference.

โš ๏ธ SSL monitors use three statuses: up (cert valid and not near expiry), warn (cert expires within threshold), down (cert expired, invalid, or unreachable).

PUT PATCH /api/v1/monitors/{id} Update SSL monitor

Partial update โ€” include only the fields you want to change. Omitted fields are preserved. Both PUT and PATCH are accepted and behave identically.

FieldTypeDescription
namestringDisplay name. Example: "example.com SSL"
ssl_domainstringBare hostname โ€” no protocol. Examples: "example.com", "api.example.com"
ssl_expiry_threshold_daysintegerDays before expiry to trigger warn. Range: 1โ€“90. Common: 7, 14, 30
check_intervalintegerSeconds between checks. Free: 60โ€“300. Pro: 60โ€“300. SSL is lightweight โ€” 300 (5 min) is usually fine.
alert_emailstringAlert email. Example: "security@example.com". Set to "" to use account default.
alert_slack_webhookstringSlack incoming webhook URL. Example: "https://hooks.slack.com/services/T00/B00/xxxx"
webhook_urlstringURL to receive POST on status changes. Set to "" to disable.
publicbooleanShow on public status page. Set false for internal domains.
pausedbooleanPause (true) or resume (false) monitoring.
groupstringGroup tag. Examples: "Certificates", "Infrastructure". Set to "" to clear.
slugstringCustom status page URL slug (/s/{slug}). Auto-sanitized. Example: "example-ssl"
maintenance_windowsarrayReplaces all maintenance windows. day: "monday"โ€“"sunday" or "daily". Send [] to clear.
curl -X PUT \ -H "X-API-Key: sr_your_key_here" \ -H "Content-Type: application/json" \ -d '{"ssl_expiry_threshold_days":7,"name":"example.com SSL (tight)"}' \ https://statusrooster.com/api/v1/monitors/ssl_def012
r = requests.put( f"{BASE}/monitors/ssl_def012", headers=headers, json={ "ssl_expiry_threshold_days": 7, "name": "example.com SSL (tight)" } ) updated = r.json()["data"]
const res = await fetch(`${BASE}/monitors/ssl_def012`, { method: "PUT", headers: { ...headers, "Content-Type": "application/json" }, body: JSON.stringify({ ssl_expiry_threshold_days: 7, name: "example.com SSL (tight)" }) }); const { data } = await res.json();

Response (200 OK): Returns the full updated monitor object.

Shared Endpoints

List All Monitors

GET /api/v1/monitors Retrieve all monitors for the authenticated account

Returns every monitor belonging to the authenticated user โ€” all types in a single array. Use the monitor_type field to filter client-side by type.

curl -H "X-API-Key: sr_your_key_here" \ https://statusrooster.com/api/v1/monitors
r = requests.get(f"{BASE}/monitors", headers=headers) monitors = r.json()["data"] # Filter by type http_monitors = [m for m in monitors if m["monitor_type"] == "http"] heartbeats = [m for m in monitors if m["monitor_type"] == "heartbeat"] for m in monitors: print(f"{m['name']} ({m['monitor_type']}): {m['status']}")
const res = await fetch(`${BASE}/monitors`, { headers }); const { data } = await res.json(); // Filter by type const httpMonitors = data.filter(m => m.monitor_type === "http"); const sslMonitors = data.filter(m => m.monitor_type === "ssl"); data.forEach(m => console.log(`${m.name} (${m.monitor_type}): ${m.status}`));

Response (200 OK):

{ "data": [ { "id": "mon_a1b2c3", "monitor_type": "http", "name": "My Website", "url": "https://example.com", "status": "up", "check_interval": 60, "uptime_percent": 99.8, "checks_total": 4320, "checks_failed": 9, "last_checked": "2026-03-03T12:30:00+00:00", "last_status_code": 200, "last_response_ms": 245.3, ... }, { "id": "hb_abc789", "monitor_type": "heartbeat", "name": "Nightly Backup", "ping_url": "https://statusrooster.com/api/ping/hb_abc789?token=aBcDeFgH...", "status": "up", "heartbeat_interval": 86400, "heartbeat_grace_period": 300, ... } ], "error": null, "meta": { "total": 2 } }
ⓘ Complete field reference (all monitor types)
FieldTypeDescription
Core Fields (all types)
idstringUnique monitor ID
monitor_typestring"http" ยท "json_api" ยท "heartbeat" ยท "ssl"
namestringDisplay name
urlstringMonitored URL (or ping URL for heartbeat, https://domain for SSL)
statusstring"pending" ยท "up" ยท "down" ยท "warn" (SSL only)
check_intervalintSeconds between checks (60โ€“300)
uptime_percentfloatLifetime uptime percentage
checks_totalintTotal checks executed
checks_failedintTotal failed checks
last_checkeddatetime|nullLast check timestamp (null if never checked)
alert_emailstringEmail for alerts
alert_slack_webhookstringSlack webhook URL
webhook_urlstringStatus change webhook URL
maintenance_windowsarrayArray of {day, start_utc, end_utc}
publicboolHas a public status page
pausedboolMonitoring is paused
groupstringDashboard group tag (empty string if ungrouped)
slugstringStatus page URL slug (/s/{slug})
created_atdatetimeCreation timestamp
last_response_by_regionobject|nullLast response time per region (e.g. {"us-east1": 120.5, "us-west1": 245.3})
HTTP & JSON/API Fields
last_status_codeint|nullHTTP status from most recent check
last_response_msfloat|nullResponse time in ms
expected_status_codeint|nullExpected HTTP status (default 200)
timeoutint|nullRequest timeout in seconds
keywordstringBody keyword check string
response_threshold_msstring|nullResponse time condition
ssl_expirydatetime|nullAuto-detected SSL cert expiry
ssl_issuerstring|nullAuto-detected SSL cert issuer
HTTP Only
http_methodstringHTTP method (GET, POST, HEAD, PUT, PATCH, DELETE, OPTIONS)
follow_redirectsboolWhether to follow 3xx redirects (default true)
basic_auth_userstringHTTP Basic Auth username
basic_auth_passstringHTTP Basic Auth password
bearer_tokenstringBearer token auth (sent as Authorization: Bearer <token>)
request_bodystringRequest body for POST/PUT/PATCH/DELETE
request_content_typestringContent-Type for request body
custom_headersarray|nullCustom request headers. Array of {key, value}
JSON/API Only
json_assertionsarray|nullArray of {path, operator, value}
auth_headerstringAuthorization header value
Heartbeat Only
ping_urlstringUnique ping URL
heartbeat_intervalint|nullExpected ping interval in seconds
heartbeat_grace_periodint|nullGrace period before alerting
SSL Only
ssl_domainstringDomain being checked
ssl_expiry_threshold_daysint|nullDays before expiry to trigger warn

Retrieve Monitor

GET /api/v1/monitors/{id} Retrieve a single monitor by ID

Returns a single monitor document with all fields. Works with all monitor types.

curl -H "X-API-Key: sr_your_key_here" \ https://statusrooster.com/api/v1/monitors/mon_a1b2c3
r = requests.get(f"{BASE}/monitors/mon_a1b2c3", headers=headers) monitor = r.json()["data"] print(f"{monitor['monitor_type']}: {monitor['status']}")
const res = await fetch(`${BASE}/monitors/mon_a1b2c3`, { headers }); const { data } = await res.json(); console.log(`${data.monitor_type}: ${data.status}`);

Response (200 OK):

{ "data": { "id": "mon_a1b2c3", "monitor_type": "http", "name": "My Website", "url": "https://example.com", "status": "up", "expected_status_code": 200, "timeout": 10, "http_method": "GET", "follow_redirects": true, "basic_auth_user": "", "basic_auth_pass": "", "bearer_token": "", "request_body": "", "request_content_type": "", "custom_headers": [], "keyword": "Welcome", "response_threshold_ms": null, "check_interval": 60, "uptime_percent": 99.8, "checks_total": 4320, "checks_failed": 9, "last_checked": "2026-03-04T12:30:00+00:00", "last_status_code": 200, "last_response_ms": 245.3, "ssl_expiry": "2026-09-15T00:00:00+00:00", "ssl_issuer": "Let's Encrypt", "alert_email": "you@example.com", "public": true, "paused": false, "group": "", "slug": "my-website-a3f8c1", "created_at": "2026-03-03T12:00:00+00:00" }, "error": null }

๐Ÿ’ก The response shape is identical to objects in the List endpoint. See the complete field reference for all type-specific fields.

Check History

GET /api/v1/monitors/{id}/checks Retrieve check results for a monitor

Returns recent check results for any monitor type, newest first. For heartbeat monitors, each entry represents a scheduled check (not a ping).

Query ParamTypeDefaultDescription
limitinteger100Number of checks to return. Range: 1โ€“500. Example: ?limit=10 for the most recent 10 checks.
curl -H "X-API-Key: sr_your_key_here" \ "https://statusrooster.com/api/v1/monitors/mon_a1b2c3/checks?limit=10"
r = requests.get( f"{BASE}/monitors/mon_a1b2c3/checks", headers=headers, params={"limit": 10} ) for c in r.json()["data"]: print(f"{c['timestamp']}: {c['is_up']} โ€” {c['response_ms']}ms")
const res = await fetch( `${BASE}/monitors/mon_a1b2c3/checks?limit=10`, { headers } ); const { data } = await res.json(); data.forEach(c => console.log(`${c.timestamp}: ${c.response_ms}ms`));

Response (200 OK):

{ "data": [ { "id": "chk_a1b2c3", "timestamp": "2026-03-03T12:30:00+00:00", "status_code": 200, "response_ms": 245.3, "is_up": true, "regions_checked": 3, "regions_up": 3, "response_ms_by_region": {"us-east1": 120.5, "us-west1": 245.3, "europe-west1": 310.1} } ], "error": null, "meta": { "monitor_id": "mon_a1b2c3", "total": 1, "limit": 10 } }
ⓘ Check record field reference
FieldTypeDescription
idstringUnique check ID
timestampdatetimeWhen the check executed
status_codeint|nullHTTP status (null for heartbeat/SSL or connection failure)
response_msfloat|nullResponse time in ms (null for heartbeat/SSL or timeout)
is_upboolWhether the check passed
regions_checkedint|nullNumber of regions that ran this check
regions_upint|nullNumber of regions where the check passed
response_ms_by_regionobject|nullResponse time per region (e.g. {"us-east1": 120.5})

Delete Monitor

DELETE /api/v1/monitors/{id} Permanently delete a monitor

Permanently deletes a monitor and all associated check history. Works with all monitor types. This action is irreversible.

curl -X DELETE \ -H "X-API-Key: sr_your_key_here" \ https://statusrooster.com/api/v1/monitors/mon_a1b2c3
r = requests.delete(f"{BASE}/monitors/mon_a1b2c3", headers=headers) print(r.json())
const res = await fetch(`${BASE}/monitors/mon_a1b2c3`, { method: "DELETE", headers }); console.log(await res.json());

Response (200 OK):

{ "data": { "deleted": true, "monitor_id": "mon_a1b2c3" }, "error": null }

Incidents

Incidents are automatically created when a monitor goes down and automatically resolved when it recovers. They cannot be manually created, modified, or deleted via the API. These endpoints provide read-only access to your incident history.

List Incidents

GET /api/v1/incidents List incidents across all your monitors

Returns incidents across all monitors belonging to the authenticated user, newest first. Filter by status, monitor, monitor type, and time window.

Query ParamTypeDefaultDescription
statusstringโ€”Filter by status: "open" or "resolved". Omit for all.
monitor_idstringโ€”Filter to a specific monitor's incidents.
monitor_typestringโ€”Filter by monitor type: "http", "json_api", "heartbeat", or "ssl".
hoursint720Time window in hours (1โ€“8760). Default returns last 30 days.
limitint50Max results (1โ€“500).
# All incidents (last 30 days) curl -H "X-API-Key: sr_your_key_here" \ https://statusrooster.com/api/v1/incidents # Only open incidents curl -H "X-API-Key: sr_your_key_here" \ https://statusrooster.com/api/v1/incidents?status=open # All heartbeat/cron incidents in the last 7 days curl -H "X-API-Key: sr_your_key_here" \ https://statusrooster.com/api/v1/incidents?monitor_type=heartbeat&hours=168 # Incidents for a specific monitor curl -H "X-API-Key: sr_your_key_here" \ https://statusrooster.com/api/v1/incidents?monitor_id=mon_a1b2c3
# All incidents r = requests.get(f"{BASE}/incidents", headers=headers) incidents = r.json()["data"] # Only currently-open incidents r = requests.get(f"{BASE}/incidents", headers=headers, params={"status": "open"}) open_incidents = r.json()["data"] for inc in incidents: dur = f"{inc['duration_seconds']}s" if inc["duration_seconds"] else "ongoing" print(f"{inc['monitor_name']}: {inc['status']} ({dur})")
// All incidents const res = await fetch(`${BASE}/incidents`, { headers }); const { data } = await res.json(); // Filter open incidents client-side const openIncidents = data.filter(i => i.status === "open"); data.forEach(i => { const dur = i.duration_seconds ? `${i.duration_seconds}s` : "ongoing"; console.log(`${i.monitor_name}: ${i.status} (${dur})`); });

Response (200 OK):

{ "data": [ { "id": "inc_x7k9m2", "monitor_id": "mon_a1b2c3", "monitor_name": "My Website", "monitor_url": "https://example.com", "status": "resolved", "status_code": 503, "response_ms": 12045.2, "started_at": "2026-03-03T08:15:00+00:00", "resolved_at": "2026-03-03T08:22:30+00:00", "duration_seconds": 450, "failure_error_message": "Connection timeout after 10s", "failure_response_headers": null, "failure_response_body": null, "regions_checked": 4, "regions_up": 1, "region_results": [ {"region": "us-east1", "is_up": false, "status_code": 503, "response_ms": 12045.2}, {"region": "us-west1", "is_up": false, "status_code": 503, "response_ms": 11200.0}, {"region": "europe-west1", "is_up": true, "status_code": 200, "response_ms": 245.3}, {"region": "asia-east1", "is_up": false, "status_code": 503, "response_ms": 10800.0} ] }, { "id": "inc_p3q8r1", "monitor_id": "mon_a1b2c3", "monitor_name": "My Website", "monitor_url": "https://example.com", "status": "open", "status_code": 522, "response_ms": null, "started_at": "2026-03-04T14:05:00+00:00", "resolved_at": null, "duration_seconds": null, "failure_error_message": "HTTP 522 โ€” Connection timed out", "failure_response_headers": null, "failure_response_body": null, "regions_checked": 4, "regions_up": 0, "region_results": null } ], "error": null, "meta": { "total": 2, "limit": 50, "hours": 720 } }
ⓘ Incident field reference
FieldTypeDescription
idstringUnique incident ID
monitor_idstringID of the monitor that triggered this incident
monitor_namestringMonitor name at the time the incident was created
monitor_urlstringMonitor URL at the time the incident was created
statusstring"open" (monitor is currently down) or "resolved" (monitor recovered)
status_codeint|nullHTTP status code that caused the failure (null for timeouts, heartbeat misses, or SSL issues)
response_msfloat|nullResponse time when the incident started (null for timeouts or non-HTTP monitors)
started_atdatetimeWhen the monitor first went down
resolved_atdatetime|nullWhen the monitor recovered (null if still open)
duration_secondsint|nullTotal downtime in seconds (null if still open)
failure_error_messagestring|nullError description from the failed check
failure_response_headersobject|nullResponse headers from the failed check
failure_response_bodystring|nullResponse body from the failed check (truncated to 2048 chars)
regions_checkedint|nullNumber of regions that checked during the incident
regions_upint|nullNumber of regions reporting UP during the incident
region_resultsarray|nullPer-region results: [{region, is_up, status_code, response_ms}]

Get Incident

GET /api/v1/incidents/{id} Retrieve a single incident by ID

Returns full details for a single incident. The incident must belong to one of your monitors.

curl -H "X-API-Key: sr_your_key_here" \ https://statusrooster.com/api/v1/incidents/inc_x7k9m2
r = requests.get(f"{BASE}/incidents/inc_x7k9m2", headers=headers) incident = r.json()["data"] print(f"Down since {incident['started_at']}, status: {incident['status']}")
const res = await fetch(`${BASE}/incidents/inc_x7k9m2`, { headers }); const { data } = await res.json(); console.log(`Down since ${data.started_at}, status: ${data.status}`);

Response (200 OK):

{ "data": { "id": "inc_x7k9m2", "monitor_id": "mon_a1b2c3", "monitor_name": "My Website", "monitor_url": "https://example.com", "status": "resolved", "status_code": 503, "response_ms": 12045.2, "started_at": "2026-03-03T08:15:00+00:00", "resolved_at": "2026-03-03T08:22:30+00:00", "duration_seconds": 450, "failure_error_message": "Connection timeout after 10s", "failure_response_headers": null, "failure_response_body": null, "regions_checked": 4, "regions_up": 1, "region_results": [ {"region": "us-east1", "is_up": false, "status_code": 503, "response_ms": 12045.2}, {"region": "us-west1", "is_up": false, "status_code": 503, "response_ms": 11200.0}, {"region": "europe-west1", "is_up": true, "status_code": 200, "response_ms": 245.3}, {"region": "asia-east1", "is_up": false, "status_code": 503, "response_ms": 10800.0} ] }, "error": null }

See List Incidents for the complete field reference.

Extras

Webhook Payloads

When a monitor's status changes, StatusRooster POSTs a JSON payload to your configured webhook_url . Your endpoint should return a 2xx status within 10 seconds.

Headers sent with every webhook:

Content-Type: application/json User-Agent: StatusRooster/1.0

Payload shape:

{ "event": "monitor.down", "monitor_name": "My Website", "monitor_url": "https://example.com", "status": "down", "status_code": 503, "response_ms": 12045.2, "timestamp": "2026-03-03T08:15:00+00:00" }
ⓘ Webhook payload field reference
FieldTypeDescription
eventstring"monitor.down", "monitor.up", or "test"
monitor_namestringMonitor display name
monitor_urlstringMonitored URL
statusstring"up", "down", or "test"
status_codeint|nullHTTP status code (null for timeouts, heartbeat misses, SSL issues, or test events)
response_msfloat|nullResponse time in ms (null for timeouts or non-HTTP monitors)
timestampstringISO 8601 UTC timestamp

Example receiver:

# Test your webhook endpoint manually curl -X POST https://your-server.com/webhooks/statusrooster \ -H "Content-Type: application/json" \ -d '{"event":"monitor.down","monitor_name":"My Website","monitor_url":"https://example.com","status":"down","status_code":503,"response_ms":12045.2,"timestamp":"2026-03-03T08:15:00+00:00"}'
# Flask example from flask import Flask, request app = Flask(__name__) @app.post("/webhooks/statusrooster") def handle_webhook(): data = request.json if data["event"] == "monitor.down": print(f"๐Ÿ”ด {data['monitor_name']} is DOWN ({data['status_code']})") elif data["event"] == "monitor.up": print(f"๐ŸŸข {data['monitor_name']} is back UP") return "", 200
// Express example const express = require("express"); const app = express(); app.use(express.json()); app.post("/webhooks/statusrooster", (req, res) => { const { event, monitor_name, status_code } = req.body; if (event === "monitor.down") { console.log(`๐Ÿ”ด ${monitor_name} is DOWN (${status_code})`); } else if (event === "monitor.up") { console.log(`๐ŸŸข ${monitor_name} is back UP`); } res.sendStatus(200); });

๐Ÿ’ก Use the Test Alert button on your monitor's settings page to send a test webhook with "event": "test" and verify your endpoint is working.

Uptime Badges

Embed live SVG badges in your README, docs, or website. No authentication required โ€” badges are public for monitors with public: true. Cached for 5 minutes. Works with all monitor types.

uptime: 99.9%uptimeuptime99.9%99.9% status: upstatusstatusupup response: 245msresponseresponse245ms245ms
GET /badge/{monitor_id}.svg Uptime percentage badge

Shields.io-style SVG showing uptime percentage. Color-coded: green (โ‰ฅ99.5%), yellow-green (โ‰ฅ99%), yellow (โ‰ฅ95%), red (<95%).

# Markdown ![Uptime](https://statusrooster.com/badge/YOUR_MONITOR_ID.svg) # HTML <img src="https://statusrooster.com/badge/YOUR_MONITOR_ID.svg" alt="Uptime" />
r = requests.get("https://statusrooster.com/badge/YOUR_MONITOR_ID.svg") with open("badge.svg", "w") as f: f.write(r.text)
const res = await fetch("https://statusrooster.com/badge/YOUR_MONITOR_ID.svg"); const svg = await res.text();
GET /badge/{monitor_id}/status.svg Current status badge

Shows current status: up (green), down (red), warn (amber, SSL only), or pending (gray).

# Markdown ![Status](https://statusrooster.com/badge/YOUR_MONITOR_ID/status.svg)
GET /badge/{monitor_id}/response.svg Response time badge

Shows most recent response time. Best suited for HTTP and JSON/API monitors. Color-coded: green (<500ms), yellow-green (<1.5s), yellow (<3s), red (โ‰ฅ3s).

# Markdown ![Response Time](https://statusrooster.com/badge/YOUR_MONITOR_ID/response.svg)

Plan Limits

All features are available on every plan. The only difference is the number of monitors. Exceeding the monitor limit returns 403 Forbidden.

LimitFreePro ($9/mo)
Monitors10200
Check interval60โ€“300s60โ€“300s
Email / Slack / SMS / Webhooksโœ“โœ“
Status pages1010
Custom brandingโœ“โœ“
Maintenance windowsโœ“โœ“
Custom headers / Basic Authโœ“โœ“
Multi-region checks4 regions4 regions
Data retention90 days90 days

Rate Limits

No strict rate limits are enforced today. Please keep polling under 60 requests per minute โ€” once per minute is sufficient for dashboard-style polling. Abusive usage may be throttled or blocked without notice.

The heartbeat ping endpoint (/api/ping/{id}) is excluded from this โ€” ping as frequently as your cron job runs.

Value Validation

Numeric fields with documented ranges (e.g. check_interval, timeout, heartbeat_interval, heartbeat_grace_period, ssl_expiry_threshold_days) return a 422 error if the value is outside the valid range. For example, timeout: 120 returns {"error": "timeout must be between 1 and 60 seconds"}. check_interval below your plan minimum returns a 403 error. Query parameters like limit and hours on GET endpoints are silently clamped to their valid bounds.