> ## Documentation Index
> Fetch the complete documentation index at: https://docs.uptimeio.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Heartbeat Monitoring

> Get alerted when a cron job, backup or scheduled task stops checking in

Heartbeat monitors work the other way round from other monitor types: **your job pings UptimeIO**, and UptimeIO alerts you when a ping does not arrive on time. Use them for anything that runs on a schedule.

## When to use it

* Cron jobs and scheduled tasks
* Backups
* ETL and data-sync pipelines
* Batch jobs and queue workers that should report in regularly

## Create a heartbeat monitor

<Steps>
  <Step title="Create the monitor">
    Choose the **Heartbeat** type, then set how often your job runs (the expected interval) and how much lateness to tolerate (the grace period).
  </Step>

  <Step title="Copy the heartbeat URL">
    UptimeIO generates a unique URL for the monitor:

    ```
    https://heartbeat.uptimeio.com/heartbeat/hb_a1b2c3d4e5f6...
    ```

    The API builds the same URL on the `api.uptimeio.com` host (`https://api.uptimeio.com/heartbeat/{token}`). Both hostnames reach the same endpoint and behave identically.
  </Step>

  <Step title="Ping it from your job">
    Request the URL when the job finishes successfully.
  </Step>
</Steps>

### Settings

| Field (API) | Description | Constraints |
| - | - | - |
| `name` | Monitor name | Required, up to 80 characters |
| `type` | `HEARTBEAT` | Required |
| `heartbeat_config.expected_interval_seconds` | How often your job should ping | Required, at least 60. Your plan's minimum interval also applies: 300 on Free, 60 on Pro and Scale |
| `heartbeat_config.grace_period_seconds` | Extra time allowed after the interval | Required, at most 3,600 (1 hour) |
| `monitoring_regions` | Required by the API; send `[]` | Heartbeats are not probed |

The target and token are generated for you. You do not send a `target`.

### Example

```bash theme={null}
curl -X POST https://api.uptimeio.com/api/monitors \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Nightly backup",
    "type": "HEARTBEAT",
    "interval_seconds": 86400,
    "monitoring_regions": [],
    "heartbeat_config": {
      "expected_interval_seconds": 86400,
      "grace_period_seconds": 1800
    }
  }'
```

## Sending a heartbeat

The heartbeat URL needs **no authentication**: the token in the URL is the credential. Send `GET` or `POST`:

```bash theme={null}
# Simple ping
curl -fsS https://heartbeat.uptimeio.com/heartbeat/YOUR_HEARTBEAT_TOKEN

# Ping with metadata (POST)
curl -fsS -X POST https://heartbeat.uptimeio.com/heartbeat/YOUR_HEARTBEAT_TOKEN \
  -H "Content-Type: application/json" \
  -d '{ "metadata": { "duration_s": 42, "rows": 1800, "status": "ok" } }'
```

Success returns `200`:

```json theme={null}
{
  "success": true,
  "data": { "message": "Heartbeat received", "timestamp": 1790000000000 }
}
```

### Metadata (POST only)

Optional `metadata` object in the JSON body:

* Up to 10 keys; keys use letters, digits, `_` and `-` (1 to 100 characters)
* Values are strings (up to 500 characters), numbers or booleans
* About 2 KB in total; the request body is limited to 20 KB

Invalid metadata is rejected with `400 INVALID_METADATA`. A body that is not valid JSON is ignored and the ping is still recorded.

### Rate limits

| Limit | Applies to |
| - | - |
| 3 requests per 30 seconds per heartbeat URL | All plans |
| 1 request per 120 seconds per heartbeat URL | Free plan, in addition |

Exceeding a limit returns `429 RATE_LIMIT_EXCEEDED` with a `Retry-After` header. A monitor rarely needs more than one ping per interval.

### Errors

| Status | Code | Meaning |
| - | - | - |
| `400` | `INVALID_TOKEN` | The token in the URL is malformed. Tokens start with `hb_`. |
| `404` | `INVALID_TOKEN` | The token is well-formed but no monitor uses it. |
| `400` | `INVALID_METADATA` | The `metadata` value failed validation. |
| `429` | `RATE_LIMIT_EXCEEDED` | Too many pings. Wait for `Retry-After` seconds. |

## When incidents open and close

* **Open**: no ping arrives within the expected interval plus the grace period. Overdue monitors are checked every 30 seconds. With an interval of 1 hour and a grace period of 5 minutes, the incident opens after 65 minutes of silence.
* **Resolve**: after **3 consecutive on-time pings**, which avoids false recoveries from an unstable job.

## Integration examples

```bash theme={null}
# crontab: nightly at 02:00, ping only if the script succeeds
0 2 * * * /opt/scripts/backup.sh && curl -fsS -m 10 --retry 3 https://heartbeat.uptimeio.com/heartbeat/YOUR_HEARTBEAT_TOKEN > /dev/null
```

```bash theme={null}
#!/bin/bash
# backup.sh - ping only when the backup succeeds
pg_dump mydb > backup.sql && curl -fsS https://heartbeat.uptimeio.com/heartbeat/YOUR_HEARTBEAT_TOKEN
```

```python theme={null}
import requests

def run_job():
    process_data()
    requests.get("https://heartbeat.uptimeio.com/heartbeat/YOUR_HEARTBEAT_TOKEN", timeout=10)
```

```javascript theme={null}
async function runTask() {
  await processQueue();
  await fetch('https://heartbeat.uptimeio.com/heartbeat/YOUR_HEARTBEAT_TOKEN');
}
```

```yaml theme={null}
# GitHub Actions
- name: Ping heartbeat
  if: success()
  run: curl -fsS ${{ secrets.HEARTBEAT_URL }}
```

## Best practices

* **Ping only on success**, so a failing job stops the pings and opens an incident.
* **Match the interval to your schedule**: hourly cron = 3,600 seconds, daily backup = 86,400.
* **Size the grace period to the job's runtime variation**: a backup that takes 10 to 20 minutes needs about 30 minutes.
* **Treat the URL as a secret.** Anyone with it can send pings and hide real failures.

## Troubleshooting

<AccordionGroup>
  <Accordion title="Incident opened but the job ran">
    The ping may have failed or gone to the wrong URL, or it was sent before the job finished. Use `curl -fsS` so errors are visible, confirm the URL, and ping at the end of the job.
  </Accordion>

  <Accordion title="No incident when the job fails">
    The job pings even on failure. Ping only after a successful run (check the exit code).
  </Accordion>

  <Accordion title="429 responses">
    You are pinging more often than 3 times per 30 seconds (or, on Free, more than once per 120 seconds). Reduce the frequency.
  </Accordion>

  <Accordion title="404 INVALID_TOKEN">
    No monitor uses this token. The monitor may have been deleted or its token replaced. Copy the current URL from the monitor.
  </Accordion>

  <Accordion title="INTERVAL_TOO_SHORT on create">
    The expected interval is below your plan minimum (Free 300 seconds).
  </Accordion>
</AccordionGroup>

## Next steps

<CardGroup cols={2}>
  <Card title="HTTP Monitoring" icon="globe" href="/monitors/http">
    Monitor web services and APIs
  </Card>

  <Card title="Notifications" icon="bell" href="/notifications/overview">
    Configure alerts for heartbeat monitors
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.