---
title: API
description: Validate API responses — assert on JSON fields, headers and body text, extract numbers to chart, and build it live with Run & inspect.
canonical: https://watchfor.io/docs/monitors/api
---

# API

Validate API responses — assert on JSON fields, headers and body text, extract numbers to chart, and build it live with Run & inspect.

An **API monitor** makes a real HTTP request to your endpoint and validates the
**response**, not just that it answered. It is a full HTTP monitor — SSL,
timing breakdown, redirects, auth, HTTP/3 — with a response-validation engine
on top: assert on any JSON field, header or body text, and **extract numbers
from the body to chart over time**.

**Target:** a full `http://` or `https://` URL.

> **HTTP monitor vs. API monitor.** The HTTP monitor answers *"is it up?"*.
> The API monitor answers *"is the response correct?"* — because an endpoint
> can return `200 OK` and still be wrong.

## When to use it

Reach for an API monitor when the **contents** of the response matter:

- A health endpoint returning `{"status":"ok"}` — alert if the field is ever
  anything else, even though HTTP still answers `200`.
- A JSON API where a field flipping (`"degraded"`, `false`, an empty list) is
  an outage the status code won't reveal.
- A value you want to **watch over time** — queue depth, active users, a
  price, rate-limit remaining — pulled out of the response and charted.

For "is my site up, optionally with a keyword", the
[HTTP monitor](/docs/monitors/web) is lighter.

## Build it live — Run & inspect

You don't have to type JSONPaths blind. In the monitor's **Settings**, the
**Try it** section runs the request **once from a real probe location** and
shows you the actual response:

1. Fill in the URL (and method / headers / auth if needed).
2. Click **Run request**.
3. You get the live response — status, timing, size, headers and the body as
   a list of **fields** (`resources.core.remaining = 60`, …).
4. Click **Extract** on any number to chart it, or **Assert** to pin it — the
   JSONPath is filled in for you.

The tester goes through the same engine a scheduled check uses, so what you
see in Run & inspect is what the monitor will do. Test runs are rate-limited
per user.

## The request

| Setting | Notes |
| --- | --- |
| **Method** | GET, POST, PUT, PATCH, DELETE, HEAD or OPTIONS. |
| **Headers** | Any custom headers (an API key, for example). Credential headers (`Authorization`, `Cookie`, `X-API-Key`…) are saved encrypted and never shown again. One click adds a no-cache bundle so a CDN can't serve a stale response. |
| **Request body** | Up to 1 MB; JSON is validated as you type. Works on GET too (Elasticsearch-style APIs). |
| **Authentication** | None, Basic, Digest or Bearer token. The password and token are saved encrypted and never shown again; a saved one is asked for again when the URL's host changes. |
| **Transport Mode** | TCP, HTTP/3 (forced QUIC), or Auto (TCP with HTTP/3 fallback). |
| **IP version** and **timeout** | Auto / IPv4 / IPv6; timeout up to 120 seconds. |

## Response assertions

Assertions validate the **content** of the response. Each is a row:

| Source | Property | Operator | Value |
|---|---|---|---|
| JSON body | `data.status` | equals | `ok` |
| JSON body | `data.items.#` | greater than | `0` |
| JSON body | `data.healthy` | equals | `true` |
| Header | `Content-Type` | contains | `json` |
| Response text | — | contains | `"success"` |

**Sources:** JSON body, header, response text. **Operators:** equals, not
equals, greater than, greater or equal, less than, less or equal, contains,
does not contain, is one of, is not one of, exists, does not exist, matches
regex. Up to 50 assertions per monitor; all of them must pass.

**JSONPath** uses dot/bracket syntax — `data.items.0.name`,
`data.#(active==true).id`. A leading `$.` is accepted, so paths you already
have paste straight in.

A failing assertion raises the **Assertions Failed** rule (on by default).
The detail page and each check's **View response** dialog show **every
assertion's pass or fail** with expected vs. actual — when a check goes red
you see exactly which rule broke and what the API returned.

> **Status code, response time and SSL are not assertions.** They are alert
> rules on the **Alerting** tab, with the same friendly controls as every
> other monitor type. Assertions are for the response *content* only.

## Extract values to chart

Point a JSONPath at a **numeric** field and give it a name
(lowercase letters, digits and underscores):

