Ping API

The single HTTP endpoint your jobs call, and what every response means.

bash
curl https://api.drumbeats.io/v1/ping/<monitor-id>/success
curl https://api.drumbeats.io/v1/ping/<monitor-id>/success
json
{
  "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#

By monitor UUID
https://api.drumbeats.io/v1/ping/<monitor-id>/<event>
https://api.drumbeats.io/v1/ping/<monitor-id>/<event>
By project and monitor slug
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#

EventURL suffixWhat it meansEffect on the monitor
start/startThe job beganOpens a run. Enables duration tracking and hung-run detection. Required on Event-driven monitors
success/successThe job finished cleanlyCloses the run, resolves any open incident, flips the monitor UP
failure/failureThe job failedCloses the run, increments the failure counter, opens an incident once failure_tolerance is reached
log/logA progress noteStored on the run timeline. Changes nothing about status
Exit code/0, /1, … /255Shell shorthand0 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.

GET, the minimum
curl -sf https://api.drumbeats.io/v1/ping/<monitor-id>/success
curl -sf https://api.drumbeats.io/v1/ping/<monitor-id>/success
POST with JSON
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"}'
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"}'
POST piping stdout straight in
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#

ParameterWhereLimitWhat it does
run_idQuery string or POST body255 charactersTies start, log, and the finish ping into one run. Required on Event-driven monitors
payloadPOST body100 KB Free, 1 MB Pro, 2 MB BusinessStdout, an error message, or structured JSON. Rendered on the run timeline
duration_msPOST bodynoneReport 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:

json
{
  "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"
}
HTTPcodeWhat it meansWhat to do
200MONITOR_PAUSEDThe monitor is paused. The ping was not recordedNothing. It answers 200 so curl -f does not fail your job
404MONITOR_NOT_FOUNDWrong monitor ID, or the monitor was deletedCopy the URL again from the dashboard
400INVALID_EVENTThe URL suffix is not a known eventUse start, success, failure, log, or a number
400INVALID_EXIT_CODEThe exit code is outside 0 to 255Clamp the value before sending
429RATE_LIMITEDMore than 600 pings for this monitor in one minuteBack off and retry. This clears on its own
429QUOTA_EXCEEDEDThe account is past its plan allowanceRetrying will never clear this. See beats and usage
500PING_FAILEDDrumbeats failed to record the pingRetry 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.