eBPF Continuous Profiling released

Last updated:

What Is API Monitoring?

API monitoring checks whether an API is reachable, responds quickly enough, and actually does what its caller asked. The last part is where an apparently simple HTTP check becomes an exercise in reading the small print. A gRPC request can return HTTP 200 while the RPC fails, GraphQL can include an errors array, and a REST endpoint can return a neatly formatted failure under 200 OK. All three can leave a status-only dashboard looking remarkably cheerful.

You need more than a status-code check to catch that sort of failure. Synthetic checks exercise the API from outside, server-side telemetry shows how real requests behave, and caller-side measurements capture what your services experience when they depend on other APIs. None of those views tells the whole story on its own.

What API monitoring actually measures

API monitoring comes down to three questions:

  • Availability: Can callers reach the API from the places they operate? Availability monitoring can test anything from basic reachability to an entire transaction.
  • Latency: How long does the operation take, including the slow requests that get lost in an average?
  • Correctness: Did the API return the expected data, in the expected shape, and complete the operation it was supposed to perform?

You'll find lists of API monitoring “types” that mix uptime, performance, synthetics, real user monitoring, validation, and compliance. The problem is that these aren't all the same kind of thing. Availability and correctness are goals. Synthetic checks and server metrics are ways of finding out whether you met them. Keeping that distinction straight makes it easier to choose an appropriate monitoring setup. API security monitoring has related but different objectives; see also observability vs monitoring.

Why 200 OK isn't quite the endorsement it sounds like

A status-only check can confirm that an HTTP exchange completed. It cannot always tell whether the API operation succeeded, because the meaning of success depends on the protocol and the response body.

  • gRPC: A gRPC server normally uses HTTP/2 :status: 200 for calls it handles, including calls that fail at the RPC level. The actual outcome is in the grpc-status trailer, with codes from 0 to 16 as described in gRPC's status codes. A broken proxy may return a non-200 HTTP status instead, as the protocol specification explains. Watch only the HTTP status and you'll miss the failures that gRPC itself is reporting.
  • GraphQL: Many servers return 200 with an errors array, sometimes alongside partial data. The newly released GraphQL-over-HTTP specification introduces more expressive HTTP behavior with application/graphql-response+json: request errors without data receive a 4xx or 5xx, while 294 Partial Success is recommended for responses containing both data and errors. That doesn't mean every existing server suddenly speaks 294. The code is not yet registered with IANA, and, in any case, 294 is still a 2xx. A monitor that accepts all 2xx responses will happily pass a partial failure.
  • REST: Some endpoints return 200 with an error object in the body, despite HTTP's established error-status semantics. No status-only monitor can discover a failure the API has decided to describe as success.

Check the actual contract. For REST, assert that required fields exist and contain valid values. For GraphQL, inspect the requested data and the errors array, allowing partial results only if the operation is designed to accept them. For gRPC, inspect grpc-status, not merely HTTP status. And when the result matters, verify the result itself: a syntactically valid response containing the wrong account balance is still wrong.

This also applies to your error-rate graphs. A gRPC service can report failed operations while HTTP-only instrumentation says everything is fine. Use RPC-aware instrumentation for RPC outcomes, and don't assume every 200 represents a successful business operation.

Three places to observe an API

The useful question is not just what you measure, but where you stand while measuring it. The API server has excellent information about requests it receives. It has rather less to say about a request that died in DNS before getting anywhere near it.

Google's SRE book distinguishes black-box monitoring, which tests behavior from outside, from white-box monitoring, which examines the system's internals. For APIs, there's also a third useful perspective: the code making the call.

Outside-in: synthetic checks

Synthetic checks catch failures without waiting for real traffic. They send requests on a schedule from selected locations and assert on the result. You configure the method, URL, authentication, headers and body; choose how often and where the check runs; and decide what counts as success. Some checks can chain requests, for example by logging in before fetching a protected resource.

