Heartbeat
Did the scheduled job actually run? The monitor that waits to be told, with copy-paste examples.
Every other monitor goes out and looks. A heartbeat sits and waits: your job calls a URL when it finishes, and if the call does not arrive on time, the monitor goes down.
Use it when
Anything runs on a schedule and failing silently would be bad: backups, cron jobs, ETL runs, certificate renewal, nightly reports, queue drains.
This is the monitor that catches the backup which stopped working in March and was noticed in September.
It is also the way to monitor something we cannot reach — a job inside a private network can call out to us even though we can never call in.
What to enter
| Field | Notes |
|---|---|
| Schedule | Every N minutes/hours/days, or a cron expression. |
| Timezone | For cron schedules, so "3am" means your 3am. |
| Grace period | How late is acceptable before it counts as missed. |
Set the grace period to cover how long the job legitimately takes to vary. A backup that normally finishes at 02:10 but occasionally at 02:40 needs at least 30 minutes of grace, or you will be paged for a slow Tuesday.
The ping URL
Each heartbeat has its own URL, shown on the monitor's page:
https://.../h/<token>
Call it when the job succeeds. GET or POST, either is fine.
| URL | Meaning |
|---|---|
/h/<token> | Success. The job finished. |
/h/<token>/start | The job started. Optional; it is what gives you a duration. |
/h/<token>/fail | The job failed. Goes down immediately. |
/h/<token>/<exit code> | 0 is success, anything from 1 to 255 is a failure. |
The exit-code form is what makes the shell idiom work, because $? is already the number you want.
Exactly when it's DOWN
- No ping arrives within the schedule plus the grace period.
- A
/failping arrives. - A ping with a non-zero exit code arrives.
The failure threshold is fixed at 1: one missed run is the event you wanted to hear about. There are no regions, and no latency alerts.
Examples
crontab — report the real exit status:
0 2 * * * /usr/local/bin/backup.sh; curl -fsS -m 10 https://.../h/<token>/$?
bash, with a start marker so you get a duration:
curl -fsS -m 10 https://.../h/<token>/start
./run-import.sh
curl -fsS -m 10 "https://.../h/<token>/$?"
GitHub Actions:
- name: Report to UptimeCraft
if: always()
run: curl -fsS -m 10 "https://.../h/${{ secrets.HEARTBEAT_TOKEN }}/${{ job.status == 'success' && '0' || '1' }}"
Python:
import urllib.request
urllib.request.urlopen("https://.../h/<token>", timeout=10)
PowerShell:
Invoke-RestMethod -Uri "https://.../h/<token>" -TimeoutSec 10
Note -m 10 and the timeouts: a monitoring call that hangs must not hold up the job it is monitoring.
Sending output with the ping
POST up to 2 KiB of text and it is kept with the ping — the tail of a log, a row count, which file was processed. Anything beyond 2 KiB is cut off.
./backup.sh 2>&1 | tail -c 2000 | curl -fsS -m 10 --data-binary @- https://.../h/<token>/$?
Limits
- 60 pings a minute per token. Ample for scheduled work; it is there so a loop calling the URL cannot flood us.
- Keep the token secret: anyone with it can report your job as healthy. If it leaks, edit the monitor to issue a new one.
- We record the ping that arrives. If the job lies — reporting success from a wrapper that did not check the real result — we believe it. Report
$?, not a hardcoded zero.
Last updated 4 October 2026
Still stuck?
If this did not answer your question, tell us and we will fix the page as well as answer you.