Developers

PingWise API

Everything the panel does with monitors, incidents, groups and status pages, over plain JSON. One key per script, the same validation and the same prices as the panel, and an OpenAPI 3.1 document you can generate a client from.

Getting started

Base URL: https://app.pingwise.com/api/v1. Make a key in Team → API keys (owners only), copy it once, and send it as a bearer token:

curl https://app.pingwise.com/api/v1/monitors \
  -H "Authorization: Bearer pw_…"

Add a monitor the same way the panel’s New monitor form would, with the same rules and the same messages when something is off:

curl https://app.pingwise.com/api/v1/monitors \
  -H "Authorization: Bearer pw_…" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://example.com", "interval": 60}'

The answer is the new monitor, with its id, its state (pending until the first round comes back) and credits_per_month, what it costs at that interval.

Authentication

Every request carries Authorization: Bearer pw_…. A key belongs to one account and is either read only or read and write; pick the narrowest one that does the job, one key per integration, and revoke it from the same page when the integration goes. Keys are stored hashed; the panel shows the prefix only, and the full key exactly once.

  • No key, or an unknown or revoked one: 401 unauthorized.
  • A read-only key on a write: 403 forbidden. A frozen account (deletion requested): 403 account_frozen.
  • No sessions, no cookies, no CSRF: the key is the whole story. Use HTTPS; plain HTTP is not served.

Conventions

  • JSON in and out. Send Content-Type: application/json with a JSON object as the body. Unknown fields are refused, so a typo can’t pass silently.
  • Times are ISO 8601 in UTC with a Z, e.g. 2026-10-31T08:18:44Z. Durations are seconds.
  • Ids: monitors, channels and accounts have UUIDs; incidents, groups and status pages have integers.
  • Lists come as {"data": […], "next_cursor": "…"}. Pass next_cursor back as ?cursor= until it is null. ?limit= is 1–100, 50 by default. Monitors list oldest first; incidents newest first.
  • PATCH changes only the fields you send. Omitted fields stay; null clears a field that allows it.
  • Secrets never come back. secret_headers and basic_password are write only; responses carry has_secret_headers and has_basic_password instead. Alert channels are listed without their addresses or tokens.
  • Deleting answers 204 with no body.

Errors

Every error is a JSON object with an error code and usually a detail sentence. Validation errors add fields, a map from field name to a list of messages, the same messages the panel shows.

HTTP/1.1 422 Unprocessable Content
{"error": "invalid", "fields": {"url": ["Enter a valid URL."], "colour": ["Unknown field."]}}
StatuserrorWhen
400bad_requestThe body is not a JSON object.
401unauthorizedNo key, or an unknown or revoked one.
403forbidden, account_frozenA read-only key on a write; a hosting-panel-managed monitor the key may not change; the account is being deleted.
404not_foundNot in this account. Reseller endpoints also answer 404 when the account is not a reseller.
405method_not_allowedThe path exists, the method doesn’t.
409not_possible, terms_missingCheck now is already running or over its hourly allowance; an invitation before the brand has terms and privacy links.
422invalidValidation: see fields. Also a bad ?limit=, ?cursor= or filter value.
429rate_limitedOver a per-key limit; retry_after and the Retry-After header say how many seconds to wait.

Rate limits and cost

Per key, per minute: 120 reads, 20 writes (POST, PATCH, DELETE) and 10 “check now” calls. Every response carries X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset (Unix time) for the bucket it counted against. Over the limit you get 429 with Retry-After.

Reading costs nothing. A monitor costs the same credits whether it was made here or in the panel: credits_per_month on every monitor says how many at its interval, and GET /account shows the balance, this month’s use and how many days the credits last. “Check now” is one extra round by every healthy probe, charged like a scheduled round, and limited to once a minute per monitor and 30 an hour per account, shared with the panel.

Checks on hosts outside a domain you have verified keep the panel’s rules too: HTTP(S) and TLS on ports 80 and 443, at most once a minute, no request bodies or custom methods. Verify the domain in the panel to unlock 15-second checks, any port and TCP, SMTP and DNS checks.

Resellers

A reseller account’s key can act inside any of its customer accounts: send X-Account: <customer id or slug> and every endpoint under Reference reads and writes that account instead. Each change is written to the customer’s support log as API key “name”: …, exactly like a visit by one of your team. Customer accounts can also have keys of their own, made by their owner; those see only their account and cannot send X-Account.

The Reseller endpoints list, create and change customer accounts and send invitations. They are available to reseller accounts only and are not combined with X-Account.

Examples

Export every monitor

import requests

