Know the moment your cron job dies.

One HTTP ping per run. Miss a beat and we email, Telegram, Slack, Discord, or webhook you within seconds. Built for PHP shops, but every language that can curl works the same.

No credit card 5-minute setup Open API, no SDK required
crontab
$ 0 * * * *  /usr/bin/php /app/bin/console hourly:rollup && \
            curl -fsS -m 10 --retry 5 https://cronheart.com/ping/<uuid>
If the script crashes, the curl never fires — we alert you within 30 seconds.
How it works

Three steps. No agents, no daemons.

Add one curl to your cron line. We watch the schedule and page you when something dies.

Curl on success

Your job pings /ping/<uuid> after a successful run. POST stdout if you want it captured.

We watch the clock

Every 30 seconds the scanner compares "next expected" against now. Past the grace window we mark you late.

Alerts fan out

Email, Telegram, Slack, Discord, signed webhook — whatever you wired up to the project, in parallel.

Features

Everything an SRE expects.
Nothing they don't.

Drop-in HTTP API

One POST /ping/<uuid> per run, with optional /start and /fail for duration and error tracking. Captures up to 10KB of stdout per run.

Cron, interval, simple

Validate any cron expression with a live "next 5 runs" preview. Or use plain intervals — "every 6 hours" — when you don't need the full grammar.

Multi-channel alerts

Email, Telegram, Slack, Discord, signed webhooks. Anti-flap dedupe so a bad run doesn't page you ten times before the recovery clears it.

Status badges

Drop a shields.io-style SVG into your README to show the world your nightly-rollup hasn't flatlined since 2024.

Timezones, done right

Schedules stored in UTC, displayed in your timezone, DST-safe. We've put the corner cases through unit tests so you don't have to.

Drop-in HTTP ping

One curl per run from your cron, systemd timer, Symfony Scheduler, or Laravel Scheduler. Five-minute setup, no SDK required.

Built for PHP, works for everything

Wire it up in any language

If it can curl, it can ping Cronheart.

UUID=your-monitor-uuid
BASE=https://cronheart.com/ping/$UUID
curl -fsS -m 10 --retry 5 "$BASE/start"
if /usr/bin/php bin/console hourly:rollup; then
    curl -fsS -m 10 --retry 5 "$BASE/success"
else
    curl -fsS -m 10 --retry 5 "$BASE/fail"
fi
$base = "https://cronheart.com/ping/{$uuid}";
file_get_contents("{$base}/start");
try {
    $app->run();
    file_get_contents("{$base}/success");
} catch (\Throwable $e) {
    $ctx = ['http' => ['method' => 'POST', 'content' => $e->getMessage()]];
    file_get_contents("{$base}/fail", false, stream_context_create($ctx));
    throw $e;
}
import requests, sys
base = f"https://cronheart.com/ping/{uuid}"
requests.get(f"{base}/start", timeout=5)
try:
    run_my_job()
    requests.get(f"{base}/success", timeout=5)
except Exception as e:
    requests.post(f"{base}/fail", data=str(e), timeout=5)
    sys.exit(1)
const base = `https://cronheart.com/ping/${uuid}`;
await fetch(`${base}/start`);
try {
    await runJob();
    await fetch(`${base}/success`);
} catch (err) {
    await fetch(`${base}/fail`, { method: 'POST', body: String(err) });
    throw err;
}
Pricing

Simple, predictable pricing

Free for 20 monitors · $5/mo for 50 · $19/mo for 200 · $49/mo for 1000.
No card on the free tier. Same product on every tier — we only meter monitor count.

See the full comparison

Stop discovering broken crons from angry users.

Two minutes to your first monitor, zero dependencies installed on your servers.