PingTo.it

pingto.it documentation

pingto.it watches your websites and services from several places on the public internet and emails you when something is really down — not when one connection somewhere drops. It can also watch your scheduled jobs: a job calls its heartbeat URL each time it runs, and you hear about it when the calls stop.

Each thing being watched is a monitor. A monitor has a type (web page, certificate, DNS, …), a target (a URL, a host name or a host:port), how often it is checked, and a few options that depend on its type.

To start, sign in with your ScaledApps account and pick your organization: its pingto.it account is created on the Free plan. Add monitors on your account page, or with the API or AI tools using a key you issue there. Plans are changed in ScaledApps ID. Questions: contact@scaledapps.com.

How checking works

pingto.it looks at every monitor that is due once a minute. Each monitor is checked by our main server and by probes: small machines on different internet connections in other places, running the same checks independently. Heartbeats are the exception — your job calls pingto.it, so there is nothing to check from elsewhere.

Checked from our main server only

Any monitor except a heartbeat can be set to central only: it is checked by our main server alone and is not sent to the probes. This is for targets that some internet connections can't reach by design — for example port 25 on a mail server, which many home and business internet providers block outbound. Its status comes from our main server's results alone. Ask for it when your monitor is set up, or any time later.

When is it down?

  • Each place keeps its own count of failures in a row. A place counts as failing once that count reaches the monitor's threshold: 2 failures in a row for most types, 1 for certificates and heartbeats (see the table). One dropped connection is not an outage.
  • After a first failure the monitor is looked at again at the next run (within a minute) rather than a full interval later, so a real outage is confirmed quickly even on monitors checked every few minutes.
  • Over the places that have reported recently (within three intervals plus two minutes): all failing → down; some failing, some working → partial; none failing → up.
  • A place that stops reporting simply drops out of the vote. A probe that finds it can't reach well-known public services either treats the problem as its own connection, and its failures are not counted.
  • Results that reach us more than 5 minutes after they were taken (a probe catching up after losing its connection) are kept in the history but never change a monitor's state or send an alert.

An outage is recorded as an incident from the first failure, so its length includes the time it took to confirm.

Monitor types

The interval can be set from 1 minute to 24 hours, in whole minutes. These are the defaults:

TypeTargetChecked everyFailures in a row to count
Web page an http or https URL minute 2
App health an http or https URL minute 2
Redirect an http or https URL 5 minutes 2
Certificate a host or domain name, e.g. example.com 6 hours 1
DNS a host or domain name, e.g. example.com 15 minutes 2
TCP port host:port, e.g. mail.example.com:25 minute 2
Heartbeat your job calls its URL 5 minutes 1

Web page checks

A GET request to the URL, following redirects (up to 5). By default it passes when the final answer is any 2xx status.

OptionMeaning
Expected statusPass only on this exact HTTP status (100–599) instead of any 2xx.
KeywordText the page must contain (up to 200 characters, matched exactly, case-sensitive).
Keyword absentText the page must not contain — for example an error message your site shows.
TimeoutHow long to wait for the whole answer: 1–50 seconds, default 30. Connecting has its own 10-second limit.
Slow aboveA response time in milliseconds. Slower checks are marked slow in the history; this is not a failure and sends no alert.

Each check records the response time split into DNS lookup, connecting, TLS handshake and time to first byte.

Connection problems are reported in plain words: Can't resolve the host name, Connection refused, Timed out, TLS handshake failed, Certificate not valid for this host, Empty reply from server, Connection reset. A wrong status reads like HTTP 503 or HTTP 404 (expected 200); a keyword problem like Page doesn't contain "Welcome" or Page contains "Database error".

The keyword options also work on JSON. To watch part of an application's own health report, point a web page check at it with a keyword absent such as "queue_ok":false — the check fails when your app reports that part as broken, even though the page itself answers 200.

App health checks

For an application's own health endpoint, such as Laravel's /up: a GET request that does not follow redirects and passes only on HTTP 200. A redirect to a login page or a maintenance page is a failure. Option: timeout, as above.

