Health checks API: create and query checks via API v2

With the health checks API (part of the IPv64.net API v2) you create, change, pause and delete checks from scripts, query measurements, events and incidents, and manage notification channels, maintenance windows and nodes. The field names are the same as in the Node64 API, so scripts look alike against both.

Overview: base URL, format and errors

All paths on this page start with /hc2 and are appended to the API v2 base URL:

https://ipv64.net/api/v2
  • Methods: GET, POST, PATCH and DELETE. If you can only send GET and POST, send POST with the header X-HTTP-Method-Override: PATCH (or DELETE).
  • Requests as JSON (Content-Type: application/json), at most 1 MB. PATCH only changes the fields you send.
  • Responses as JSON in the envelope {"data": …, "meta": {…}}; meta only appears for lists with paging details. DELETE answers 204 without content.
  • Times in ISO 8601 with time zone, in responses always UTC (e.g. 2026-10-26T08:15:00.000Z).
  • The complete description as OpenAPI 3.1 (section “Healthchecks 2.0”) is available at ipv64.net/api_v2_docs.

Errors

Errors follow RFC 9457 as application/problem+json with a fixed error code in code:

{
  "type": "https://ipv64.net/api/v2/errors#limit_reached",
  "title": "Forbidden",
  "status": 403,
  "detail": "Online-Limit erreicht: 10 von 10 Messstellen belegt, benötigt werden 2 weitere. …",
  "code": "limit_reached",
  "errors": ["Online-Limit erreicht: …"]
}

Validation messages of checks and channels (detail, errors) are in German.

HTTPCodes (selection)Meaning
401unauthenticated, invalid_tokenAPI key missing, invalid or expired, or the account is not active
403limit_reachedmeasuring point limit of the account class reached
404not_foundcheck, channel, maintenance window, test run or node not found
409move_requires_test, unchanged, not_verified, disabled, node_channelaction not possible in the current state (details in detail)
422invalid_healthcheck, private_target, invalid_type, move_rejected, invalid_channel, invalid_code …invalid input; for checks and channels all messages are in errors
429rate_limited, sms_limittoo many requests or daily SMS limit reached
502delivery_failedmessage could not be delivered

Authentication and limits

The API uses your API keys from Account > API (see Account: API keys). Send the key only in a header – never in the URL:

curl "https://ipv64.net/api/v2/hc2/account" -H "Authorization: Bearer YOUR_API_KEY"
curl "https://ipv64.net/api/v2/hc2/account" -H "X-API-Key: YOUR_API_KEY"
The API key has full rights

An API key may do everything in the account, not just health checks. A cron job that only reports a push does not need an API key – use the push URL with the check's token instead.

Limits

  • General: per account up to 300 requests at once, then 10 per second; per IP address 600 at once, then 20 per second. Failed logins: 20, then 12 per minute per IP. The headers RateLimit-Limit and RateLimit-Remaining show the current state; on 429, Retry-After gives the waiting time in seconds.
  • Test messages: one per 30 seconds per channel.
  • Confirmation codes: 5 codes per hour, 10 attempts per hour – shared with the portal.
  • Sending through an IPv64 channel (/send): 30 at once, then 30 per hour, at most 200 per day; SMS additionally according to the daily limit of your account class.
  • The health check limits of your account class (online measuring points, online measurements per day, locations per check) also apply via API, see Limits.

Account and settings

GET /hc2/account

Account class, limits and usage:

{"data": {
  "plan": "Supporter",
  "online_measuring_points": {"used": 6, "limit": 10},
  "locations_per_check": 2,
  "checks_per_day": {"used": 2817, "limit": 8192},
  "sms_per_day": 50,
  "locations": [{"code": "fsn", "name": "Falkenstein"}, {"code": "nbg", "name": "Nürnberg"}],
  "nodes": {"total": 1, "online": 1},
  "checks": 12
}}

limit: null means unlimited; for sms_per_day that is 0.

GET and PATCH /hc2/settings

Quiet hours and grouped alerts of the account (see Notifications). Allowed keys:

