Heartbeat Monitoring
Heartbeats are passive monitors for cron jobs, scheduled workers, and batch pipelines. Instead of Upwatch probing your service, your job pings Upwatch when it completes. Silence past the expected interval triggers an alert.
Dead-man's switch
If your cron job stops checking in, we page you. No agents. No polling. Just a curl at the end of your script.
Flexible intervals
From 60 seconds to 7 days. Set a grace period to absorb natural jitter. Free plan: 10 heartbeats. Pro: unlimited.
Quick Start
Create a heartbeat
In your dashboard, go to Heartbeats and click New Heartbeat. Give it a name (e.g. "Nightly DB backup"), set the expected interval, and optionally a grace period.
Copy your ping URL
After creation, you'll see your unique ping URL:
Append the ping to your job
Add the curl call at the end of your cron job or script:
0 3 * * * /opt/scripts/backup.sh && curl -fsS https://upwatch.dev/ping/TOKEN > /dev/null
Ping Endpoints
All three HTTP methods are accepted on the success endpoint, so you can use whatever is most convenient for your runtime.
/ping/{token}Records a successful check-in. Resets the incident timer. If an active incident was open, it resolves automatically and sends an "all clear" alert.
/ping/{token}/failExplicitly reports a failure. Opens an incident immediately without waiting for the grace period. Use this when your script detects an error and can report it proactively.
/opt/scripts/backup.sh
if [ $? -ne 0 ]; then
curl -fsS -X POST https://upwatch.dev/ping/TOKEN/fail
exit 1
fi
curl -fsS https://upwatch.dev/ping/TOKEN
Note: The ping URL does not require authentication. Treat it like a secret — don't commit it to public repos. Rotate it by deleting and recreating the heartbeat if compromised.
Code Examples
crontab
0 3 * * * /opt/scripts/backup.sh && curl -fsS https://upwatch.dev/ping/TOKEN >/dev/null
# Use -m 10 to enforce a 10-second timeout on the ping itself
0 3 * * * /opt/scripts/backup.sh && curl -fsS -m 10 https://upwatch.dev/ping/TOKEN >/dev/null
Python
def run_backup():
# ... your backup logic ...
pass
try:
run_backup()
urllib.request.urlopen("https://upwatch.dev/ping/TOKEN", timeout=10)
except Exception as e:
urllib.request.urlopen("https://upwatch.dev/ping/TOKEN/fail", timeout=10)
raise
GitHub Actions
scheduled-task:
runs-on: ubuntu-latest
steps:
- name: Run backup
run: ./scripts/backup.sh
- name: Ping heartbeat
if: success()
run: curl -fsS https://upwatch.dev/ping/${{ secrets.UPWATCH_HB_TOKEN }}
Store the token in a GitHub Actions secret to avoid exposing it in logs.
wget
Intervals & Grace Periods
Expected Interval
How often your job should check in. Range: 60 seconds to 7 days. If no ping arrives within this window after the last ping (or creation for new heartbeats), the grace period begins.
Grace Period
Extra buffer beyond the interval before an incident opens. A 5-minute grace on an hourly job means we wait until 65 minutes before alerting. Use this to absorb natural jitter from system load or network latency.
When does the clock start?
For a new heartbeat that has never been pinged, the interval is measured from the heartbeat's creation time. Once it receives its first ping, the clock resets to that ping's timestamp and runs from there.
Best Practices
Ping on success, not on start
Use job.sh && curl /ping/TOKEN so the ping only fires when the job actually completes without error.
Add -m 10 to curl
A network hiccup on the ping call shouldn't hang your job indefinitely. -m 10 enforces a 10-second max wait.
Store the token as a secret or env var
Don't hardcode it in scripts committed to public repos. Use UPWATCH_HB_TOKEN in your environment or a secrets manager.
Use one heartbeat per job, not per machine
If you run the same backup on three servers, create three heartbeats. Shared tokens make it impossible to tell which machine stopped checking in.
Set grace = natural jitter × 2
If your job usually takes 5–10 minutes, set a 20-minute grace. You want alerts for real failures, not for load-induced delays.
FAQ
Does the ping URL expire?
No. Tokens are permanent until you delete the heartbeat. If you suspect a token has been compromised, delete the heartbeat and create a new one.
What's the rate limit on pings?
120 requests per token per 60 seconds. This is more than enough for any reasonable job frequency and is designed to absorb retry loops.
Can I use GET or only POST?
Both GET and POST (and HEAD) are accepted on the success endpoint. Use whatever your runtime supports most easily. The /fail endpoint only accepts POST.
How is heartbeat monitoring different from HTTP monitoring?
HTTP monitors poll a URL on a schedule — they test if something is reachable. Heartbeat monitors wait for a ping — they test if something ran. You need both for complete coverage: HTTP for services that should always be up, heartbeats for jobs that should run periodically.