Documentation
How PingThat checks your sites, decides something is down, and talks to your systems.
Get started
- Create an account and confirm your email.
- Add a monitor: an address, how often to check, and what counts as healthy.
- Run the first check. You'll see real timings, not sample data.
- Add an email or webhook destination, confirm it, send a test, then create a route.
How checks work
Each check is an HTTP GET from PingThat's servers with the user agent PingThat-Monitor. We record name lookup, connection, TLS handshake, time to first byte, and total time. The response body is read only when you set a keyword rule, limited to the first 1 MiB, and never stored.
- Status codes you list (for example
200-299, 301) count as up. Redirects are followed unless their code is one you accepted. - HTTPS certificates are always verified.
- Only public internet addresses on ports 80, 443, 8080 and 8443 can be checked. Private, loopback, link-local and internal addresses are refused, including after redirects.
- Response time means the HTTP request, not a full browser page load.
When PingThat decides something is down
Each monitor uses one of two check types.
Basic (the default)
- One region checks on your schedule. The first failed check marks the monitor Confirming.
- If the next check also fails, an incident opens and the monitor is Down. The number of failures in a row is shown in the incident.
- Two consecutive passing checks resolve the incident.
Confirmed (optional)
Available when the service runs at least three check regions.
- A failed check from the primary region marks the monitor Confirming.
- Two other regions run fresh checks. If at least two of the three fail within 30 seconds, an incident opens.
- If only the primary region failed, the monitor is Degraded and no incident or alert is created.
- If confirmation can't complete, it stays unconfirmed. PingThat never quietly marks it up.
- Recovery needs two consecutive passes from the primary region plus one pass from a different region.
Both types
- Slow responses are tracked separately: three checks in a row over your threshold open a latency incident.
- If checks stop arriving, the monitor shows No data, never a false Down.
Outage durations run from the first failed check to the first passing check, so they are estimates; the real start and end fall somewhere between checks.
Webhooks
PingThat sends a signed POST with a JSON body. Respond with any 2xx within 10 seconds. Other responses are retried with growing delays for a limited number of attempts, except 4xx errors (other than 408, 425 and 429), which are treated as final. Redirects are not followed. Delivery is at least once, so de-duplicate on the event id.
Headers
PingThat-Event-Id: stable across retriesPingThat-Delivery-Id: the delivery recordPingThat-Timestamp: unix seconds at send timePingThat-Signature:v1=followed by the hex HMAC-SHA256 oftimestamp.bodyusing your signing secret
Verify the signature (Node.js)
import crypto from "node:crypto";
export function verify(secret, headers, rawBody) {
const ts = Number(headers["pingthat-timestamp"]);
if (Math.abs(Date.now() / 1000 - ts) > 300) return false; // reject replays
const expected = "v1=" + crypto.createHmac("sha256", secret)
.update(`${ts}.${rawBody}`).digest("hex");
const given = headers["pingthat-signature"] ?? "";
return given.length === expected.length &&
crypto.timingSafeEqual(Buffer.from(given), Buffer.from(expected));
}Verify the signature (Python)
import hmac, hashlib, time
def verify(secret: str, headers: dict, raw_body: bytes) -> bool:
ts = int(headers["PingThat-Timestamp"])
if abs(time.time() - ts) > 300: # reject replays
return False
expected = "v1=" + hmac.new(secret.encode(), f"{ts}.".encode() + raw_body, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, headers.get("PingThat-Signature", ""))Example payload
{
"id": "6d0c2f0e-…", // stable event id: use it to de-duplicate
"type": "incident.opened",
"created_at": "2026-10-10T14:02:19+00:00",
"data": {
"monitor": { "id": "…", "name": "Acme homepage", "target": "https://acme.example/" },
"client": { "id": "…", "name": "Acme Bakery" },
"incident": { "id": "…", "category": "availability", "failure_category": "http_status",
"first_failure_at": "…", "confirmed_at": "…", "url": "https://…/incidents/…" },
"regions": [ { "region": "…", "outcome": "failure", "status_code": 503 } ]
}
}Event types: incident.opened, incident.resolved, incident.flapping (a summary sent instead of repeated alerts when a monitor keeps failing and recovering), and test.notification. Your signing secret is shown once when you add the webhook; you can replace it at any time.
Limits and what's not here yet
Published check IP addresses, an API for managing monitors, status pages, and reports are not available yet.