API = "https://app.pingwise.com/api/v1"
H = {"Authorization": "Bearer pw_…"}

monitors, cursor = [], None
while True:
    r = requests.get(f"{API}/monitors", headers=H, params={"limit": 100, "cursor": cursor})
    r.raise_for_status()
    page = r.json()
    monitors += page["data"]
    cursor = page["next_cursor"]
    if cursor is None:
        break
print(len(monitors), "monitors")

Add a monitor when a site is created, pause it when the site is suspended

r = requests.post(f"{API}/monitors", headers=H, json={
    "name": "Customer shop",
    "url": "https://shop.example.com",
    "interval": 60,
    "group": 12,
    "notes": "Hosted on web-03. Contact: Nikos, +30 69…",
})
if r.status_code == 422:
    print(r.json()["fields"])          # {"url": ["…"]} — the same messages as the panel
monitor_id = r.json()["id"]

requests.post(f"{API}/monitors/{monitor_id}/pause", headers=H)
requests.post(f"{API}/monitors/{monitor_id}/resume", headers=H)

What is down right now?

curl "https://app.pingwise.com/api/v1/incidents?status=open" -H "Authorization: Bearer pw_…"

A reseller creating a customer and its first monitor

acct = requests.post(f"{API}/accounts", headers=H, json={
    "name": "Bakery Nikos", "contact_email": "[email protected]", "monthly_cap": 50000,
}).json()

requests.post(f"{API}/monitors", headers={**H, "X-Account": acct["id"]},
              json={"url": "https://bakerynikos.gr", "interval": 300})

requests.post(f"{API}/accounts/{acct['id']}/invites", headers=H,
              json={"email": "[email protected]", "role": "owner"})

Reference

Generated from openapi.json, the same file the server is tested against: every route below exists, and every route that exists is below. Field details are under Objects.

Account

GET /account

The account the key acts in, with its credits

Returns 200 Account

Monitors

GET /monitors

List monitors Oldest first. Paging with limit=100 and the cursor exports every monitor.

state query
up, down (down or partial), slow or paused · one of up, down, slow, paused
group query
A group id, or none
type query
A monitor type
q query
Part of the name, URL or host
limit query
Rows per page, 1–100
cursor query
next_cursor of the previous page

Returns 200 a page of Monitor

POST /monitors

Add a monitor The same rules as the panel’s New monitor form, with its messages: e.g. other ports than 80 and 443 and some check types need a verified domain. It costs credits_per_month.

Body MonitorInput

Returns 201 Monitor

GET /monitors/{id}

A monitor, with uptime and its open incident

Returns 200 Monitor

PATCH /monitors/{id}

Change a monitor Only the fields sent change. On a monitor a hosting panel manages, its customer’s own key may change name, alert_partial and notes only.

Body MonitorInput

Returns 200 Monitor

DELETE /monitors/{id}

Delete a monitor

Returns 204 no body

POST /monitors/{id}/pause

Pause

Returns 200 Monitor

POST /monitors/{id}/resume

Resume

Returns 200 Monitor

POST /monitors/{id}/check

Check now One extra round by every healthy probe through the normal quorum, charged like a scheduled round. Once a minute per monitor, 30 an hour per account (shared with the panel), 10 a minute per key.

Returns 202 Check

Incidents

GET /monitors/{id}/incidents

A monitor’s incidents, newest first

status query
open or resolved
since query
ISO 8601: started or resolved since, or still open
limit query
Rows per page, 1–100
cursor query
next_cursor of the previous page

Returns 200 a page of Incident

GET /incidents

Incidents, newest first

status query
open or resolved · one of open, resolved
since query
ISO 8601: started or resolved since, or still open
monitor query
A monitor id
limit query
Rows per page, 1–100
cursor query
next_cursor of the previous page

Returns 200 a page of Incident

GET /incidents/{id}

An incident

Returns 200 Incident

Groups

GET /groups

Monitor groups

limit query
Rows per page, 1–100
cursor query
next_cursor of the previous page

Returns 200 a page of Group

POST /groups

Add a group

Body GroupInput

Returns 201 Group

PATCH /groups/{id}

Rename or move a group

Body GroupInput

Returns 200 Group

DELETE /groups/{id}

Delete a group

Returns 204 no body

Channels

GET /channels

Alert channels (read only; addresses and secrets are never returned)

limit query
Rows per page, 1–100
cursor query
next_cursor of the previous page

Returns 200 a page of Channel

Status pages

GET /status-pages

Status pages (read only)

limit query
Rows per page, 1–100
cursor query
next_cursor of the previous page