A synthetic check can discover that a TLS certificate expired, DNS is broken, or a newly deployed endpoint no longer works. These can all happen while the server's own request metrics look perfectly peaceful. Nobody has managed to reach it to make those metrics interesting.

The limitation is that synthetics only test the requests you've written. A passing check with a tidy payload says nothing about the unusual headers or parameters a real client sends. And a check that runs once a minute is not a substitute for measuring the distribution of latency across thousands of real requests.

There are practical details worth getting right. More frequent checks detect failures sooner but use more quota and rate limit. Use separate test credentials, control test data, and ensure write operations don't leave real side effects behind. A synthetic check that creates an actual order every minute is a rather expensive definition of availability. If its token expires, you'll get a failure that looks like an outage until you inspect the response. Tag synthetic requests using a recognizable header or User-Agent, then filter or separate them when analyzing real production traffic. The synthetic monitoring guide covers retries and alert severity in more detail. For a tool-level comparison, see the best synthetic monitoring tools.

Server-side: the requests that actually arrive

Server-side monitoring tells you what real traffic did, route by route. It captures payloads and clients your synthetic checks never anticipated, and it gives you enough observations to distinguish normal latency from a slow tail.

OpenTelemetry's stable http.server.request.duration histogram measures request durations. Combined with its attributes, it supports request counts, latency distributions and error analysis. Use http.route, such as /users/{id}, rather than individual URL paths: the route template lets you group requests without creating a separate metric series for every user ID. Look at latency percentiles and separate successful requests from failed ones. An endpoint that starts failing quickly can make its average duration look better, which is not the improvement anyone was hoping for. For the broader model, see the four golden signals and SLA, SLO and SLI.

Be precise about errors, too. Under OpenTelemetry's HTTP span status rules, a server-side 4xx response doesn't normally set the span status to Error. An error rate derived from error.type or error span status therefore won't necessarily move when a client rollout produces a flood of 401 or 429 responses. Track http.response.status_code separately if those outcomes matter to your users.

And remember what the server can't know. If the application is down, it isn't producing its own metrics. If TLS or DNS fails before the request arrives, it doesn't see that either. The gateway or load balancer may still record the 502 or 503 it returned, so collect telemetry at that layer rather than relying on the application to narrate its own absence.

Caller-side: what your services experienced

Caller-side monitoring catches failures that the destination might not consider failures at all. OpenTelemetry's stable http.client.request.duration histogram measures outbound requests from your services. Grouping by server.address helps identify a slow or failing dependency, and browser or mobile telemetry can provide a similar perspective through real user monitoring.

Suppose your client times out and gives up. The server may complete the work a moment later and log 200. Neither observation is necessarily wrong. The caller did fail to get its answer, and that's the failure your users encountered.

For an API you own, caller-side data complements the server's view, particularly when requests cross services. The same distinction matters in microservices monitoring. For a third-party API, caller-side data is your only measurement of real calls, because you don't get to install an agent on someone else's servers. Like server metrics, though, it stays silent when no call is attempted.

When the views disagree

A few failure patterns make the boundaries easier to remember:

  • A quiet-period outage or expired TLS certificate: synthetic checks can report it before real traffic arrives; the application itself may have nothing to record.
  • A rare payload failure or a p99 latency regression: real server traffic exposes patterns that a periodic scripted check is unlikely to sample; callers may see them too.
  • A client timeout followed by a server-side 200: caller telemetry records the timeout, while the server records completed work.
  • An API that returns 200 with protocol-level errors: none of these views is automatically correct unless its checks or instrumentation understand the protocol.

The point isn't to collect three copies of the same request. It's to avoid asking one perspective to explain an event that happened outside its field of view.

Monitoring third-party APIs

For APIs you can't instrument, combine outbound-call measurements with synthetic checks. A payment provider or identity service can be responsible for a significant fraction of your application's latency. You still don't get access to its internal dashboards, however important it is to your architecture.

