Heartbeat monitoring for jobs that fail by going quiet.

Heartbeat monitoring is a check where the job reports in, rather than the monitor probing the job. Each run ends with one small HTTP request. The monitor knows when the next one is due, and if it does not arrive inside a grace window, that silence becomes an alert. It is the only kind of monitoring that catches a job that never started, which is how cron jobs, queue workers and scheduled AI agents usually fail.

What heartbeat monitoring is, and what it is for.

Uptime monitoring calls your service every minute and alerts when the call fails. That works for something that is always on. A scheduled job is not listening for requests, so there is nothing to probe. Heartbeat monitoring inverts the direction: the job calls in, and the gap is the signal.

Where it fits

  • Cron jobs and scheduled scripts. The nightly report, the hourly sync, the backup. The cron entry that was removed in a refactor never raises an exception; it just stops existing.
  • Queue workers and consumers. A worker that died after a deploy, or is stuck on one long job from yesterday, leaves the queue growing while every dashboard reads green.
  • Scheduled AI agents. The enrichment agent, the daily summary, the triage bot on a timer. These fail the same way as cron, and when they do run, you also want to know what they cost and whether the output was any good.
  • No-code automations. n8n, Make and Zapier flows can fire an HTTP request as their last step, which is all a heartbeat needs.

Where it does not

A web service or API that answers requests all day wants an uptime check, not a heartbeat. A request-driven agent with no schedule wants run monitoring: every run reports its status and a failed run alerts, but there is no "due" time to miss. Agent Ping does both; this page is about the scheduled case.

How it works: four parts.

A ping, a schedule, a grace window and a sweep. Everything else is a detail of what the ping carries.

1. The ping

The job ends by sending one request with the agent name and a status. ok is the default; fail and timeout are the other two. A job that can run for a long time sends start first and ok or fail at the end, and the duration is the gap between them.

bashone heartbeat
curl 'https://eu.ingest.agentping.io/v1/ping?key=ping_eu_<token>&agent=daily-summary&status=ok'

The token in the URL is a ping token, scoped to one agent and only able to send heartbeats. A leaked ping token can falsify one job's status; a leaked API key would be a full team compromise, so never put one in a URL.

2. The schedule

You tell the monitor when the job is meant to fire, in the same cron syntax the job uses. The monitor computes the next expected time after each run. A job with no schedule can still send heartbeats, and its failures still alert, but nothing watches for its absence.

3. The grace window

Scheduled jobs do not fire to the second. The grace window is how many minutes past the expected time the monitor waits before calling the run overdue. Set it from the job's real jitter: a nightly batch that sometimes starts 20 minutes late wants 30 to 45 minutes. A window that is too short teaches the team to ignore the channel, which is worse than no monitor.

4. The sweep and the alert

A sweep runs every minute. When an agent passes its window with no run received, an overdue alert goes to every enabled channel with the agent, how many minutes it is overdue and when a run was last seen. You get one alert per outage, not one per minute: the state is remembered until a run lands again, and then a recovery notice goes out and the slate is clean for the next incident.

POST · your webhookagent.overdue
{
  "event": "agent.overdue",
  "subject": "daily-summary is overdue",
  "body": "No run received for 31 minutes past the expected window.",
  "agent": { "slug": "daily-summary", "id": "agt_…" },
  "run": null,
  "last_seen_at": "2026-10-22T09:00:42Z",
  "minutes_overdue": 31,
  "url": "https://agentping.io/agents/agt_…"
}

Slack and Teams get formatted message blocks, PagerDuty gets its own event shape, email gets a digest. The same alert goes to whichever channels the agent has enabled.

Setup, one line per runtime.

Create the agent, copy its ping token, set the schedule and grace window, and add the ping to the job. The success and failure pings target the same agent.

Cron

Cron swallows every exit code, so fire the success ping only when the script exits zero and the failure ping only when it does not. The trailing redirect keeps cron's email-on-output behaviour quiet.

cronsuccess and failure pings
0 9 * * *  /path/to/daily-summary.sh \
  && curl -fsS 'https://eu.ingest.agentping.io/v1/ping?key=ping_eu_<token>&agent=daily-summary&status=ok' > /dev/null 2>&1 \
  || curl -fsS 'https://eu.ingest.agentping.io/v1/ping?key=ping_eu_<token>&agent=daily-summary&status=fail' > /dev/null 2>&1

GitHub Actions, n8n, Make, Zapier

