All posts
Monitoring6 min readWatchFor Team

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

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.

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

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 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 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:

SourcePropertyOperatorValue
JSON bodydata.statusequalsok
JSON bodydata.items.#greater than0
JSON bodydata.healthyequalstrue
HeaderContent-Typecontainsjson
Response textdoes not containerror

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

PrincipleIn one line
A 200 isn't a health checkIt describes the transport, not the truth.
Assert on the bodyRequire what success looks like; forbid what failure looks like.
Trend the numbersSome outages creep — chart the value and alert before the wall.
Send real requestsRight 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 for the fundamentals, HTTP status codes explained, and, if you like engine-room detail, how we schedule 25 million checks.

Share this article