```
active_users   ←  data.metrics.active_users
queue_depth    ←  data.queue.depth
rate_remaining ←  resources.core.remaining
```

Each becomes a metric (`api.extract.<name>`) stored with every check, up to
20 per monitor:

- **Charted over time** on the detail page — every value on its **own
  scale**, so a huge number (a reset timestamp of `1786203625`) never flattens
  a small one (`used = 0`), with min / average / max for the period.
- **Inspectable per check** in the Probes tab, next to the assertion results.
- Booleans map to `1`/`0`, so a `{"healthy": true}` flag charts too.

This is a time series pulled straight out of your API. For pass/fail
conditions on the same fields, add an assertion.

> **Numbers → extract; text/structure → assert.** Extract a numeric value you
> want to trend; assert an existence, string or structural check.

## Alert rules

For an API monitor, **success = the endpoint answered**. Everything else is a
rule on the **Alerting** tab, so each failure gets its own descriptive
incident:

| Preset | Default | Fires when |
| --- | --- | --- |
| **Probe Status** | on | No HTTP response at all (DNS, connection, TLS, timeout). |
| **HTTP Status Evaluation** | on | Status outside the accepted classes — a `2xx` / `3xx` group picker, exact codes allowed. |
| **Assertions Failed** | on | Any response-content assertion failed. |
| **Response Time (Latency)** | off | Latency over a threshold in milliseconds. |
| **SSL Certificate** | on, 30 days (warning) | Certificate nearing expiry — add the optional critical level (7 days) and [reminder days](/docs/monitors/certificates#expiry-levels-and-reminders) on the same card. |

So a `500` fires *"Unexpected HTTP status"*, a wrong JSON field fires
*"Response assertion failed"*, and a real outage fires *"Endpoint
unreachable"* — never the wrong one. Failures are confirmed across locations
before firing; see [Alerting](/docs/alerting).

The alert metrics for the API are `success`, `api.status_code`,
`metrics['api.assertions_failed']`, `metrics['api.response_time_ms']` and
`metrics['api.ssl_days_until_expiry']` — see
[API in the type catalog](/docs/api/monitor-types#api).

## What else it captures

Because it is a full HTTP monitor, every check also records the certificate
(days until expiry, issuer, TLS version), the timing breakdown (DNS, connect,
TLS handshake, time to first byte), the response size, redirects and final
URL, the HTTP protocol version, HTTP/3 advertisement and legacy-TLS
detection. The **Probes** tab lists every check per location, and **View
response** opens the captured body, that check's assertion outcomes and
extracted values.

## Example: monitor a rate-limit endpoint

`https://api.github.com/rate_limit` returns:

```json
{ "resources": { "core": { "limit": 60, "remaining": 60, "reset": 1786203625, "used": 0 } } }
```

1. **Run & inspect** → **Run request**, then click **Extract** on
   `resources.core.remaining` and `resources.core.used`.
2. Click **Assert** on `resources.core.limit` → equals `60`, so a silent
   quota change fails the check.
3. Save. The detail page now charts `remaining` and `used` over time, each on
   its own scale, and the **HTTP Status Evaluation** rule (accept `2xx`)
   pages you if the endpoint starts erroring.

## Also via API

Create API monitors with `type: "api"` and `config.assertions` /
`config.extractions` — see [Monitors API](/docs/api/monitors#create-a-monitor)
and the [type catalog](/docs/api/monitor-types#api). Extracted values are
returned with each check in [check history](/docs/api/checks-uptime#check-history).
Try requests interactively with the free [API tester](/api-tester).

## Secrets in requests

Type `{{NAME}}` in the username, password, bearer token, a header value or the
request body to use an organisation variable or secret from **Settings →
Variables** — `Authorization: Bearer {{API_TOKEN}}`, or
`{"client_secret": "{{CLIENT_SECRET}}"}` in a token request. The reference is
stored as written; the secret stays encrypted and is filled in for each
request, including **Run & inspect**. Rotating the secret updates every check
that uses it. See [Web checks → Credentials and
variables](/docs/monitors/web#credentials-and-variables) for how saved
credentials behave.

---

Canonical page: https://watchfor.io/docs/monitors/api · All docs: https://watchfor.io/docs · Site guide: https://watchfor.io/llms.txt
