---
title: Importing monitors
description: Switch from UptimeRobot, Better Stack or Uptime Kuma in one step. Bring your monitors over with an API key, an export file or a CSV, on every plan.
---

# Importing monitors

Moving to UpHeron does not mean typing in every monitor again. Bring them over from UptimeRobot, Better Stack, Uptime Kuma or a CSV file in one step, on every plan, Free included.

In the dashboard, open **Import** at the top of any page, choose where your monitors are, and paste an API key or choose a file. You see what would be created and what would be skipped, and why, before anything is created. Untick what you don't want and import the rest.

## Where your monitors come from

| From | What to give | Where to find it |
|---|---|---|
| Better Stack | The team's API token: monitors and heartbeats | Better Stack → API tokens → Team-based tokens |
| CSV file | A list in [UpHeron's CSV format](#the-csv-format) | Any spreadsheet; start from the [example file](/downloads/upheron-monitors-example.csv) |
| Uptime Kuma | The JSON backup | Uptime Kuma 1.x → Settings → Backup → Export. Uptime Kuma 2.0 has no export: write a CSV file |
| UptimeRobot | A read-only API key, or a CSV in the columns of its bulk upload template | UptimeRobot → Integrations → API → Read-only API key |

An API key is used once, from our server, to list your monitors, and is neither stored nor logged. A read-only UptimeRobot key is enough; you can delete the key in the other service as soon as the import is done. Files can be up to 5 MB, and an import brings at most 1,000 monitors; import a larger account in parts.

## What each monitor becomes

Each monitor becomes the nearest UpHeron check, with its name, interval and paused state:

| From | Becomes |
|---|---|
| HTTP(S), website, status, expected status code | Website check, with its method and the healthy status range |
| Keyword (alert when the text is missing) | Website check with the required text |
| Ping | Ping |
| Port, TCP | TCP port |
| SMTP, IMAP, POP (Better Stack) | The mail server check, over TLS on ports 465, 993 and 995 and STARTTLS on the others |
| DNS | DNS, with the record type and expected answer where the source has them |
| Heartbeat, push | Heartbeat, with its period and grace time |
| UDP (UptimeRobot, with a request to send) | UDP |

Website checks also watch the domain's registration and the TLS certificate and alert before they expire, as every UpHeron website monitor does, so the source's SSL and domain expiry reminders need no monitor of their own. In a CSV file you can also add separate `ssl` and `domain` monitors with their own warning periods.

**Intervals.** A monitor that checks faster than your plan allows is slowed to your plan's fastest interval (5 minutes on Free, 30 seconds on Pro, 10 seconds on Business), and the preview says so. The longest interval is a day.

**Not imported**, and said so in the preview:

- Checks UpHeron does not have, such as UptimeRobot's visual comparisons and third-party service monitors, Better Stack's Playwright scenarios, and Uptime Kuma's database, Docker, MQTT, game server and Kafka monitors.
- Keyword checks that alert when the page *contains* the text (UptimeRobot's "keyword exists", Better Stack's keyword absence, Uptime Kuma's inverted keyword) and Uptime Kuma's upside-down mode: UpHeron alerts when the required text is missing.
- Monitors already in your workspace (the same address, or a heartbeat with the same name), and a monitor listed twice.
- Monitors beyond your plan's monitors: the first ones fit, in the order listed. A bigger plan has more; see [pricing](/pricing).
- WebSocket and gRPC checks on Free: they come with Pro.

Also not brought over: request headers, bodies and credentials (add them to the monitor afterwards), notification contacts, status pages, maintenance windows and history. Imported monitors alert every notification contact of the workspace, as a new monitor does, and get no status page of their own; add them to a status page under **Status pages**. Each import is recorded in the [audit log](/docs/api#audit-log) on Business.

## Heartbeats get new URLs

Heartbeat monitors are created with new UpHeron URLs. Your scheduled jobs still ping the old service until you change them: after the import, the dashboard lists each heartbeat's new URL once (the API returns it as `heartbeat_url`). Copy them and update your cron jobs, backups and scripts. Until a job pings its new URL, the heartbeat alerts that the ping is missing, one period and grace time after the import.

## The CSV format

The first row names the columns. The columns can come in any order, in any case, and with spaces, dashes or underscores (`Expected Status`, `expected_status` and `expected-status` are the same). Columns UpHeron does not know are ignored, so you can export a spreadsheet as it is. Values are separated by commas, semicolons or tabs; put a value that contains one in double quotes. Download the [example file](/downloads/upheron-monitors-example.csv).

| Column | Also read as | Value |
|---|---|---|
| `url` | `host`, `hostname`, `target`, `address`, `URL/IP` | `https://example.com`, or a host or `host:port`. A website without `https://` gets it. Not needed for heartbeats |
| `type` | `kind`, `monitor type` | `website` (the default), `keyword`, `ping`, `port`, `dns`, `heartbeat`, `ssl`, `domain`, `smtp`, `imap`, `pop`, `websocket`, `grpc` |
| `name` | `friendly name`, `monitor name` | Shown in alerts and on status pages. Defaults to the address |
| `interval` | `check interval`, `frequency` | Seconds, or `30s`, `5m`, `1h`. Defaults to 5 minutes |
| `port` | | For `port` and mail checks, when the address has none |
| `method` | | `GET` (the default), `HEAD`, `POST`, `PUT`, `PATCH`, `DELETE` |
| `expected_status` | `status codes` | `200`, `2xx` or `200-399`. Defaults to 200 to 399 |
| `keyword` | `keyword value` | Text a website's page must contain |
| `record_type` | | For `dns`: `A` (the default), `AAAA`, `CNAME`, `MX`, `TXT`, `NS`, `SOA`, `SRV` |
| `expected_answer` | `expected value` | For `dns`: an answer the record must contain |
| `grace` | `grace period` | For `heartbeat`: seconds a ping may be late |
| `warn_days` | | For `ssl` and `domain`: days before expiry to alert |
| `paused` | `status` | `yes` (or a `status` of `paused`) imports it paused |
| `timeout` | | Seconds |

```csv
name,type,url,interval,expected_status,keyword
Shop,website,https://shop.example.com,5m,200-299,Add to cart
Database,port,db.example.com:5432,300,,
Nightly backup,heartbeat,,86400,,
```

UptimeRobot's bulk upload template (`Type`, `URL/IP`, `Interval`, `Friendly Name`, `Keyword Type`, `Keyword Value`, `Port`) is read as it is: choose UptimeRobot or CSV file and the file.

## From the API and AI agents

The import works the same through the [API](/docs/api#importing-monitors) and the [MCP server](/docs/mcp), with a token that has the `write` scope:

```bash
curl https://upheron.com/api/v1/monitors/import/preview \
  -H "X-API-Key: $UPTIME_MONITOR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"source": "uptimerobot", "api_key": "'"$UPTIMEROBOT_READ_ONLY_KEY"'"}'
```

The answer lists each monitor with `action` (`create` or `skip`), `reason` (`unsupported`, `duplicate`, `over_allowance`, `needs_pro` or `invalid`), a `message`, `notes` on what changed, and the UpHeron `monitor` it maps to. Send the `monitor` objects you want:

```bash
curl https://upheron.com/api/v1/monitors/import \
  -H "X-API-Key: $UPTIME_MONITOR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"source": "uptimerobot", "monitors": [{"name": "Shop", "kind": "http", "url": "https://shop.example.com", "interval_seconds": 300}]}'
```

The answer has the monitors `created` (with each heartbeat's new `heartbeat_url`) and those `skipped` with why. For a file, send its text as `file` with `"source": "csv"` or `"uptime_kuma"`. Agents use `preview_monitor_import` and `import_monitors`, and show you the preview before importing.