Redirect checks

For an address that should send visitors somewhere else — an old domain, a short link. A GET request that does not follow the redirect. It passes when the status is 301, 302, 303, 307 or 308 and the Location header is what you expect.

OptionMeaning
Expected locationThe exact Location, or a prefix ending in * (https://example.com/* accepts any page on that site). Empty: any location.
Expected statusOnly this status, e.g. 301.

Certificate checks

Connects to the host on port 443 and looks at the certificate it actually serves — which catches a renewed certificate the web server hasn't loaded yet.

  • Down when the certificate is expired, not trusted, not valid for the host name, or has fewer than 7 days left. One failed check is enough.
  • Warning email when it has 21 days or fewer left (the warning threshold can be set from 1 to 90 days). You get one warning per certificate; a renewed certificate can warn again when its own time comes.

Failures read like Certificate expires in 5 days, Certificate expired on 2026-10-01 or Certificate is for www.example.com, not example.com.

DNS checks

For a domain. Each check:

  1. looks up the domain's nameservers (NS) and asks every one of them for the domain. If any doesn't answer, the check fails (Not answering: ns2.example.net); if none answer, No nameserver answered;
  2. reads the domain's NS, A, AAAA and MX records, its SPF record (the TXT record starting v=spf1) and its DMARC record (TXT at _dmarc.).

The first check records these as the baseline. When a later check finds any difference, you get one DNS changed email showing each record before and after, and the new records become the baseline. A change on its own does not mark the monitor down — if you didn't make it, check your registrar and DNS host.

TCP port checks

Port checks need a domain you have verified, so they can't be pointed at other people's machines: on your account's Domains page, add the domain, publish the TXT record shown (_pingto.example.com = pingto-verify=…) and press Verify. Port checks on that domain and its subdomains are then allowed, and run from our main server only. The record is looked for daily; if it is missing for a week, the domain is unverified and its port checks stop.

For a service that isn't a web site: mail, a database, SSH. The target is host:port. It passes when a connection opens within 5 seconds. Option: expected greeting — text the server's first line must contain (for example ESMTP on a mail server), read for up to 3 seconds.

Heartbeats

For backups, imports, scheduled tasks — anything that should run on time. Each heartbeat monitor has a private URL with a 32-character token. Treat it like a password: anyone who has it can report for your job.

RequestDoes
GET, POST or HEAD
https://pingto.it/h/{token}
The job ran. Marks it up (sending an UP again email if it was down) and starts the clock for the next run.
GET or POST
https://pingto.it/h/{token}/fail
The job ran and failed. Marks it down straight away and sends a DOWN email with the reason The job reported a failure. The next successful ping brings it back up.

Both answer 200 with the text OK. A token that doesn't exist, or belongs to a paused monitor, gets a 404. No sign-in, cookie or header is needed. The request body is ignored and pingto.it doesn't read exit codes: your job decides which URL to call.

Interval and grace period

A heartbeat has an interval (how often the job runs; default 5 minutes) and a grace period (how late it may be; 1 minute to 24 hours, and equal to the interval if not set). If no ping arrives within interval + grace of the last one, the monitor goes down with No ping since 2026-10-31 02:30 UTC — or Never pinged, counted from when it was set up. For a nightly job, use an interval of 24 hours and a grace period that covers how long the job can take.

Examples

Ping every 5 minutes to show a server is alive:

*/5 * * * * curl -fsS -m 10 --retry 3 https://pingto.it/h/YOUR_TOKEN > /dev/null

A nightly backup that reports success or failure:

30 2 * * * /usr/local/bin/backup.sh; if [ $? -eq 0 ]; then curl -fsS -m 10 --retry 3 https://pingto.it/h/YOUR_TOKEN; else curl -fsS -m 10 --retry 3 https://pingto.it/h/YOUR_TOKEN/fail; fi > /dev/null

Laravel's scheduler has this built in:

Schedule::command('backup:run')->dailyAt('02:30')
    ->pingOnSuccess('https://pingto.it/h/YOUR_TOKEN')
    ->pingOnFailure('https://pingto.it/h/YOUR_TOKEN/fail');

Call the URL at the end of the job, so a job that hangs or dies part-way is reported as late.

Status values

StatusMeaning
unknownJust set up: no result yet.
upWorking from every place checking it.
partialFailing from some places but working from others — usually a network problem between those places and your site, not the site itself.
downFailing from every place checking it (or, for a heartbeat, late or reported failed).
pausedNot being checked. Pausing and resuming is done by ScaledApps staff on request.

Alerts

Alerts are sent by email to your account's alert addresses (set on your account's Alerts page; at first, the address of whoever created the account), and to your Slack channels and webhooks. Your monitors alert only you, and no one else's monitors alert you. Places that check are named "Location 1, 2, …":

