pingrobot

PingRobot / Docs

REST API

Create, change and read your monitors, alert contacts, maintenance windows and status pages from scripts, CI pipelines and your own tools. The API is on every plan, including Free.

Quick start

  1. In the app, open Settings → API keys and create a key. Copy it: it's shown only once.
  2. Send it as a Bearer token:
curl https://pingrobot.io/api/v1/monitors \
  -H "Authorization: Bearer pr_live_…"

Create a monitor:

curl https://pingrobot.io/api/v1/monitors \
  -H "Authorization: Bearer pr_live_…" \
  -H "Content-Type: application/json" \
  -d '{ "type": "http", "name": "Shop", "url": "https://shop.example.com" }'

OpenAPI and MCP

  • OpenAPI 3.1: pingrobot.io/api/v1/openapi.json describes every route below. Import it into Postman, Insomnia or Bruno, or generate a client with your favourite OpenAPI generator. It doesn't need a key.
  • MCP server: connect Claude, Cursor or VS Code at https://pingrobot.io/mcp with the same keys. See the MCP docs.

Basics

Base URLhttps://pingrobot.io/api/v1
FormatJSON in and out. Send Content-Type: application/json with every request that has a body.
TimesUnix time in seconds (UTC).
Rate limit120 requests a minute per key. Over that you get 429 with Retry-After: 60.
Plan limitsThe same as in the app: monitor count, fastest interval, status pages, alert channels and history.

Authentication

Every request needs an API key in the Authorization header:

Authorization: Bearer pr_live_9f2c…

Each key has an access level, chosen when you create it. Keys work on your account: the same monitors and pages you see in the app.

KeyWhat it can do
Read onlyGET requests only. Good for dashboards and reporting.
Read & writeEverything below: create, change and delete.

We store only a hash of each key, so a lost key can't be shown again: revoke it in Settings and create a new one. Revoking takes effect at once. You can have up to 10 keys. Keep keys on your server; they are not meant for code that runs in a browser.

A key can't reach billing, account settings or other keys. Those need a signed-in session in the app.

Errors

Errors return a JSON body with a human-readable message. Plan limits also say "upgrade": true:

{ "error": "Your plan includes 10 monitors. Upgrade to add more.", "upgrade": true }
StatusMeaning
400The request isn't valid. The message says which field.
401The key is missing, wrong or revoked.
403The key is read-only, or the feature isn't on your plan.
404No such route, or the item doesn't exist on your account.
409A conflict, such as a status page address that is taken.
415The body isn't sent as application/json.
429Too many requests. Wait for Retry-After seconds.

Account

GET /account

Your plan, its limits and what you use. Handy to check a key works.

{
  "mode": "live",
  "email": "you@example.com",
  "plan": "pro",
  "limits": { "monitors": 50, "minInterval": 1, "statusPages": 5, "channels": ["email", "webhook", "slack", "discord"], "historyDays": 365 },
  "usage": { "monitors": 12, "statusPages": 1 },
  "grace": null
}

Monitors

RequestWhat it does
GET /monitorsAll monitors, with status, the last 24 hours and 30-day uptime.
POST /monitorsCreates a monitor. Returns 201 and { "monitor": … }.
GET /monitors/{id}One monitor with uptime stats, a response-time chart and recent incidents. Add ?range=7d, 30d, 90d or 1y for longer history (within your plan).
PATCH /monitors/{id}Changes the fields you send; the rest stay as they are.
DELETE /monitors/{id}Deletes the monitor and its history.
GET /monitors/{id}/incidents/{incidentId}One incident with detail, what the failing check saw: the HTTP status, response headers (without cookies), the first 1 KB of the body or the connection, TLS or DNS error, the response time and the second-region re-check. Also its comments.
POST /monitors/{id}/incidents/{incidentId}/comments{ "body": "Rolled back deploy 4512." } adds a comment (up to 2,000 characters). Comments are private to your account and team.
DELETE /monitors/{id}/incidents/{incidentId}/comments/{commentId}Deletes a comment.
POST /monitors/testRuns one check with the settings you send, without saving. See below.
POST /monitors/bulk{ "ids": [12, 13], "action": "pause" }. action: pause, resume, delete, tag or untag (with "tag": "acme"). Up to 250 monitors; returns how many were changed and which were skipped, and why.
GET /monitors/exportEvery monitor as a CSV file (the same columns the import reads).
POST /monitors/import{ "csv": "…", "dryRun": true } creates monitors from CSV: our export, or another tool's (headers such as Friendly Name, URL/IP, Type, Port, Interval). Up to 500 rows; returns what was created, errors and warnings by line. Use dryRun to preview.

