Ping API
The single HTTP endpoint your jobs call, and what every response means.
curl https://api.drumbeats.io/v1/ping/<monitor-id>/successcurl https://api.drumbeats.io/v1/ping/<monitor-id>/success{
"ok": true,
"ping_id": "8f14e45f-ceea-467a-9f4c-1b2d3e4f5a6b",
"event": "SUCCESS",
"timestamp": "2026-08-01T02:00:03.481Z"
}{
"ok": true,
"ping_id": "8f14e45f-ceea-467a-9f4c-1b2d3e4f5a6b",
"event": "SUCCESS",
"timestamp": "2026-08-01T02:00:03.481Z"
}Every monitored job calls this API. There is no SDK, no authentication header, and no request signing. The monitor ID in the URL is the credential, so treat the URL as a secret.
Two URL forms#
https://api.drumbeats.io/v1/ping/<monitor-id>/<event>https://api.drumbeats.io/v1/ping/<monitor-id>/<event>https://api.drumbeats.io/v1/s-ping/<project-id>/<monitor-slug>/<event>https://api.drumbeats.io/v1/s-ping/<project-id>/<monitor-slug>/<event>The slug form lets one script run against several projects. Keep the monitor slug identical in staging and production, swap the project ID per environment, and the job code never changes.
Copy either form from the URLs panel on the monitor's detail page. Retyping a UUID by hand is the most common cause of a monitor that stays silent.
The four events#
| Event | URL suffix | What it means | Effect on the monitor |
|---|---|---|---|
start | /start | The job began | Opens a run. Enables duration tracking and hung-run detection. Required on Event-driven monitors |
success | /success | The job finished cleanly | Closes the run, resolves any open incident, flips the monitor UP |
failure | /failure | The job failed | Closes the run, increments the failure counter, opens an incident once failure_tolerance is reached |
log | /log | A progress note | Stored on the run timeline. Changes nothing about status |
| Exit code | /0, /1, … /255 | Shell shorthand | 0 behaves as success, anything else as failure, with the exact code kept for triage |
log never opens or closes an incident. Use it to record which phase a job reached, so a hung run tells you where it stopped.
GET or POST#
Both work on every event. GET is what most cron integrations use. Reach for POST when you want to attach a payload, a run_id, or a measured duration.
curl -sf https://api.drumbeats.io/v1/ping/<monitor-id>/successcurl -sf https://api.drumbeats.io/v1/ping/<monitor-id>/successcurl -X POST https://api.drumbeats.io/v1/ping/<monitor-id>/failure \
-H "Content-Type: application/json" \
-d '{"run_id": "job-123", "payload": "Connection refused on line 42"}'curl -X POST https://api.drumbeats.io/v1/ping/<monitor-id>/failure \
-H "Content-Type: application/json" \
-d '{"run_id": "job-123", "payload": "Connection refused on line 42"}'python job.py 2>&1 | curl -X POST https://api.drumbeats.io/v1/ping/<monitor-id>/success \
-H "Content-Type: text/plain" --data-binary @-python job.py 2>&1 | curl -X POST https://api.drumbeats.io/v1/ping/<monitor-id>/success \
-H "Content-Type: text/plain" --data-binary @-Parameters#
| Parameter | Where | Limit | What it does |
|---|---|---|---|
run_id | Query string or POST body | 255 characters | Ties start, log, and the finish ping into one run. Required on Event-driven monitors |
payload | POST body | 100 KB Free, 1 MB Pro, 2 MB Business | Stdout, an error message, or structured JSON. Rendered on the run timeline |
duration_ms | POST body | none | Report the duration yourself. Otherwise Drumbeats computes it from start to finish when both share a run_id |
Read the response#
A recorded ping returns 200 with ok: true, the ping ID, the event Drumbeats stored, and the receive timestamp. Exit-code pings also echo back exit_code.
Errors carry a machine-readable code alongside the message, so a wrapper script can branch on the reason without parsing English:
{
"ok": false,
"status": "error",
"error": "Too many pings. Please slow down.",
"message": "Too many pings. Please slow down.",
"code": "RATE_LIMITED"
}{
"ok": false,
"status": "error",
"error": "Too many pings. Please slow down.",
"message": "Too many pings. Please slow down.",
"code": "RATE_LIMITED"
}| HTTP | code | What it means | What to do |
|---|---|---|---|
200 | MONITOR_PAUSED | The monitor is paused. The ping was not recorded | Nothing. It answers 200 so curl -f does not fail your job |
404 | MONITOR_NOT_FOUND | Wrong monitor ID, or the monitor was deleted | Copy the URL again from the dashboard |
400 | INVALID_EVENT | The URL suffix is not a known event | Use start, success, failure, log, or a number |
400 | INVALID_EXIT_CODE | The exit code is outside 0 to 255 | Clamp the value before sending |
429 | RATE_LIMITED | More than 600 pings for this monitor in one minute | Back off and retry. This clears on its own |
429 | QUOTA_EXCEEDED | The account is past its plan allowance | Retrying will never clear this. See beats and usage |
500 | PING_FAILED | Drumbeats failed to record the ping | Retry with exponential backoff |
Rate limits#
The ping API accepts 600 pings per monitor per minute, roughly 10 per second sustained. That covers around 120 concurrent runs each emitting start, a few log events, and a finish. The limit is per monitor, so a busy job cannot starve the rest of your project.
What happens when a ping never lands#
A ping that never reaches Drumbeats is indistinguishable from a job that never ran. That is the point of the design, and it is also its sharpest edge.
For a Cron or Heartbeat monitor, a dropped ping means the window closes empty and you get a MISSED incident for a job that actually succeeded. For an Event-driven monitor, a dropped finish ping means the run stays open and gets recorded as hung at max_duration_seconds.
Both are false alarms caused by your network, not by your job. Production hardening covers the retry and timeout patterns that keep them rare, and explains why the ping call should never be able to fail the job it is watching.
Next#
Scheduled pings for Cron and Heartbeat patterns. Event-driven pings for run_id correlation across concurrent workers. Payloads for attaching context. Exit codes for the shell shorthand.