Skip to content

Heartbeats

A heartbeat monitors a scheduled job, such as a cron job or a backup. Your job calls the heartbeat’s address each time it runs. If no call arrives in time, or a call reports a failure, the heartbeat goes down and alerts.

  1. In the sidebar, go to Uptime, open the Heartbeats tab, and click Add heartbeat.

  2. Enter a Name that says what runs, so an alert says what stopped. The name is required.

  3. Set Expect a request every to how often the job runs, in minutes, hours, or days. The default is 1 day. The longest is 7 days.

  4. Set With a grace period of to how late a call can be before it counts as missed, from 0 to 7 days. The default is 1 hour. With 0, the heartbeat alerts as soon as a call is late.

  5. Save the heartbeat, then copy its address with Copy. The address is shown only to people who can edit monitors.

The address has the form <your dashboard address>/api/push/<token>. If no call arrives within the period plus the grace period, the heartbeat goes Down and alerts at once: there’s no confirmation period, because the grace period is the confirmation. You can’t run a heartbeat on demand.

Call the address at the end of your job. Any HTTP method works, including GET, POST, and HEAD.

Check in when the job finishes
curl -fsS -m 10 --retry 5 -o /dev/null "$HEARTBEAT_URL"

In a crontab, chain the call after the job, so it only runs when the job succeeds:

Crontab: back up, then check in
0 3 * * * /usr/local/bin/backup.sh && curl -fsS -m 10 --retry 5 -o /dev/null "$HEARTBEAT_URL"

To report a failure straight away instead of waiting for a missed call, add /fail or an exit code to the address:

Address Means
<address> Success.
<address>/fail Failure, shown as “Reported failure”.
<address>/<exit code> An exit code from 0 to 999. 0 is success; anything else is a failure, shown as “Exited with code N”.

To pass your job’s exit code, call <address>/$? right after it:

Report the job’s exit code
/usr/local/bin/backup.sh
curl -fsS -m 10 --retry 5 -o /dev/null "$HEARTBEAT_URL/$?"

You can also add these query parameters:

Parameter Description
msg A message to record with the call.
ping A response time to record, in milliseconds.
status up for success; any other value counts as a failure. Only applies to the bare address, and is ignored after /fail or an exit code.
Call Response
A valid call 200 with {"ok": true}
An unknown or paused heartbeat 404 with {"ok": false, "msg": "Monitor not found or not active."}
Anything else after the token 404 with Unknown heartbeat path "/xyz". Use /fail or /<exit-code>.
A negative or unrealistic ping value 404 with “Invalid ping value”
Too many calls 429 with {"ok": false, "msg": "Too many requests"}

Each heartbeat accepts up to 60 calls a minute, and each IP address up to 600.

In a heartbeat’s health timeline and in the uptime figure on its card, uptime is measured by time, not by counting calls: the time it was up, out of the time its state was known. Time while it’s paused, and time before its first call, count as neither up nor down. The availability table on its page counts outages instead, as for every check. See Availability.

In the health timeline, each bar covers a stretch of time:

  • A bar is red if the heartbeat was down at any point in it, and green if it was up for all the time its state was known. An hour in which it went down and came back is red; the hours after it recovered are green.
  • An hour with no call in it still shows the heartbeat’s state. Its tooltip says “Between calls (expected every 1d)”, or “No call arrived (expected every 1d)” while the heartbeat is down, and its Uptime is the share of that hour it was up.

Other checks, and tunnels, count their results instead. They run on a fixed schedule, so counting results and measuring time give nearly the same figure.

In the REST API, POST /api/v1/servers with "type": "push" creates a heartbeat:

Field Details
name Required.
period The longest time between two calls from your job, in seconds. Default 86400 (1 day), from 60 to 604800 (7 days). Below your plan’s shortest period, the answer is 403 “The shortest period on your plan is N seconds (requested M).”
grace How late a call can be before the heartbeat goes down, in seconds. Default 3600 (1 hour), from 0 to 604800.
tags, active Optional, as for other checks.

The answer includes period, grace, and heartbeat_url, the address your job calls, in the form <your dashboard address>/api/push/<token>. GET /api/v1/servers/:id and PUT /api/v1/servers/:id return it too, but list rows never do, and it’s only returned to people who can edit monitors (Owner, Admin, or Manager). Anyone who has the address can report the job up or down, so treat it as a secret.

A heartbeat has no target, so url, host, port, keyword, domain, and accepted_statuscodes are refused, as are an ip_version other than null and an ssl_expiration or domain_expiration other than 0. The settings other checks use for timing are refused with a pointer to the heartbeat’s own:

Field Refused with
interval “A heartbeat’s interval is its period: send period (seconds) instead.”
timeout “timeout is not for heartbeats: nothing is requested.”
maxretries “maxretries is not for heartbeats: grace is how long to wait before it is down.”

PUT /api/v1/servers/:id changes a heartbeat’s period, grace, name, tags, and active (pause and resume). Its address never changes.