Returns 200 a page of StatusPage

Probes

GET /probes

The probes that vote, with their addresses for allow-lists

Returns 200 a page of Probe

Reseller

GET /accounts

Your customer accounts Reseller accounts only (404 otherwise, and while reselling is not switched on). Not with X-Account.

limit query
Rows per page, 1–100
cursor query
next_cursor of the previous page

Returns 200 a page of Customer

POST /accounts

Add a customer account Reseller accounts only (404 otherwise, and while reselling is not switched on). Not with X-Account.

Body CustomerInput

Returns 201 Customer

PATCH /accounts/{id}

Change a customer account Reseller accounts only (404 otherwise, and while reselling is not switched on). Not with X-Account. panel_org_id can’t change.

Body CustomerInput

Returns 200 Customer

POST /accounts/{id}/invites

Invite someone to a customer account Reseller accounts only (404 otherwise, and while reselling is not switched on). Not with X-Account. The invitation email the panel sends; 409 terms_missing until your brand has its terms and privacy links.

Body InviteInput

Returns 201 Invite

Meta

GET /openapi.json

This document

Returns 200 object

Objects

Error

FieldTypeNotes
error requiredone of unauthorized, forbidden, account_frozen, not_found, invalid, bad_request, rate_limited, not_possible, terms_missing, method_not_allowed
detailstring
fieldsobject
retry_afterinteger

MonitorInput

FieldTypeNotes
namestringDefaults to the URL or host. · up to 120 characters
typeone of http, tls, tcp, smtp, dns, ping, heartbeatdefault "http"
urlstring or nullhttp: the full http(s) address. · up to 2000 characters
hoststring or nulltls, tcp, smtp, dns, ping: the hostname. · up to 253 characters
portinteger or null1–65535
intervalone of 15, 30, 60, 300, 900, 1800, 3600Seconds between checks. · default 60
timeoutintegerdefault 10 · 1–30
ip_versionone of auto, 4, 6default "auto"
expectarray of integerAccepted status codes; [] = any 2xx.
keywordstringhttp: text the page must contain. · up to 200 characters
keyword_absentbooleanDown when the keyword IS found.
follow_redirectsbooleandefault true
verify_tlsbooleandefault true
detect_error_pagesbooleandefault true
cert_warn_daysintegerdefault 14 · 0–90
starttlsboolean
expect_bannerstringup to 200 characters
qnamestringup to 253 characters
rtypestringdefault "A"
dev_modeboolean401/403/404 count as up, and no alerts are sent.
methodone of GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONSAnything but GET and HEAD is sent from every probe each round and may change data. · default "GET"
req_headersarray of array of string or objectShown in the panel.
req_bodystringup to 10240 characters
req_content_typestring
basic_userstringup to 200 characters
hb_periodinteger or nullheartbeat: how often the job runs (seconds).
hb_graceinteger or nullheartbeat: how late it may be (seconds).
audiencearray of one of eu, na, sa, as, af, oc or nullVisitors’ continents; null = the account default.
slow_msinteger or null50–60000
slow_metricone of ttfb, server, total
slow_scopeone of auto, audience, all
alert_slowboolean
alert_partialboolean
excluded_continentsarray of one of eu, na, sa, as, af, oc or nullContinents whose probes don’t vote; null = the account default.
groupinteger or nullA group id (GET /groups), or null.
notesstringPlain text for your team (contact person, what to do). Sent with down alerts and reminders; never on status pages. · up to 2000 characters
secret_headersarray of array of string or object or nullWrite only: stored encrypted, never returned. [] or null removes them.
basic_passwordstring or nullWrite only: stored encrypted.
channelsarray of string (uuid)Alert channels that should name this monitor. A channel that alerts for all monitors is left as it is.

Monitor

