Programmatic access to your PoppaPing monitors, incidents, and status pages.
1. Get your API key — Go to Dashboard → API and create a key. Copy it immediately; it won't be shown again.
2. Make your first request
curl -H "Authorization: Bearer pp_live_YOUR_KEY_HERE" \
https://poppaping.com/api/v1/monitors
3. Parse the response
{
"data": [
{
"id": "a1b2c3d4-...",
"name": "Production API",
"url": "https://api.example.com/health",
"current_status": "up",
"response_time_ms": 142,
...
}
],
"meta": { "page": 1, "per_page": 25, "total": 3, "total_pages": 1 }
}
All API requests require an API key. Pass it via either header:
# Option 1: Authorization header (recommended)
Authorization: Bearer pp_live_a8Xk3m...
# Option 2: X-API-Key header
X-API-Key: pp_live_a8Xk3m...
Keys are created in Dashboard → API with configurable scopes:
| Scope | Access |
|---|---|
| monitors:read | List & view monitors, uptime, checks |
| monitors:write | Create, update, delete, pause, resume monitors |
| incidents:read | List & view incidents |
| status-pages:read | List & view status pages |
| alerts:read | List alert channels |
Account endpoints (/account, /account/usage) require any valid key, no specific scope.
Rate limits are per API key, using a sliding 60-second window.
| Plan | Requests / minute |
|---|---|
| Free | 60 |
| Starter | 120 |
| Growth | 180 |
| Pro | 300 |
| Business | 600 |
Every response includes rate limit headers:
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 45
X-RateLimit-Reset: 1708531200
When exceeded, you'll receive 429 Too Many Requests:
{ "error": "rate_limit_exceeded", "message": "Rate limit exceeded. Retry after 15 seconds." }
All errors return JSON with error and message fields:
{ "error": "not_found", "message": "Monitor not found." }
| Status | Error Code | Meaning |
|---|---|---|
| 400 | validation_error | Invalid input data |
| 401 | unauthorized | Missing or invalid API key |
| 401 | key_expired | API key has expired |
| 403 | insufficient_scope | Key lacks required scope |
| 403 | plan_limit_reached | Plan quota exceeded |
| 404 | not_found | Resource doesn't exist |
| 429 | rate_limit_exceeded | Too many requests |
/api/v1/account
Get current account info and plan details. No scope required.
curl -H "Authorization: Bearer $KEY" https://poppaping.com/api/v1/account
/api/v1/account/usage
Get current billing period usage. No scope required.
// Response
{
"monitors_used": 3, "monitors_active": 2, "monitors_limit": 5,
"checks_last_24h": 1440, "plan": "starter"
}
/api/v1/monitors
List all monitors. Scope: monitors:read
Params: ?page=1&per_page=25
curl -H "Authorization: Bearer $KEY" https://poppaping.com/api/v1/monitors
/api/v1/monitors
Create a new monitor. Scope: monitors:write
curl -X POST -H "Authorization: Bearer $KEY" \
-H "Content-Type: application/json" \
-d '{"name":"Production API","url":"https://api.example.com/health","interval_seconds":60}' \
https://poppaping.com/api/v1/monitors
Body fields: name (required), url (required), method (GET), interval_seconds, timeout_seconds (30), headers ({}), regions ([])
/api/v1/monitors/:id
Get single monitor with 30-day uptime. Scope: monitors:read
/api/v1/monitors/:id
Update monitor settings. Only include fields to change. Scope: monitors:write
curl -X PATCH -H "Authorization: Bearer $KEY" \
-H "Content-Type: application/json" \
-d '{"interval_seconds":30}' \
https://poppaping.com/api/v1/monitors/MONITOR_ID
/api/v1/monitors/:id
Delete a monitor permanently. Scope: monitors:write
/api/v1/monitors/:id/pause
POST
/api/v1/monitors/:id/resume
Pause or resume monitoring. Scope: monitors:write
/api/v1/monitors/:id/uptime
Get uptime statistics. Scope: monitors:read
Params: ?period=24h|7d|30d|90d (default: 30d)
// Response
{
"data": {
"monitor_id": "...", "period": "30d",
"uptime_percentage": 99.97, "total_checks": 43200,
"up_checks": 43187, "down_checks": 13,
"avg_response_time_ms": 142.3
}
}
/api/v1/monitors/:id/checks
Get recent check results. Scope: monitors:read
Params: ?page=1&per_page=100 (max 500)
/api/v1/incidents
List all incidents. Scope: incidents:read
Params: ?status=open|resolved&monitor_id=UUID&page=1&per_page=25
/api/v1/incidents/:id
Get incident details with check timeline. Scope: incidents:read
// Response includes timeline of checks during the incident
{
"data": {
"id": "...", "monitor_id": "...",
"started_at": "2025-02-21T14:32:00+00:00",
"resolved_at": "2025-02-21T14:45:00+00:00",
"cause": "Connection timed out",
"status": "resolved",
"timeline": [ { "status": "down", "region": "chicago", ... }, ... ]
}
}
/api/v1/status-pages
List your status pages. Scope: status-pages:read
/api/v1/status-pages/:id
Get status page with current component statuses. Scope: status-pages:read
/api/v1/alerts
List configured alert channels (email, webhooks). Scope: alerts:read
PoppaPing sends webhook events when monitor status changes. Configure webhook URLs in Alerts.
| Event | Trigger |
|---|---|
| monitor.down | Monitor transitions to DOWN |
| monitor.recovered | Monitor recovers from DOWN to UP |
| incident.created | New incident opened |
| incident.resolved | Incident resolved |
{
"event": "monitor.down",
"timestamp": "2025-02-21T14:32:00+00:00",
"data": {
"monitor": {
"id": "...", "name": "Production API",
"url": "https://api.example.com/health",
"current_status": "down", ...
},
"incident": {
"id": "...", "started_at": "2025-02-21T14:32:00+00:00",
"cause": "Connection timed out", "status": "open"
}
}
}
Every webhook includes an X-PoppaPing-Signature header — an HMAC-SHA256 of the raw request body using your webhook secret (found in Settings).
Python
import hmac, hashlib
def verify_signature(body: bytes, signature: str, secret: str) -> bool:
expected = hmac.new(secret.encode(), body, hashlib.sha256).hexdigest()
return hmac.compare_digest(f"sha256={expected}", signature)
Node.js
const crypto = require('crypto');
function verifySignature(body, signature, secret) {
const expected = 'sha256=' + crypto
.createHmac('sha256', secret)
.update(body)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(expected), Buffer.from(signature)
);
}
Questions? Email [email protected]