Add an HTTP request as the final step and a second one on the failure path. In GitHub Actions that is a step with if: failure(); in n8n it is an HTTP Request node on the error output. The GitHub Actions, n8n, Make and Zapier recipes have the exact node settings.

Python, TypeScript, Go, PHP

Anything with an SDK can send the same heartbeat in one call, with the cost and duration attached, and upgrade to full run tracing later without migrating anything.

pythonheartbeat from a script
import agentping

agentping.init(api_key="apk_…")
agentping.heartbeat("daily-summary", status="ok", cost_usd=0.084, duration_ms=41_892)

What a heartbeat can carry for an AI job.

Presence is the minimum. A scheduled AI agent has three more questions worth answering on the same request: what it cost, how long it took, and whether the output was any good.

  • Cost. Send cost_usd if the job knows it, or send provider, model, input_tokens and output_tokens from the response's usage block and Agent Ping prices the run from its rate card. Spend then rolls up per agent, per customer and per month, with a budget alert before the invoice.
  • Duration. A start ping followed by ok or fail gives every run a duration, so a job that is slowing down week over week is visible before it starts missing its window.
  • Status and history. Every heartbeat is a run with a status. Failed runs count against the agent's health, show up on its status page, and can fire an alert rule of their own, separate from the overdue alert.
  • Output. The JSON heartbeat endpoint accepts an output field. Attach the result and a Verify rubric scores it, so a job that runs on time and produces rubbish is caught too.
basha heartbeat with tokens, priced server side
curl 'https://eu.ingest.agentping.io/v1/ping?key=ping_eu_<token>&agent=daily-summary&status=ok&provider=openai&model=gpt-5&input_tokens=1240&output_tokens=380'

Against a generic cron monitor, honestly.

Cronitor, Healthchecks.io and Dead Man's Snitch do heartbeat monitoring well, and the ping is the same one line. If all you have is cron jobs, any of them is a fine choice.

The difference is what the heartbeat is attached to. A cron monitor records that the job ran. Agent Ping records a run of an agent, with cost, duration, status, optional output and a quality score, in the same dashboard as agents that report through an SDK, OpenTelemetry or a framework hook. When the overdue alert fires, the on-call engineer sees the last successful run and what it cost, and when the job is an AI agent, whether its recent output was drifting before it went quiet. That is the case for using one tool for the fleet instead of a cron monitor for the schedules and something else for the agents.

What is heartbeat monitoring?
Heartbeat monitoring is a check where the job reports to the monitor, rather than the monitor probing the job. Each run ends by sending a small HTTP request, the heartbeat. The monitor knows when the next one is due, and if it does not arrive inside a grace window, the silence becomes an alert. It is the only kind of monitoring that catches a job that never started.
How is it different from uptime monitoring?
Uptime monitoring calls your service every minute and alerts when the call fails. That works for something that is always on, like a web server. A cron job, a queue worker or a scheduled AI agent is not listening for requests, so there is nothing to probe. Heartbeat monitoring inverts the direction: the job calls in, and the gap is the signal.
What should the grace window be?
Set it from the job's real jitter, not from hope. A nightly batch that sometimes starts 20 minutes late wants a 30 to 45 minute window. A five-minute window on that job teaches the team to ignore the channel. For a job that runs every 10 minutes, 5 minutes is usually right.
How many alerts do I get when a job is down for a week?
One. The alert state is remembered until a run lands again, so an outage is one overdue alert and one recovery notice, not a page every minute. If the job also sends a failure heartbeat, each failed run is counted against the agent and can fire its own alert rule.
Can a heartbeat carry the cost of an AI job?
Yes. Send cost_usd on the ping, or send the model and token counts and Agent Ping prices the run from its rate card. The heartbeat then reports presence, status, duration and spend in one request, and the job shows up in the same cost, uptime and quality views as agents instrumented with an SDK.
Is this the same as Cronitor or Healthchecks.io?
The ping is the same idea and the setup is the same one line. The difference is what the heartbeat is attached to. A generic cron monitor records that the job ran. Agent Ping treats the heartbeat as a run of an agent, with cost, duration, status and optionally output and a quality score, in the same place as the agents that report through an SDK.

Add a heartbeat to your first job in a minute.

Create an agent, copy its ping token, paste one line into the job. The free plan has no card and no time limit.

Pulse: AI agent monitoring → Heartbeat docs → Cron recipe → Your AI agent stopped running →