Use http.client.request.duration grouped by server.address to measure real requests to each provider. Track latency and failures, and look at 429 Too Many Requests separately. Exhausted quota can resemble an unreliable provider when all you look at is a general error rate. If the provider exposes remaining quota, monitor that as well.

Synthetic checks answer a different question: does an endpoint representing the integration still work when your application is quiet? Use separate credentials where possible and keep the schedule inside the provider's rate limits. An enthusiastic monitoring system is perfectly capable of consuming the quota it was supposed to monitor.

If both views show trouble, investigate the provider and the network path to it. Don't assume the provider is guilty, though. An expired credential or network policy shared by your application and the synthetic probe can make both fail together. Compare error details, traces and the provider's status page. If only real calls fail, focus on what differs about your requests, especially credentials, payloads and quotas.

API monitoring vs. API testing

API testing asks whether a build behaves correctly under controlled conditions. API monitoring asks whether the deployed API continues to behave correctly with real dependencies and traffic. Both can use assertions, but a test suite won't automatically warn you that a production TLS certificate expired or a dependency slowed down overnight.

Keep both. Run synthetic checks after deployments to catch obvious integration regressions quickly, then keep them running alongside real-traffic monitoring. Production has a talent for producing inputs and failure conditions your test fixtures didn't think to invite.

How Dash0 monitors APIs

Dash0 covers the outside-in view with synthetic HTTP checks and the server-side view with OpenTelemetry-based service monitoring. You can also send outbound-call metrics from your own services, while Website Monitoring connects browser activity with the backend requests it triggers.

Dash0 synthetic checks run from public regions or private locations. A private worker can reach in-cluster services or internal APIs and connects outbound to Dash0 without requiring inbound access to the network. Checks support status, header and body assertions, including JSONPath or text matching. They also expose timing thresholds for DNS, connection, TLS, request, response and total duration; connection, DNS, TLS and timeout errors; and SSL certificate expiry checks. That means a REST error object or GraphQL errors array behind 200 can be detected by a body assertion, rather than waved through because the status code looks nice.

Results can be Critical or Degraded and use the same notification channels as other alerts. Checks can be managed through the Dash0 Operator, Terraform provider or CLI. They are HTTP checks, not full browser journeys or native gRPC operation checks; for gRPC, rely on protocol-aware application telemetry for RPC outcomes.

Example Dash0 synthetic HTTP check for google.com showing configured status and response-time criteria, uptime, and DNS, TLS, and request timing.

On the server side, Dash0's Service Catalog shows RED metrics: Requests, Errors and Duration, including by HTTP route or gRPC method. Dash0 derives these at ingestion from spans as dash0.spans.red, rather than requiring an http.server.request.duration histogram. The error calculation uses span status, so the earlier warning about server-side 4xx responses still applies.

When a synthetic run fails, you can inspect its assertions, the request and response, error details, and a timing waterfall across DNS, TCP, TLS and the request phases. For a backend instrumented with OpenTelemetry, View full trace follows the request through the gateway and instrumented services to dependencies such as databases. The span attribute dash0.trace.origin.type = SYNTHETIC_CHECK also lets you isolate synthetic traffic or exclude it when investigating real callers. Knowing which check failed is useful. Knowing where the time went is usually what fixes it.

Final thoughts

Start with your API's actual contract, not a generic definition of success as “status less than 400.” Add protocol-aware assertions to synthetic checks on critical endpoints. Measure real traffic per route, including 4xx outcomes that might be absent from span-based error rates. For third-party dependencies, watch the caller's view alongside scheduled checks.

Then be selective about alerts. A failure that persists after a retry, occurs from several locations, or threatens a route's service objective may justify a page. A minor degradation or slow drift usually deserves investigation without waking someone up. The point of the monitoring setup is to find failures users care about, not to discover how many notifications your team can endure.