KeyValue
notify.quiet_enabledtrue/false (stored as 1/0)
notify.quiet_from, notify.quiet_totime HH:MM
notify.quiet_daysweekdays 1 (Mon) to 7 (Sun), as a list or "1,2,3"
notify.quiet_tztime zone, e.g. Europe/Berlin
notify.rate_per_hour0 to 1000 (0 = unlimited)
notify.batch_threshold0 to 100
notify.batch_window_seconds0 to 600
curl -X PATCH "https://ipv64.net/api/v2/hc2/settings" -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"notify.quiet_enabled": true, "notify.quiet_from": "23:00", "notify.quiet_to": "06:30"}'

Manage checks

Method and pathEffect
GET /hc2/checksall checks; filters: type, state, execution, node_id, tag, group, q (search in name and target)
POST /hc2/checkscreate a check → 201 with the check
GET /hc2/checks/{token}one check with measuring points (sources) and availability (availability_percent for 24h, 30d, 1y)
PATCH /hc2/checks/{token}change fields (see below about the execution)
DELETE /hc2/checks/{token}delete the check with all measurement data → 204; if it was a monitor in DNS failover, the header Warning: 299 names the policies
POST /hc2/checks/{token}/pause, /resumepause or resume; response: the check
POST /hc2/checks/{token}/runcheck now: local and hybrid checks → 202 with a command_id per node; online checks → 200 with queued: false (they run at their interval); push checks → 422 not_applicable

Fields of a check

FieldDefault on createNote
typehttphttp, keyword, json, dns, icmp, tcp, port, cert, push, docker, whois
name–required, up to 200 characters
target–URL, host, IP, domain or container – depending on the type; empty for push
params{}settings of the type (see Parameters per type)
interval_seconds60locally from 5, online/hybrid from 20, up to 86400; WHOIS from 3600
retry_interval_seconds20from 5, at most the interval
retries, stabilize1, 30–10 and 1–20
connect_timeout_seconds, timeout_seconds5, 100.5–60 and 1–120 (online at most 30 and 60)
ip_modeautoauto, ipv4, ipv6, both
executiononlineonline, local, both
nodes or node_id–measuring nodes (list of IDs) or a single node; required for local/both
locationsall locationse.g. ["nbg","fsn"]; shortened to the locations of your account class
policyallall, any, majority
notify_down, notify_up, notify_degradedtrue, true, falsewhat you get notified about
channels[]channel IDs; empty = default channels
repeat_minutes0reminder, 0–10080
critical, invert, pausedfalse
group, tags, descriptionemptygroup up to 100 characters; tags as a list or text
parentemptytoken of the parent check

The response additionally contains token, node_id, node_name, nodes (with names), revision, created_at, updated_at and state with state, since, last_check_at, last_status, last_latency_ms, last_reason, last_message, down_since and last_push_at. Push checks also have a push object (see Push URL). The state is one of up, down, degraded, flapping, stabilizing, dependent, pending and paused; “not monitored” and “quota used up” show as pending with last_reason unchecked or quota_exhausted.

Parameters per type (params)

TypeKeys in params
http, keyword, jsonmethod, accept_status (e.g. "200-299,301"), follow_redirects, max_redirects, basic_user, basic_password, headers (object), body, insecure_tls, cert_min_days (no default via API, i.e. off); keyword only: keyword, keyword_absent; json only: json_rules, json_any
dnsrecord_type, resolver (system, server, authoritative), server, expect (any, contains, equals, regex, nxdomain, public_ip), values (list), min_ttl, dnssec
icmpcount, max_loss_percent, max_latency_ms
tcpport, send, expect, tls
portport, expect (open or closed)
certport (default 443), min_days, server_name, skip_chain, skip_hostname
whoissource (auto, rdap, whois), min_days, contains (list)
pushperiod_seconds, grace_seconds

json_rules is a list of objects {"path": "status", "op": "eq", "value": "ok"} with the operators eq, ne, lt, le, gt, ge, contains, not_contains, regex, exists, not_exists – or the text form as in the form. Limits and meaning of all values are listed under Check types.

Examples

Create an HTTPS check from both locations:

curl -X POST "https://ipv64.net/api/v2/hc2/checks" -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"type": "http", "name": "Website", "target": "https://example.com/",
       "interval_seconds": 60, "locations": ["nbg", "fsn"],
       "params": {"accept_status": "200-299", "cert_min_days": 14}}'

Push check for a nightly backup:

curl -X POST "https://ipv64.net/api/v2/hc2/checks" -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"type": "push", "name": "Backup NAS", "params": {"period_seconds": 86400, "grace_seconds": 3600}}'

