upti.my
All Articles
API Monitoring··6 min read

Why HTTP 200 Is Not a Health Check

Your API returns 200 OK, but is it actually healthy? Learn why status codes lie and what to check instead.

Every monitoring tutorial starts the same way: "Create an endpoint that returns 200 OK." And every production outage eventually teaches you why that's not enough.

The 200 OK Lie

Your API server is running. It responds to requests. It returns HTTP 200. And yet, your users are seeing errors. What gives?

HTTP status codes describe the HTTP transaction, not your application's health. A 200 means "I received your request and sent a response." It says nothing about whether that response is correct, useful, or complete.

⚠️

The Problem

A 200 OK tells you the server is running. It doesn't tell you the app is working.

Real Examples of Healthy 200s That Broke Production

  • Database connection pool exhausted: The API returns { "users": [] } instead of actual user data. HTTP 200. Application broken.
  • Cache server unreachable: Responses take 8 seconds instead of 80ms. HTTP 200. Users leaving.
  • Downstream service returning errors: Your API wraps the error in a 200 response with { "error": "service unavailable" }. HTTP 200. Features broken.
  • Misconfigured feature flag: Health endpoint works, but the main product returns the wrong version. HTTP 200. Users confused.

What to Check Instead

A proper health check validates that your application can actually serve its purpose. Here's what that looks like in practice.

1. Response Body Validation

Don't just check that you got a response. Check that the response contains expected data:

health-check.test.ts
// Bad: Only checks status
expect(response.status).toBe(200);

// Good: Validates response structure
expect(response.status).toBe(200);
expect(response.body.data).toBeDefined();
expect(response.body.data.length).toBeGreaterThan(0);

2. Response Time Thresholds

A slow response is often a precursor to no response. Set thresholds:

monitor-config.json
{
  "assertions": [
    { "type": "status", "value": 200 },
    { "type": "responseTime", "operator": "lt", "value": 2000 }
  ]
}

3. Deep Dependency Checks

Your health endpoint should verify critical dependencies:

health.ts
export async function healthCheck() {
  const checks = await Promise.all([
    checkDatabase(),
    checkCache(),
    checkDownstreamAPI()
  ]);
  
  const unhealthy = checks.filter(c => !c.healthy);
  
  return {
    status: unhealthy.length === 0 ? 'healthy' : 'degraded',
    checks: checks,
    timestamp: new Date().toISOString()
  };
}

Pro Tip

Keep dependency checks fast. If a dependency is slow, that's a health signal in itself. Set timeouts on each check.

4. Functional Validation

The best health check exercises real functionality. Instead of a synthetic /health endpoint, monitor an actual API endpoint that represents core functionality:

  • E-commerce API: Monitor the product listing endpoint
  • SaaS app: Monitor the user authentication flow
  • Data API: Monitor a representative query

Setting This Up in upti.my

You can configure all of this in the dashboard. Create a healthcheck and add assertions that go beyond status codes:

  1. Create an application for your service
  2. Add a healthcheck with your API endpoint
  3. Add assertions for status, response time, and JSON body validation
  4. Set up alerting to Slack, Discord, or PagerDuty

You can validate that specific JSON paths exist, match patterns, or contain expected values. If the response structure changes or data is missing, you get alerted.

📌Key Takeaways

  • 1HTTP 200 means the server responded, not that the response is correct
  • 2Always validate response bodies, not just status codes
  • 3Set response time thresholds to catch degradation early
  • 4Health endpoints should verify dependencies, not just return static responses
  • 5The best health check exercises real application functionality

The goal of monitoring isn't to prove your server is running. It's to prove your service is delivering value to users. Design your health checks accordingly.