FieldTypeNotes
namestringDefaults to the URL or host. · up to 120 characters
typeone of http, tls, tcp, smtp, dns, ping, heartbeatdefault "http"
urlstring or nullhttp: the full http(s) address. · up to 2000 characters
hoststring or nulltls, tcp, smtp, dns, ping: the hostname. · up to 253 characters
portinteger or null1–65535
intervalone of 15, 30, 60, 300, 900, 1800, 3600Seconds between checks. · default 60
timeoutintegerdefault 10 · 1–30
ip_versionone of auto, 4, 6default "auto"
expectarray of integerAccepted status codes; [] = any 2xx.
keywordstringhttp: text the page must contain. · up to 200 characters
keyword_absentbooleanDown when the keyword IS found.
follow_redirectsbooleandefault true
verify_tlsbooleandefault true
detect_error_pagesbooleandefault true
cert_warn_daysintegerdefault 14 · 0–90
starttlsboolean
expect_bannerstringup to 200 characters
qnamestringup to 253 characters
rtypestringdefault "A"
dev_modeboolean401/403/404 count as up, and no alerts are sent.
methodone of GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONSAnything but GET and HEAD is sent from every probe each round and may change data. · default "GET"
req_headersarray of array of string or objectShown in the panel.
req_bodystringup to 10240 characters
req_content_typestring
basic_userstringup to 200 characters
hb_periodinteger or nullheartbeat: how often the job runs (seconds).
hb_graceinteger or nullheartbeat: how late it may be (seconds).
audiencearray of one of eu, na, sa, as, af, oc or nullVisitors’ continents; null = the account default.
slow_msinteger or null50–60000
slow_metricone of ttfb, server, total
slow_scopeone of auto, audience, all
alert_slowboolean
alert_partialboolean
excluded_continentsarray of one of eu, na, sa, as, af, oc or nullContinents whose probes don’t vote; null = the account default.
groupinteger or nullA group id (GET /groups), or null.
notesstringPlain text for your team (contact person, what to do). Sent with down alerts and reminders; never on status pages. · up to 2000 characters
idstring (uuid)
has_secret_headersboolean
has_basic_passwordboolean
ping_urlstring or nullheartbeat: the URL your job calls.
channelsarray of string (uuid)Enabled channels that alert for it.
pausedboolean
managedbooleanKept in sync by a hosting panel integration.
stateone of pending, up, slow, down, partial, unconfirmed, blocked, maintenance, paused
sincestring or null (date-time)
last_check_atstring or null (date-time)
credits_per_monthintegerWhat it costs a month at its interval (0 when paused or a heartbeat).
created_atstring (date-time)
updated_atstring (date-time)
uptimeobject: 24h, 7d, 30dGET /monitors/{id} only.
open_incidentinteger or nullGET /monitors/{id} only.

Account

FieldTypeNotes
idstring (uuid)
namestring
slugstring
timezonestring
billingstring
is_resellerboolean
parentobject: id, name or null
creditsobject: balance, month_used, free_left, daily, days_left
planobject: pack, eur, status, next_renewal_at, ends_at or nullThe monthly credit plan, read only; null when there is none.

Incident

FieldTypeNotes
idinteger
monitorobject: id, name
statusone of open, resolved
reasonstring
detailstring
started_atstring (date-time)
confirmed_atstring (date-time)
resolved_atstring or null (date-time)
duration_sinteger or null
explanationobject: headline, what_happened, likely_cause, what_to_do, scope, confidence or nullAccounts with AI explanations only.

Group

FieldTypeNotes
idinteger
namestringup to 120 characters
positioninteger
monitorsinteger

GroupInput

FieldTypeNotes
namestringup to 120 characters
positioninteger0

Channel

FieldTypeNotes
idstring (uuid)
kindstring
namestring
enabledboolean
verifiedboolean
all_monitorsboolean
monitorsarray of string (uuid)
groupsarray of integer

StatusPage

FieldTypeNotes
idinteger
namestring
slugstring
urlstring
visibilityone of public, protected, private
brandobject: id, name
entriesarray of object: id, name, section, group, monitors

Probe

FieldTypeNotes
namestring
locationstring
continentstring
providerstring
ipv4string or null
ipv6string or null
rdnsstring or null

Check

FieldTypeNotes
idinteger
monitorstring (uuid)
statusstring
requested_atstring (date-time)

Customer

FieldTypeNotes
idstring (uuid)
slugstring
namestring
contact_emailstring or null
monthly_capinteger or null
month_usedinteger
monitorsinteger
open_incidentsinteger
panel_org_idstring or null
joinedboolean
invitedboolean
created_atstring (date-time)

CustomerInput

FieldTypeNotes
namestringup to 150 characters
contact_emailstring or null (email)
monthly_capinteger or nullCredits a month this customer may use; null = no cap. · 1
panel_org_idstringPOST only: an Enhance organisation whose websites are in your account; they move to the new customer and stay in sync.

InviteInput

FieldTypeNotes
email requiredstring (email)
roleone of owner, member, viewerdefault "owner"

Invite

FieldTypeNotes
idinteger
accountstring (uuid)
emailstring
rolestring
created_atstring (date-time)
expires_in_daysinteger

Changes

v1 · October 2026: first release. We add fields and endpoints without changing what exists; anything that would break a client gets a new version path and notice in the panel.

Questions or a missing endpoint: hello@pingwise.com.