List checks that are down, pause a check:

curl "https://ipv64.net/api/v2/hc2/checks?state=down" -H "Authorization: Bearer YOUR_API_KEY"
curl -X POST "https://ipv64.net/api/v2/hc2/checks/YOUR_CHECK_TOKEN/pause" -H "Authorization: Bearer YOUR_API_KEY"

Errors on create: 422 invalid_healthcheck (invalid input), 422 private_target (private target online), 403 limit_reached (measuring point limit).

Change the execution (moving)

If a PATCH changes execution, the nodes or the locations, the API saves all other fields but leaves the execution unchanged and answers 409 move_requires_test. You switch with a test run – or you send "force_move": true, then the switch happens without a test.

Method and pathEffect
POST /hc2/checks/{token}/movestart a test run; body: execution (required), nodes or node_id, locations → 202 with the test run
GET /hc2/testruns/{id}state of the test run
curl -X POST "https://ipv64.net/api/v2/hc2/checks/YOUR_CHECK_TOKEN/move" -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" -d '{"execution": "both", "nodes": ["YOUR_NODE_ID"], "locations": ["nbg", "fsn"]}'
{"data": {"id": 4711, "status": "ok", "target": "both", "applied": true,
  "results": [{"source": "ipv64 Nürnberg", "ok": true, "message": "ipv4 up 23.4 ms"}, …]}}

Poll the test run every 2 seconds for at most 60 seconds. status is pending, ok (switch applied, applied: true), failed (stays as before) or expired (no answer within 60 seconds, then with error). Errors on start: 409 unchanged (already runs this way), 422 move_rejected (e.g. node offline, remote management off, private target, limit, Docker check).

Measurements, events, incidents and statistics

Method and pathParametersContent
GET /hc2/checks/{token}/measurementslimit (1–5000, default 200), since, until, sourcesingle measurements, newest first: time, source, source_label, status (up/down), latency_ms, reason, message, family, details
GET /hc2/checks/{token}/events, GET /hc2/eventslimit (1–1000, default 100), before, after, sinceevents of one check or all checks: id, token, source, kind, time, from_state, to_state, reason, message, duration_seconds, flaps, silent, incident_id
GET /hc2/checks/{token}/incidentslimit (1–500, default 50)incidents of one check
GET /hc2/incidentslimit (1–1000, default 100), open=true, sinceincidents of all checks: id, token, name, status (open/closed), started_at, ended_at, duration_seconds, reason, message, sources
GET /hc2/checks/{token}/statsrange: 24h (default), 7d, 30d, 1yavailability and latency (min, average, max) in total and per measuring point, plus a time series per hour (24h, 7d) or per day (30d, 1y)
  • Measuring points (source): loc:nbg or loc:fsn for the IPv64 locations, node:NODE_ID for a node, push for pushes.
  • Single measurements are only kept for a short time; for longer periods use /stats.
  • Paging of events: meta contains limit, has_more and next_before (or next_after when you page forward with after). Pass the value as before or after in the next request.
  • silent: true means no message was sent for this event.
curl "https://ipv64.net/api/v2/hc2/checks/YOUR_CHECK_TOKEN/stats?range=30d" -H "Authorization: Bearer YOUR_API_KEY"
curl "https://ipv64.net/api/v2/hc2/incidents?open=true" -H "Authorization: Bearer YOUR_API_KEY"

Push URL for cron jobs

Pushes do not go through API v2 and need no API key. Every push check has a push object in the response:

FieldContent
urlhttps://ipv64.net/health.php?token=TOKEN – the push URL
hub_urlan equivalent address directly at the health checks server
node_pathpath on the node (/api/push/TOKEN) for local and hybrid push checks, otherwise null
# Report OK
curl -fsS -m 10 "https://ipv64.net/health.php?token=YOUR_PUSH_TOKEN"
# Report an outage
curl -fsS -m 10 "https://ipv64.net/health.php?token=YOUR_PUSH_TOKEN&status=down&msg=backup+failed"

Parameters: status=down or fail for an outage, msg or message for a message (up to 500 characters). The response is OK. At most 60 pushes per minute per token; pushes count against the daily quota. More under Push checks.

Notification channels

