Monitoring REST and GraphQL APIs with OpenTelemetry means instrumenting the HTTP and GraphQL layer to emit traces and metrics over OTLP, so API health, latency and errors are fully observed..
Monitoring REST and GraphQL APIs with OpenTelemetry means instrumenting the HTTP and GraphQL layer to emit traces and metrics over OTLP, so API health, latency and errors are fully observed.
APIs are the contract with your consumers, so the golden signals apply directly: request rate, error rate, latency (p50/p95/p99) and saturation, per endpoint and, for GraphQL, per operation and resolver. GraphQL adds a twist, a single HTTP endpoint hides many operations, so aggregate endpoint metrics tell you almost nothing and you need resolver-level detail to find what is slow.
The reading that resolves most API incidents is per-endpoint (or per-operation) latency percentiles alongside the downstream call inside the request, because API latency is usually a specific slow dependency, and for GraphQL a specific slow resolver, not the API layer itself.
REST APIs are well covered by OpenTelemetry HTTP auto-instrumentation, so each route emits a server span and metrics over OTLP with little effort, and downstream calls become child spans. For GraphQL you add the GraphQL instrumentation for your server, which creates a span per operation and per resolver, so you can see which query and which resolver is slow behind the single endpoint.
The GraphQL-specific caution is cardinality and naming: capture the operation name (not just the endpoint) so operations are distinguishable, and be careful that resolver-level spans on high-volume APIs can generate a lot of data, so sample sensibly while keeping the operation-level metrics complete.
The signals that explain most API incidents:
| Signal | Source | What it tells you / watch for |
|---|---|---|
http.server.request.duration | HTTP instrumentation | Per-endpoint latency histogram; for REST this is your primary latency signal. |
error rate by route / operation | HTTP / GraphQL instrumentation | 4xx and 5xx per endpoint or GraphQL operation; separates client from server faults. |
GraphQL operation duration | GraphQL instrumentation | Latency per named operation, the GraphQL equivalent of per-endpoint timing. |
GraphQL resolver duration | GraphQL instrumentation | Latency per resolver; reveals the specific field that is slow behind an operation. |
resolver call count per request | GraphQL instrumentation | Many calls to one resolver in a request is the GraphQL N+1 pattern. |
downstream span duration | client instrumentation | Latency of DB and service calls inside a request; usually where the time goes. |
An API dashboard should work for both REST and GraphQL. Row one: request rate and error rate by endpoint or operation, and p95/p99 latency by endpoint or operation. Row two, for GraphQL: slowest resolvers and resolver call counts per request to catch N+1. Row three: downstream call latency by target, since that is where most API latency actually lives.
# p99 latency by endpoint (REST) or operation (GraphQL)
histogram_quantile(0.99, http.server.request.duration) by (http.route)
# error rate by route/operation
sum(rate(http_server_requests{status=~"5.."}[5m])) by (http.route)
/ sum(rate(http_server_requests[5m])) by (http.route)
# GraphQL resolver latency (find the slow field)
histogram_quantile(0.95, graphql.resolver.duration) by (field)
A GraphQL API looks fine at the endpoint level but specific queries are slow, because one resolver is expensive. Resolver-level spans expose the slow field, which aggregate endpoint metrics never would. Optimise or cache that resolver.
One operation is slow and its trace shows a resolver called many times, once per item in a list, each making its own database call. The resolver call count per request is the tell. Batch the loads (a dataloader pattern) so the list resolves in one query, not N.
An endpoint's p99 rises and the added time is in a specific downstream span, a database query or an upstream API, while the API layer itself is fast. The fix is in the dependency; the trace makes that unambiguous rather than leaving you to tune the API.
Starting thresholds to tune:
| Condition | Signal | Meaning |
|---|---|---|
| 5xx rate by route/operation > 2% for 5m | errors | An endpoint or operation is failing. |
| p99 latency > SLO for 10m | latency | An endpoint or operation regressed. |
| resolver call count per request spiking | GraphQL | An N+1 pattern has appeared. |
| downstream p95 > SLO | dependency | A dependency is the bottleneck. |
Ops Singularity's TelemetryOps ingests API telemetry over OpenTelemetry, and Sentinel AI resolves the API incidents it reveals through governed Action Tickets validated by Sherlock. Explore TelemetryOps and ServiceOps.
Use OpenTelemetry auto-instrumentation for your HTTP framework so each route emits traces and metrics over OTLP, then watch per-endpoint latency percentiles and error rates.
Use the GraphQL instrumentation for your server so each operation and resolver becomes a span, revealing which query and resolver is slow behind the single GraphQL endpoint.
Bring a real incident. We will show you Sentinel investigate, act and verify end to end, with every action reversible and audited.