To pause a monitor send PATCH with { "active": false }, and { "active": true } to resume it.

Monitor fields

FieldTypeNotes
typestringhttp, keyword, port, heartbeat or dns. Required.
namestringUp to 80 characters. Shown on status pages. Defaults to the target.
urlstringhttp and keyword: a public http(s) URL.
host, portstring, numberport: host name or IP and TCP port. dns: the domain in host.
keyword, keywordAbsentstring, booleankeyword: the text to find in the first 512 KB. With keywordAbsent: true the monitor is down when the text is found.
dnsType, dnsExpectedstring, string[]dns: record type (A, AAAA, CNAME, MX, NS, TXT or CAA; default A) and up to 10 values that must be present.
intervalMinnumber1, 5, 10, 15, 30 or 60 (heartbeats: 5 to 1440). Free plans check every 5 minutes at most.
timeoutSnumber3 to 30 seconds. Default 15.
httpMethodstringGET, HEAD, POST, PUT, PATCH, DELETE or OPTIONS.
httpAuthobject{ "type": "basic", "username": "ops", "password": "…" } or { "type": "bearer", "token": "…" }, sent as the Authorization header. Responses show only the type and username, never the secret; leave the password or token out on an update to keep it, or send null to remove it.
httpHeadersarray[{ "name": "X-Api-Key", "value": "…" }]. Header values are returned as saved, so prefer httpAuth for credentials.
httpBodystringUp to 8 KB, for methods other than GET/HEAD.
expectedCodesstringStatus codes that count as up, like "200-299,301". Default: 2xx. Expecting a 3xx checks the redirect instead of following it.
jsonRulesarrayhttp only, up to 10: [{ "path": "$.status", "op": "equals", "value": "ok" }]. op: equals, not_equals, contains, not_contains, greater_than, less_than, exists, not_exists, not_empty.
slowMsnumber|nullCounts the check as down when the response is slower (100 to 30,000 ms).
sslDaysnumber|nullHTTPS monitors: certificate reminder 7, 14 or 30 days ahead. New HTTPS monitors default to 14.
expiryDaysnumber|nullDomain expiry reminder 7, 14, 30 or 60 days ahead.
alertAfterMinnumberSend the down alert only after this many minutes down: 0, 2, 5, 10, 15, 30 or 60.
repeatMinnumber"Still down" reminder every 15, 30, 60, 120, 360 or 1440 minutes; 0 for none.
tagsstring[]Up to 10 labels to group monitors, e.g. by client or environment (lowercased).
contactIdsnumber[]Which alert contacts this monitor uses. Left out on create: every confirmed contact.
activebooleanfalse pauses the monitor.

Read-only fields in responses: id, status (pending, up, down or paused), statusSince, lastReason, heartbeatUrl and lastPingAt (heartbeats), createdAt, and ssl / domain with the dates we found.

Example: an API monitor with a JSON rule

curl https://pingrobot.io/api/v1/monitors \
  -H "Authorization: Bearer pr_live_…" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "http",
    "name": "API health",
    "url": "https://api.example.com/health",
    "intervalMin": 1,
    "httpAuth": { "type": "bearer", "token": "YOUR_SERVICE_TOKEN" },
    "jsonRules": [{ "path": "$.status", "op": "equals", "value": "ok" }],
    "alertAfterMin": 2
  }'

Example: a heartbeat for a cron job

curl https://pingrobot.io/api/v1/monitors \
  -H "Authorization: Bearer pr_live_…" \
  -H "Content-Type: application/json" \
  -d '{ "type": "heartbeat", "name": "Nightly backup", "intervalMin": 1440 }'

The response's heartbeatUrl is the address your job calls when it finishes, for example curl -fsS https://pingrobot.io/hb/…. Calling it doesn't need an API key.

Example: pause monitors during a deploy