Method and pathEffect
GET /hc2/channel-typesall channel types with their fields (key, label, kind, required, secret, options, default) and needs_verification
GET /hc2/channels, GET /hc2/channels/{id}channels; secrets are missing from config, secrets_set lists the ones that are set
POST /hc2/channelscreate a channel: type, name, config, enabled (default true), default (default false), lang (de/en), notify_down, notify_up → 201, for e-mail/SMS with verification_required: true
PATCH /hc2/channels/{id}change; omitted secrets stay. For a node's channels only name and switches (config → 409 node_channel)
DELETE /hc2/channels/{id}delete and remove from all checks → 204
POST /hc2/channels/{id}/testtest message → {"sent": true, "queued_at_node": false}; confirmed channels only
POST /hc2/channels/{id}/verify/sendsend a confirmation code to the e-mail address or mobile number (valid for 15 minutes)
POST /hc2/channels/{id}/verifyconfirm with {"code": "123456"}; wrong or expired: 422 invalid_code
POST /hc2/channels/{id}/sendyour own message through a confirmed, enabled channel ipv64_mail, ipv64_sms or app: subject, text (required), optional kind, event_id, source

Channel types and their config keys: ipv64_mail (email), ipv64_sms (number), app (device_key, critical), telegram (bot_token, chat_id, silent), discord (webhook_url, username), slack and teams (webhook_url), matrix (server, token, room), ntfy (server, topic, token, user, password, priority_down, priority_up), gotify (server, app_token, priority), pushover (user_key, app_token, device, priority_down, priority_up), webhook (url, method, headers, body, insecure_tls), smtp (host, port, security, user, password, from, to). Meaning and defaults: Channel types. URLs and the SMTP host must be public, SMTP only on port 25, 465, 587 or 2525; otherwise 422 invalid_channel with the messages in errors. Internal targets only work through a channel on the node.

curl -X POST "https://ipv64.net/api/v2/hc2/channels" -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"type": "ntfy", "name": "ntfy phone", "default": true, "config": {"server": "https://ntfy.sh", "topic": "my-secret-topic"}}'

With /send, an event_id that was already sent successfully in the last 24 hours is not sent again (response duplicate: true). For SMS, sms_left in the response tells how many SMS are left today (null = unlimited).

Maintenance windows

Method and pathEffect
GET /hc2/maintenance, GET /hc2/maintenance/{id}maintenance windows
POST /hc2/maintenancecreate → 201
PATCH /hc2/maintenance/{id}change
DELETE /hc2/maintenance/{id}delete → 204

Fields: name, start_at and end_at (ISO 8601, required, end after start), repeat (none, daily, weekly, monthly), repeat_until, tokens (list of check tokens, empty = all checks) and active (default true).

curl -X POST "https://ipv64.net/api/v2/hc2/maintenance" -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name": "Proxmox updates", "start_at": "2026-11-01T03:00:00+01:00", "end_at": "2026-11-01T04:00:00+01:00", "repeat": "weekly"}'

Nodes

Method and pathEffect
GET /hc2/nodesnodes with health checks sync: node_id, name, location, version, timezone, modules, remote_mgmt, online, last_seen, last_sync, outbox_size, conflicts, linked_at
GET /hc2/nodes/{node_id}one node with its local and hybrid checks
PATCH /hc2/nodes/{node_id}name, location, remote_mgmt (off, read, full)
POST /hc2/nodes/linklink a node with a code: code (8 characters), optional location, remote_mgmt (default full) → 202; the node fetches its credentials itself within about 10 seconds. Unknown or expired code: 404 code_unknown
DELETE /hc2/nodes/{node_id}disconnect the health checks sync; keep_checks in the JSON body (default true) continues checks of only this node online where possible → {"unlinked": true, "checks_kept_online": 2, "checks_deleted": 1}
POST /hc2/nodes/{node_id}/commandscommand to the node: name = run_check, pause, resume (each with args.token), repair (full sync) or resolve_conflict (args.id, args.keep = server/local) → 202
GET /hc2/nodes/{node_id}/commandslast commands (limit 1–200, default 20) with done_at, ok, output
GET /node64/nodesthe node overview: all Node64 containers of the account with modules, features (health checks sync, honeypot sensor, CDN64 tunnel, DynDNS), sync state and removed

The node only accepts commands if its remote management allows it (see Remote management). Nodes register themselves with POST /node64/hello; you do not need that in your own scripts.

Found a mistake or missing something? Tell us in the v64.tech forum. · Updated: 2026-11-04