Guides

Bulk import monitors

Create dozens of monitors at once from a CSV or JSON file, including screenshots, SSL checks, regions, and TCP/DNS fields.

Instead of adding monitors one at a time, upload a CSV or JSON file and create up to 200 at once. The import is all-or-nothing: if any row is invalid, or the batch would exceed your plan's monitor limit, nothing is created and every reason is listed so you can fix the file and retry.

The fastest path

  1. Go to Monitors, then click Bulk import in the top right.
  2. Drag and drop a .csv or .json file, or click to browse for one.
  3. Review the parsed rows. Optionally pick a team and default regions for the whole batch, and tick Enable screenshots for all and/or Enable SSL check for all if you want every imported monitor to have those on regardless of what the file says.
  4. Optionally click Check reachability to probe every URL once and see an UP/DOWN badge per row before you commit. This is advisory only: a site that blocks automated probes may show DOWN yet still be worth importing, so a DOWN badge never stops the import.
  5. Click Import N monitors.

CSV columns

Only name and url are required (url is optional for ping, tcp, dns, and group monitors, which target a host instead of a URL). Every other column is optional:

ColumnMeaning
methodHTTP method (default GET)
interval, timeoutSeconds (defaults 60 / 30)
typehttp (default), ping, tcp, or dns
ping_targetHost to ping (type ping)
hostHost to check (type tcp or dns)
tcp_portPort number (type tcp)
dns_record_typeA, AAAA, A_AAAA, CNAME, MX, TXT, or NS (type dns)
dns_expected_valueOptional substring the resolved record must contain (type dns)
ssl_checktrue/false: HTTPS certificate expiry check
screenshot_enabled (aliases: screenshot, screenshots)true/false: capture a screenshot when the monitor goes down
follow_redirectstrue/false
keyword, keyword_typeBody keyword to assert (contains, not_contains, or regex)
valid_status_codese.g. 200-299
regions (alias: prober_regions)Which prober regions to check from
tagsFree-text tags
headers (alias: request_headers)Custom request headers, as Key: value pairs separated by ;
body (alias: request_body)Custom request body — raw text, sent as-is

Multi-value cells (valid_status_codes, regions, tags) use ; or | to separate values (commas are the CSV delimiter). headers also uses ; to separate multiple header pairs, but | is a valid character inside an HTTP header value, so it is never treated as a header separator — see below.

The regions column is a technical import field, so it uses machine-readable region IDs from the current prober fleet. To avoid working with IDs, leave the column empty and choose default regions for the whole batch in the import screen.

name,url,method,interval,timeout,ssl_check,screenshot_enabled,regions,valid_status_codes,tags,headers,body
"My API",https://api.example.com,GET,300,10,true,false,,200-299,"prod;api","X-Api-Key: abc123;Accept: application/json","{""ping"": true}"
"Health",https://app.example.com/health,GET,300,5,false,false,,200-299|301,prod,,

headers holds one or more Key: value pairs joined with ;, e.g. X-Api-Key: abc123;Accept: application/json. body is sent as written, with two exceptions: leading and trailing whitespace is trimmed from every cell (including body), and a Windows-style \r\n line ending anywhere inside it — even inside a quoted, multi-line value — is converted to a plain \n. Either cell that contains a comma (a JSON body almost always does) must be wrapped in double quotes, with any inner double quote doubled ("") — the row above's body cell shows this: "{""ping"": true}" becomes the request body {"ping": true}. A header line with no colon (e.g. a pasted curl -H argument without one) fails the whole import and names the offending row, the same way a missing name/url does. A structurally broken CSV file — most commonly a stray or unmatched double quote inside an unquoted value (an inch mark like 15" wide, for example) — also fails the whole import and names the line where the parser gave up, rather than importing only the rows that came before it.

JSON files use the same field names as an array of objects. headers is a plain JSON object ({"X-Api-Key": "abc123"}) instead of the Key: value; string form, and every other field uses regular JSON arrays and booleans instead of the ;/| and true/false string conventions.

The API validates each row against the workspace's current Uptime capabilities. Failure screenshots require screenshot_monitoring, multiple selected regions require multi_region, custom request headers or a request body require a paid Uptime plan (the same gate the single-monitor create form's Behaviour section applies), and the check interval must respect the current minimum. If a row asks for an unavailable capability, the whole import is rejected and the response lists each offending row. Adjust the file or the workspace capability, then import it again. See Screenshot debugging and the current pricing page.

Toggling screenshots or SSL for monitors you already have

You don't need to re-import to change these settings in bulk. On the Monitors page, select the monitors with the checkboxes, and the yellow bulk action bar that appears has two more icons alongside pause/resume/interval/regions/tags/team/delete: a camera icon for screenshots and a lock icon for SSL checks. Click one and choose Enable or Disable. The change applies to every selected monitor immediately, and your selection stays in place afterward so you can keep going.

Selecting monitors without losing your place

While you're ticking checkboxes to build a selection, clicking anywhere else on a row toggles that row's selection too, instead of navigating away and clearing everything you'd already picked. To open a monitor's detail page while you have a selection in progress, click its name (or use the kebab menu): both still navigate normally.

See also

Was this page helpful?