SubjectWhen
DOWN: nameA monitor becomes down — including when a partial outage spreads to every place. Lists each place and the error it saw.
Partly failing: nameA monitor becomes partial, listing where it fails and where it works.
UP again: nameIt recovers, with how long it was down and the error it was failing with.
Certificate expires in N days: hostOnce per certificate, at the warning threshold (21 days by default).
DNS changed: domainOnce per change, with the records before and after.

You get one email when the status changes, not one per failed check: a monitor that stays down doesn't email again until it recovers.

Text messages (SMS) for down, recovered and certificate alerts (Pro plan and up) are being switched on: numbers can be added on the Alerts page now. See text message alerts for how opting in, STOP and HELP work.

Slack and webhooks

On the Starter plan and up, every alert can also go to Slack (paste an incoming-webhook URL) and to webhooks of your own: an https address that receives a POST with a JSON body. Add them on your account's Alerts page and send a test. A delivery that fails (no 2xx answer within 10 seconds) is retried after 1, 5 and 30 minutes and 2 hours. Redirects are not followed, and the address must be on the public internet.

POST /your/hook
webhook-id: msg_…
webhook-timestamp: 1767225600
webhook-signature: v1,K5o…=
Content-Type: application/json

{"type": "monitor.down",
 "timestamp": "2026-11-02T14:03:00+00:00",
 "data": {"monitor": {"id": "kbXbepWC", "name": "Shop", "type": "http", "target": "https://shop.example.com/", …},
          "incident": {"started_at": "…", "confirmed_at": "…", "resolved_at": null, "cause": "HTTP 502"},
          "locations": {"Location 1": "HTTP 502", "Location 2": "HTTP 502"}}}

Types: monitor.down, monitor.degraded (failing from some places), monitor.recovered, certificate.expiring, dns.changed, and test. Requests are signed with the Standard Webhooks scheme: the signature is v1, + base64 of HMAC-SHA256 over webhook-id.webhook-timestamp.body, keyed with your secret (the part after whsec_, base64-decoded). The secret is shown once, when you add the webhook. Check the signature and that the timestamp is recent before trusting a request; use webhook-id to ignore a repeat.

If your plan no longer includes Slack or webhooks, they are kept but nothing is sent to them; email always works.

Maintenance windows

Expecting downtime? Schedule a window on the Alerts page, or from a deploy script with the API, for every monitor or one. While it lasts, monitors are still checked and their history kept, but nobody is alerted. If something is still down when the window ends, you are told then; an outage that is over before the window ends sends nothing at all. A window lasts at most 31 days.

curl -X POST https://pingto.it/api/v1/maintenance \
  -H "Authorization: Bearer ps_…" -H "Content-Type: application/json" \
  -d '{"minutes": 15, "note": "Deploy"}'

Status pages

