Sending heartbeats
The ping URL, what you can send with a heartbeat, and what LastSeen answers.
Each heartbeat monitor has its own ping URL, made from its key:
https://ping.lastseen.io/YOUR-KEY
A request to it, GET or POST, is a heartbeat. The key is the only credential, so there's nothing else to send: no headers, no account ID.
Signals #
| URL | What it says |
|---|---|
/YOUR-KEY | I'm alive. |
/YOUR-KEY/fail | Something's wrong. The monitor goes down at once, and you're alerted. |
/YOUR-KEY/start | I've just started (a reboot, a job beginning). It's noted in the monitor's history; the status doesn't change. |
A heartbeat after a /fail brings the monitor back up, with a recovery alert.
Sending a reading #
A heartbeat can carry a value: a number or a short piece of text. The monitor's page graphs numbers, and value rules can alert on them.
With GET, in the query string:
curl "https://ping.lastseen.io/YOUR-KEY?v=21.5"
With POST, as JSON:
curl -X POST https://ping.lastseen.io/YOUR-KEY -H "Content-Type: application/json" \
-d '{"v": 21.5, "seq": 1042, "up": 86400}'
| Field | Meaning |
|---|---|
v (or value) | The reading: a number, text or true/false. Up to 64 bytes on the Free plan. |
seq | A counter that goes up by one each heartbeat. Gaps show as lost heartbeats; a reset shows as a restart. |
up | Seconds since the device started. A drop shows as a restart. |
Any other fields in a JSON body are kept with the reading. A body can be at most 1024 bytes.
Values #
On a monitor's page, value rules alert on what a monitor reports, not just on silence: num > 30, value == "ERROR", or "hasn't changed for 45 minutes". Each rule can require several readings in a row before it trips, so one noisy sample doesn't wake anyone. Value alerts go to the same channels and follow the same snooze as down alerts.
What LastSeen answers #
| Status | Body | Meaning |
|---|---|---|
200 | ok | Recorded. |
200 | ok, payload ignored: … | The heartbeat counted; the reading didn't (malformed, or too long for the plan). |
404 | unknown ping key | No monitor has this key: a typo, or the key was replaced. |
413 | body over 1024 bytes | The body is too big. |
429 | rate limited | Too many pings; Retry-After says how many seconds to wait. |
503 | temporarily unavailable, retry | Our side; try again shortly. |
Rate limits #
Each monitor may send 2 heartbeats a minute, with bursts of up to 5, which is why the shortest interval is 30s. /fail and /start have their own allowance of 1 a minute (bursts of 3), so a chatty device can't use up the one message that matters most.
From a cron job #
Ping at the end of the job, so the heartbeat means it finished:
0 2 * * * /usr/local/bin/backup.sh && curl -fsS -m 10 --retry 3 https://ping.lastseen.io/YOUR-KEY > /dev/null
To report failures as well, wrap the job with our script. It runs the job, then sends a heartbeat if it succeeded, or /fail with the exit status if it didn't (which alerts at once). The job's own exit status is passed on:
curl -fsSo /usr/local/bin/lastseen-run https://app.lastseen.io/examples/lastseen-run.sh && chmod +x /usr/local/bin/lastseen-run
0 2 * * * /usr/local/bin/lastseen-run YOUR-KEY /usr/local/bin/backup.sh
For a job that runs at set times rather than every few minutes, watch it on its cron schedule.
From code #
Python, with only the standard library:
import urllib.request
urllib.request.urlopen("https://ping.lastseen.io/YOUR-KEY?v=21.5", timeout=10)
A fuller example, with retries, is at app.lastseen.io/examples/heartbeat.py. For ESP32 and ESP8266, the LastSeen Arduino library handles retries, seq and up for you.
HTTPS or plain HTTP #
ping.lastseen.io answers on both https:// and plain http://. Use HTTPS wherever you can. Plain HTTP is there for small devices like the ESP8266, for which TLS costs too much memory; the Arduino library uses it.
Keys #
The key is 26 characters, and LastSeen stores it hashed. Admins can show it again on the monitor's page. Rotate key on the monitor's page makes a new one and stops the old one at once; the monitor's history notes who rotated it.