---
title: API monitoring beyond status codes: assert on the response, chart what matters
description: A 200 OK only proves your API answered — not that the answer was right. Here's why status-only checks miss real outages, what monitoring the response actually takes, and how WatchFor's new API monitor asserts on JSON, extracts values into charts, and pages you when the body goes wrong.
canonical: https://watchfor.io/blog/api-monitoring-beyond-status-codes
---

# API monitoring beyond status codes: assert on the response, chart what matters

2026-08-09 · WatchFor Team · Monitoring · api, monitoring, reliability

A 200 OK only proves your API answered — not that the answer was right. Here's why status-only checks miss real outages, what monitoring the response actually takes, and how WatchFor's new API monitor asserts on JSON, extracts values into charts, and pages you when the body goes wrong.

An API returns `200 OK`. Your uptime monitor is green. Everyone goes to lunch.

Meanwhile the body says `{"status": "degraded", "items": []}`. The order list is empty, a partner's integration is failing silently, and the mobile app is showing yesterday's data. Nothing is "down" — the HTTP request succeeded. The *answer* was wrong, and nothing was watching the answer.

This is the gap between **uptime** monitoring and **API** monitoring. The first asks *did it respond?* The second asks *did it respond correctly?* — and for an API, that second question is the whole job.

## Why a status code isn't a health check

A status code describes the transport, not the truth. An endpoint can hand back a flawless `200` while everything the caller cares about is broken:

- A cached error page served with a `200` by a CDN.
- `{"error": "database unavailable"}` wrapped in a `200` by an over-forgiving framework.
- A list endpoint that returns `200` and an **empty array** because a filter silently broke.
- A boolean that flipped — `"healthy": false` — with the HTTP layer none the wiser.

None of these move the status code. All of them are outages to whatever calls the API. If your monitoring stops at the status line, these fail invisibly until a human downstream notices — which is the most expensive way to find out.

> The failure mode of API monitoring isn't the false alarm. It's the **silent 200** — the check that stays green while the thing it's supposed to protect is broken.

## What monitoring the response actually takes

Reading the response — not just the status — pulls in a few things a page-load check never has to deal with.

### Assert on the content

The core move is to require the response to *contain what success looks like* and *not contain what failure looks like*. `status == "ok"`. `items > 0`. `error` absent. This is what catches the silent 200 — you're checking the body against a contract, not trusting the HTTP code.

### Trend the numbers, don't just pass/fail

Some failures don't arrive as a boolean; they *creep*. Queue depth climbing. Rate-limit remaining draining. A price feed drifting. Active-user count quietly falling to zero. You want these as a **time series** you can watch trend and alert on a threshold — so you get paged *before* the value hits the wall, not after it already broke something.

### Send the request a real client sends

Real APIs need real requests: the right method (`POST`, `PUT`, not just `GET`), auth headers, a JSON body. A check that only hits the public, unauthenticated surface is testing a different API than your users call. And the request has to defeat caching, or a CDN will happily serve you a stale `200` forever.

### Watch latency with a caller's budget

APIs get called in loops and chains. A 2-second endpoint becomes a 20-second page. API latency budgets are tighter than a webpage's, and the threshold should reflect what the *callers* can tolerate, not what feels fast in isolation.

### Check from more than one place

Confirm from several regions so one network blip doesn't page you — and so you can tell "the API is down" apart from "the API is down *from Frankfurt*."

## How the WatchFor API monitor does it

We shipped a dedicated [**API monitor**](/api-monitoring) built around exactly this: a full HTTP monitor — SSL, timing breakdown, redirects, auth, HTTP/3 — with a response-validation engine on top.

### Build it live, don't type JSONPaths blind

The part we're proudest of. In the monitor's settings, **Run & inspect** sends the request **once from a real monitoring probe** and shows you the actual response — status, timing, size, headers, and the JSON body flattened into a list of fields (`resources.core.remaining = 60`). Then:

- Click **Assert** on any field to pin it as a rule — the JSONPath is filled in for you.
- Click **Extract** on any number to chart it — again, no typing.

Because it runs through the same prober and engine a scheduled check uses, what you see building the monitor is exactly what the monitor will do. (Want to try the request part with no account? The free [API Tester](/api-tester) runs a real request from a probe and shows you the same flattened response.)

### Assertions read the body

Each assertion is a row — source, path, operator, value:

| 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 | — | does not contain | `error` |

When a check goes red, the detail page and each check's **View response** dialog show *every* assertion, pass or fail, with **expected vs. actual** — so you see which rule broke and what the API actually returned, not just that "something" failed.

### Extraction turns a field into a chart

Point a JSONPath at a numeric field, name it, and it becomes a metric stored on every check:

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

Each gets **its own chart on its own scale** (so a reset-timestamp of `1786203625` never flattens a `used = 0` next to it), and each is **alertable on a threshold** just like response time — `queue_depth > 1000` warning, `> 5000` critical. Booleans map to `1`/`0`, so a `{"healthy": true}` flag charts and alerts too. It's a time series pulled straight out of your API.

### Status, latency and SSL live where every monitor's do

One deliberate design choice: **status code, response time and SSL expiry aren't assertions.** They're evaluated on the **Alerting** tab, with the same friendly controls as every other monitor type — pick the status classes you consider healthy (`2xx`, `3xx`), set a latency threshold, get warned before a certificate lapses. Assertions are reserved for the response *content*. It keeps "is the transport healthy?" and "is the payload correct?" as separate, legible questions instead of one tangled rule list.

## The honest limits

An API monitor validates **one request, deeply**. That's the right tool for the vast majority of "is this endpoint returning the right thing?" questions — and it's a genuinely different tool from a status-only check. What it isn't (yet) is a scripted multi-step journey — *log in, place an order, poll until shipped*. If your critical path only breaks in sequence, that's a browser-flow shaped problem, and it's on our roadmap rather than in this monitor today. We'd rather say that plainly than imply a single check covers a login-then-act chain.

## The bottom line

| Principle | In one line |
| --- | --- |
| A 200 isn't a health check | It describes the transport, not the truth. |
| Assert on the body | Require what success looks like; forbid what failure looks like. |
| Trend the numbers | Some outages creep — chart the value and alert before the wall. |
| Send real requests | Right method, real auth, cache defeated — or you're testing a different API. |
| Separate transport from payload | "Is it up?" and "is it correct?" are two questions. Keep them legible. |

Your API is the quiet workhorse the rest of your product leans on. Watch what it *says*, not just that it *answered* — because the silent 200 is the outage nobody sees coming.

Related reading: the [API Monitoring Guide](/blog/api-monitoring-guide) for the fundamentals, [HTTP status codes explained](/blog/http-status-codes-explained), and, if you like engine-room detail, [how we schedule 25 million checks](/blog/scheduling-25-million-checks-one-go-binary).

---

Canonical page: https://watchfor.io/blog/api-monitoring-beyond-status-codes · All articles: https://watchfor.io/blog/all.md · Site guide: https://watchfor.io/llms.txt