A public page at pingto.it/s/your-name for your customers: whether each chosen monitor is working, 90 days of daily history with uptime, maintenance now and scheduled, and incidents from the last two weeks with a note you can add (on the monitor's page). It never shows what you monitor, the errors seen or where checks run, and you can give each monitor a public name. Make them on your account's Status pages page. The page updates every minute.

Add your logo (PNG, JPEG, WebP or SVG, with an optional white version for visitors in dark mode) and where it links. On the Business plan the page can also live at your own address, such as status.example.com: verify the domain on the Domains page, add a CNAME record from the hostname to pingto.it, and pingto checks the DNS and gets the certificate itself, usually within minutes.

Plans

FreeStarterProBusiness
Monitors (heartbeats included)1050150500
Fastest checkevery 5 minutesevery minuteevery minuteevery minute
Status pages11520
Status page at your own address———Yes
Slack and webhooks—YesYesYes
API requests per minute60120300600

An account may go 20% over its monitor count; past that, new monitors are refused until some are removed or the plan changes. If an account ends up beyond that (say its plan changed), the oldest monitors up to that number keep running and the newest are paused — not checked, no alerts — and the account's alert addresses are told; they resume when monitors are deleted or the plan grows.

API

Issue a key on your account page (owners and admins). Send it as Authorization: Bearer ps_…. Keys have scopes: monitors:read and monitors:write. Monitor ids are 8 characters.

RequestDoes
GET /api/v1/monitorsYour monitors
POST /api/v1/monitorsAdd one: type, name, target, interval_minutes and the type's options (as on the page). A heartbeat comes back with its heartbeat_url.
GET /api/v1/monitors/{id}One monitor
PATCH /api/v1/monitors/{id}Change what you send; an option sent as null is cleared. The type is fixed.
POST /api/v1/monitors/{id}/pause, /resumeStop and restart checks and alerts
DELETE /api/v1/monitors/{id}Remove it; past incidents stay
GET /api/v1/monitors/{id}/checksRecent checks, newest first (?limit= up to 500)
GET /api/v1/incidentsOpen incidents first, then recent ones
GET /api/v1/maintenanceCurrent and upcoming maintenance windows
POST /api/v1/maintenanceminutes or ends_at; optional monitor, starts_at, note
DELETE /api/v1/maintenance/{id}End it now, or cancel one not yet started
curl -X POST https://pingto.it/api/v1/monitors \
  -H "Authorization: Bearer ps_…" -H "Content-Type: application/json" \
  -d '{"type": "http", "name": "Shop", "target": "https://shop.example.com/", "interval_minutes": 5, "keyword": "Add to cart"}'

Errors come back as {"error": {"code": "…", "message": "…"}}. Codes: invalid_key (401), rate_limited (429), not_found (404), invalid_request, interval_too_short, blocked_address, unverified_domain (422), monitor_limit, account_suspended (403). Targets must be on the public internet, when saved and at every check.

AI tools (MCP)

AI assistants that speak the Model Context Protocol can use pingto.it at https://pingto.it/mcp with the same key (as a bearer token), scopes and limits as the API. Tools: list, get, create, update, pause, resume and delete monitors; recent checks; incidents; list, start and end maintenance windows. A read-only key sees only the reading tools.

claude mcp add --transport http pingto https://pingto.it/mcp --header "Authorization: Bearer ps_…"

Limits and data

WhatLimit
Check interval1 minute (5 on Free) to 24 hours, in whole minutes
Heartbeat grace period1 minute to 24 hours
Web page and app health timeout1–50 seconds (default 30); 10 seconds to connect
TCP connect5 seconds
Monitor name120 characters
Target500 characters
Keyword, keyword absent, expected greeting200 characters each

Every check result is kept for 30 days; hourly summaries (for uptime figures) are kept after that.

Our checks on your site

Web page, app health and redirect checks identify themselves with the user agent pingto.it-monitor/1.0 (+https://pingto.it). Leave it out of your analytics, and make sure a firewall or bot filter doesn't block it — a blocked check looks like an outage. Checks only read: they send a plain GET with no cookies and never submit forms. Questions: contact@scaledapps.com. See also the terms and privacy policy.