API monitors and assertions
An API monitor sends a fully-configured HTTP request — method, headers, authentication, body — and evaluates an ordered list of assertions against the response. The check passes only when every assertion passes; each assertion's pass/fail, expected and actual values are recorded on the result so you can see exactly what broke.
Need a whole flow — log in, extract a token, call an authenticated endpoint? Chain up to 10 of these requests with variable extraction in a Transaction monitor (Business plan and up).
Create an API monitor
- Go to /monitors/create and choose Type: API.
- Set the shared fields (project, name, interval, ownership attestation) and the Target — the endpoint URL, starting with
http://orhttps://. - Configure the request and at least one assertion (the form requires a minimum of one), then save.
Request configuration
- HTTP method — GET, POST, PUT, PATCH, DELETE or HEAD.
- Request headers — repeatable name/value rows sent verbatim (e.g.
Accept: application/json). - Authentication — one of:
- None
- Bearer token — sent as
Authorization: Bearer <token>. - Basic auth — username + password, sent as an
Authorization: Basicheader. - API key header — a header name (e.g.
X-API-Key) plus the key value.
- Request body — free text (e.g. a JSON payload), sent only for POST, PUT and PATCH.
Note: Secrets (bearer token, basic password, API key value) are stored encrypted and never shown again. On the edit form, leave the secret field blank to keep the saved value. In check results and logs, secret headers and auth values are always masked.
Assertions
Each assertion is a row of type, optional path, operator and expected value.
Assertion types
| Type | What it tests | Uses the path field? |
|---|---|---|
| Status code | The HTTP response status | No |
| Latency (ms) | Total response time in milliseconds | No |
| JSON path | A value extracted from the JSON body | Yes — the JSONPath |
| Response header | A response header's value | Yes — the header name |
| Body contains | The raw response body | No |
Operators
| Operator | Meaning |
|---|---|
equals / not equals |
Loose comparison — "200" equals 200, "true" equals true |
greater than / less than |
Numeric comparison (both sides must be numeric) |
contains |
Substring match; on a list (from a wildcard JSONPath), membership |
matches (regex) |
The actual value matches the regular expression |
For Body contains, equals is treated as contains automatically.
Examples
- Status code
equals200 - Latency (ms)
less than800 - JSON path
$.statusequalsok - JSON path
$.data.items[*].skucontainsWIDGET-1 - Response header
content-typecontainsapplication/json - Body contains — expected
"healthy":true
JSONPath syntax
CompleteStatus supports a focused JSONPath subset:
| Syntax | Meaning | Example |
|---|---|---|
$ |
The root (every path starts here) | $ |
.key |
Child by name (letters, digits, _, -) |
$.data.user.name |
['key'] or ["key"] |
Child by name, any characters | $['odd key.name'] |
[n] |
Array element by index (negative counts from the end) | $.items[0].sku, $.items[-1] |
[*] or .* |
Wildcard over an array or object | $.items[*].sku |
Wildcards fan out: $.items[*].sku yields the list of every item's sku. With a wildcard path, equals compares the whole list, and contains checks whether any element matches — usually what you want. A path with no wildcard yields the single first match, or nothing when absent (an absent value fails equals and passes not equals against anything non-null).
Not supported: recursive descent (..), filter expressions ([?(...)]), slices ([0:2]) and unions.
How the check behaves
- If you somehow have an API monitor with no assertions, it falls back to "any 2xx/3xx status passes".
- The whole check is time-boxed to 30 seconds, retries once on a network error, follows redirects (each hop is re-validated against internal networks), and runs from a single probe region.
- On failure, the result's detail lists every assertion with its expected vs. actual value plus a
N assertion(s) failederror; a confirmed failure (two consecutive) opens an incident, exactly like uptime monitors — see HTTP uptime monitors.
Content-change monitoring
If what you want is "tell me when this page changes" rather than "assert this value", use the separate Change detection monitor type:
- Comparison mode — Text (hash the normalized text content) or Visual (text hash plus a screenshot captured on change).
- Render JS with headless Chrome — enable for JavaScript-rendered pages; off, the raw HTML is compared.
- CSS selector (optional) — scope the comparison to one region, e.g.
#mainorarticle.post. A single simple selector (tag,#id,.classor a combination of those) is supported; descendant selectors like#main .contentare not, and an unmatched selector falls back to the whole page. - Ignore patterns — regex patterns stripped before hashing, for content that legitimately changes every load (CSRF tokens, timestamps):
csrf-token="[^"]+".
The first check stores a baseline. A later change fails the check and, once confirmed, opens an incident carrying a unified diff of what was added and removed (and a screenshot in visual mode). The last 50 snapshots are kept per monitor.
There is also a simpler Keyword monitor type: assert a literal string or regex is present (or absent) in a page, with a case-sensitivity toggle — a lighter tool for "did the error page appear?" checks.
Related guides
- Transaction monitors — multi-step API flows with variable extraction and per-step assertions.
- HTTP uptime monitors — intervals, plan limits and monitor management.
- Cron and heartbeat monitors — push-based monitoring for internal jobs.
- API monitoring beyond ping — assertion strategy, latency budgets and auth, on the blog.
- Health check endpoints done right — designing a
/healthendpoint worth asserting on.