curl -X PATCH https://pingrobot.io/api/v1/monitors/12 \
  -H "Authorization: Bearer pr_live_…" \
  -H "Content-Type: application/json" \
  -d '{ "active": false }'

For planned work that repeats, a maintenance window is usually better: checks pause and resume on their own.

POST /monitors/test

Send the same fields as for a new monitor. We run one check now and return the result and the first part of the response, without saving anything. It is limited to 10 tests a minute.

Alert contacts

RequestWhat it does
GET /contactsYour contacts and the channels your plan includes.
POST /contacts{ "type": "email", "target": "ops@example.com", "label": "On-call" }. type: email, webhook, slack, discord or ntfy (the topic URL, such as https://ntfy.sh/your-topic); target is the address or the https URL. Email contacts get a confirmation link and alert once confirmed. A webhook's response includes its signing secret.
DELETE /contacts/{id}Removes the contact from all monitors.
POST /contacts/{id}/testSends a test alert to the contact.

A new confirmed contact is added to all your monitors. To choose per monitor, set contactIds on the monitor. Webhook payloads and signatures are described in the webhook docs.

Maintenance windows

While a window is active, the monitors it covers aren't checked and send no alerts.

RequestWhat it does
GET /maintenanceAll windows, each with active and expired.
POST /maintenanceCreates a window.
PATCH /maintenance/{id}Replaces a window's settings.
DELETE /maintenance/{id}Deletes it.
FieldNotes
nameUp to 80 characters.
repeatonce, daily or weekly.
monitorIdsThe monitors it covers, or null for all of them.
startAt, endAtonce: start and end (unix seconds), at most 7 days apart.
timeMin, durationMindaily / weekly: start as minutes after midnight (0–1439) and length in minutes (5–1440).
daysweekly: a bitmask of weekdays, Monday = 1, Tuesday = 2, Wednesday = 4 … Sunday = 64.
tzAn IANA time zone such as Europe/Istanbul. Default UTC.
# Every Sunday 02:00–03:00 Istanbul time, all monitors
curl https://pingrobot.io/api/v1/maintenance \
  -H "Authorization: Bearer pr_live_…" \
  -H "Content-Type: application/json" \
  -d '{ "name": "Weekly patching", "repeat": "weekly", "days": 64, "timeMin": 120, "durationMin": 60, "tz": "Europe/Istanbul", "monitorIds": null }'

A deploy script can open a one-off window right before it starts (startAt now, endAt in 15 minutes) and delete it when it's done.

Status pages

RequestWhat it does
GET /status-pagesYour pages.
POST /status-pages{ "title": "Acme status", "slug": "acme", "monitorIds": [12, 13] }. Optional: brand (a hex colour), noindex, showUptime and showIncidents (booleans), password (Pro: 6–128 characters; null removes it; it is never returned, responses say passwordProtected), slaTarget (e.g. 99.9) and slaPeriod (month for the calendar month, or 30d for the last 30 days).
PATCH /status-pages/{id}Changes the fields you send.
DELETE /status-pages/{id}Deletes the page.

The page is public at https://pingrobot.io/s/{slug} and https://{slug}.pingrobot.io. Custom domains are set up in the app (see status page domains).

Announcements

RequestWhat it does
GET /status-pages/{id}/announcementsThe page's incidents and notices with their updates.
POST /status-pages/{id}/announcementsPosts one: { "kind": "incident", "status": "investigating", "title": "Checkout errors", "body": "We're looking into it.", "notify": true }.
POST /status-pages/{id}/announcements/{aid}/updatesAdds an update: { "status": "resolved", "body": "Fixed.", "notify": true }.
DELETE /status-pages/{id}/announcements/{aid}Removes it.
GET /status-pages/{id}/subscribersThe page's email subscribers (up to 1,000), each with confirmed.
DELETE /status-pages/{id}/subscribers/{sid}Removes a subscriber.

kind and its status values: incident (investigating, identified, monitoring, resolved), maintenance (scheduled, in_progress, completed) and info (posted). With "notify": true (Pro), confirmed email subscribers get the update.

Changes

We add fields and routes without notice, so ignore fields you don't know. Anything that would break an existing integration comes as a new version (/api/v2), and v1 keeps working.

Questions or something missing? Get in touch.