Skip to content

By the Octri team · Updated

How to set up an API health check: liveness, readiness and uptime checks

It's tempting to call an endpoint /health, return 200, and consider monitoring finished. That endpoint may tell you the process is still running. It may tell you almost nothing about whether the API can serve an actual customer.

A useful health check starts with the decision its result will trigger. Restarting an unhealthy process, taking an instance out of rotation, and warning an on-call engineer are three different actions. They need different signals.

How do you set up an API health check?

Give each check one job. Use /livez to show that the process is alive. Use /readyz to say whether this instance can handle traffic. Use an external uptime monitor to verify that a real caller can reach the public API.

Check Meaning Typical action
Liveness The process can respond Restart a stuck process
Readiness Required dependencies are usable Stop routing new traffic to this instance
Uptime The public service responds correctly and quickly Open an incident or page someone

The distinction matters on Kubernetes. A failed liveness probe can restart the container; a failed readiness probe removes it from service endpoints. Kubernetes warns against making liveness depend on an external system, because restarting every healthy application instance won't repair a database outage.

Four endpoints, four failures

For this guide I ran a small local HTTP server with a deliberately slow dependency. The same process answered three paths. /livez returned immediately; /readyz treated the dependency as unavailable once its 50 ms allowance was exceeded; /naive waited the full simulated 180 ms and then returned success.

The results were revealingly ordinary:

Probe Database slow Elapsed in the local test
/livez 200 ~1 ms
/readyz 503 ~51 ms
/naive 200 ~181 ms

The naive endpoint's answer was technically sincere: its database call eventually finished. Operationally, the answer was unhelpful. The API was already taking longer than its intended dependency budget, and the check did not report that fact.

This is a controlled example, not a measurement of a production database. A real readiness probe must enforce the deadline on the actual dependency call, not merely sleep and decide afterward.

Diagram explaining liveness, readiness, naive deep checks, and external uptime monitoring
Each probe should answer one operational question; the diagram uses an illustrative timing scenario.

What a readiness check should do

First decide which dependencies make the instance unable to serve its primary function. A write-heavy orders API may need its database, but might still accept orders while a noncritical email provider is down. Return an overall failure only for dependencies that prevent the important requests from succeeding.

Set a short timeout per dependency and run independent checks concurrently. Do not allow one stuck network call to stall the health endpoint itself. A response can be both compact and diagnostic:

http
HTTP/1.1 503 Service Unavailable
Content-Type: application/health+json
Cache-Control: no-store

{
  "status": "fail",
  "checks": {
    "database:responseTime": [
      {"status": "fail", "observedValue": 500, "observedUnit": "ms"}
    ],
    "cache:responseTime": [{"status": "pass"}]
  }
}

A health check shouldn't run an expensive full-table query or perform a write on every probe. Do the smallest operation that exercises the dependency you need. If a dependency is optional, mark it as a warning in the body and keep the overall status consistent with your platform's routing decision.

The health check response format

A bare 200 or 503 is enough for most load balancers. For humans and richer monitors, the Health Check Response Format for HTTP APIs describes a JSON structure with an overall status and named checks. It is an Internet-Draft, not a universally adopted standard; decide whether the format suits your consumers.

Avoid returning internal connection strings, database hostnames, secrets, or full exception messages to an unauthenticated public endpoint. A public probe can receive a simple status, while an internal probe or observability system retains the detailed reason.

Cache-Control: no-store is also sensible. You don't want an intermediary serving a healthy answer that predates the failure.

Monitor it from outside

Internal readiness probes can all be green while users cannot resolve your domain, complete TLS, or reach the load balancer. An external uptime check fills that gap. It should call a representative public route, from more than one location if your audience is geographically distributed.

Choose the interval, timeout, acceptable status, and latency budget intentionally. A response can be 200 and still arrive too late to be useful. For a latency-sensitive endpoint, counting a 300 ms success as healthy against a 100 ms budget gives your callers a more honest signal than status alone.

Octri uptime-check settings with interval, timeout, accepted status and latency budget
The same URL can be healthy or unhealthy depending on status, timeout, and response-time budget.

Test the monitor's failure path as well as its success path: break a non-production dependency, verify the check fails, see whether the alert reaches the right person, and confirm that a recovery message appears when the system returns to normal.

Octri uptime history showing checks that exceeded the configured latency limit
A successful HTTP status can still miss a performance requirement.

If the API offers hundreds of routes, start with a few that represent important workflows. One health endpoint cannot prove that every authenticated or write operation works, so pair the uptime checks with real-traffic error and latency monitoring.

Checklist for API health checks

  • Keep liveness shallow and free of remote dependencies.
  • Make readiness reflect only dependencies required to serve traffic.
  • Enforce explicit deadlines on dependency calls.
  • Use 503 when the instance should stop receiving new requests.
  • Return a small, safe response body that names failing checks.
  • Set Cache-Control: no-store for frequently changing health responses.
  • Monitor a representative public URL from outside your infrastructure.
  • Treat unexpectedly slow 200 responses as failures if they break the service budget.
  • Verify alert delivery and recovery before the system goes live.

API health checks with Octri

Octri Monitoring can probe public API endpoints on a schedule, track their latency and uptime, and show the result on a public status page. You can configure acceptable status codes, timeouts, and a latency budget for each check. It can also generate checks for suitable GET operations from an OpenAPI specification.

An external check complements the application's /livez and /readyz endpoints rather than replacing them. If you keep those roles separate, the check that restarts a process won't trigger a restart storm just because a shared dependency is struggling.

Keep reading