01 · Definition
Apdex scores how many users were served fast enough
Apdex (Application Performance Index) is an open standard that turns a set of response times into a single satisfaction score between 0 and 1. You pick one target response time, T. Every request is then counted as satisfied, tolerating, or frustrated, and the score is the weighted share that ended up happy.
Apdex comes from a published specification, so the same traffic scores the same way in any tool that implements it. The only thing you configure is T.
A score of 1 means every request beat the target and 0 means none did. Errors count as frustrated whatever their duration, which is the part that catches people out.
Apdex answers a different question than a p95 alert
A p95 alert says
“The slow 5% crossed one second.”
That is the shape of the tail. It stays silent when requests fail quickly, and it says nothing about whether the slow 5% was 5 requests or 50,000.
An Apdex alert says
“One request in five was slow or broken.”
That is the size of the tail, with failures counted as unhappy users. It is closer to the sentence you would use to describe the incident to someone else.
02 · The formula
The Apdex formula: three bands, one target time
You choose T, the response time at which users stop perceiving the response as immediate. Every request in the measurement window then lands in one of three bands, and the band decides how much credit the request earns. The upper boundary is never configured separately: the tolerating band always ends at 4T.
Finished faster than T. The user got what they asked for and did not notice the wait. Worth one point.
Between T and 4T. Slow enough to feel, not slow enough to leave. Worth half a point.
Slower than 4T, or failed at any speed. Worth nothing.
Always between 0 and 1. Both band boundaries come from the single value you set for T.
A worked example
In a five-minute window a service handles 10,000 requests against T = 500ms. 9,000 finish under 500ms, 800 finish between 500ms and 2s, 150 take longer than 2s, and 50 return an error.
Note where the 200 slow-or-failed requests went. The 50 errors score zero alongside the 150 timeouts, because a 200ms 500 Internal Server Error did not satisfy anybody. That single rule is most of the reason to track Apdex next to your latency percentiles.
03 · The one input
How to choose T, the Apdex target time
T is the whole rule. It sets the satisfied boundary directly, and the frustrated boundary follows from it, because the tolerating band always ends at 4T. Move T from 500ms to 1s and you have also moved the frustrated line from 2s to 4s. Two teams reporting an Apdex of 0.95 against different targets are not reporting the same thing.
Pick T as the latency at which your users stop perceiving the response as immediate, per class of traffic:
200ms – 300ms
Internal or service-to-service APIs
They sit inside someone else's request, so their share of the end-user budget is small.
500ms
User-facing API endpoints
A fair starting point for a JSON API behind a UI, and the default Maple ships with.
1s – 2s
Full page loads and heavy reports
Users accept more from something that visibly does more work.
Not applicable
Background and batch endpoints
Nobody is waiting. Alert on throughput or failure rate instead of on satisfaction.
Two ways to pick a T that tells you nothing
Setting T to your current p95 guarantees a score near 0.95 forever. Setting it so tight that a healthy week already reads 0.6 buries every threshold you might pick inside your normal noise.
The honest way to choose is empirical: set T, look at a healthy week, and check that the score reads somewhere in the 0.9 to 1.0 band. That leaves room below for a threshold to mean something. If the chart is pinned at 1.0, T is too generous; if it never gets near 0.95, T is too tight or the service is genuinely slow.
04 · Interpretation
What is a good Apdex score?
The conventional bands below are a reasonable default, and they are the ones most tools ship with. Read them as relative to the T you chose, not as absolutes.
Every request beat T. Usually a sign that T is too generous rather than that the service is perfect.
Healthy, with a normal slow tail.
Degraded, and users can feel it.
The common alerting line. Roughly a fifth of traffic is frustrated or worse.
Most requests are failing or timing out.
0.8 is a convention, not a law. The number that matters is where your service sits when it is healthy, and how far it has to fall before you would want to be woken up. Publish T next to any score you share; without it the number is not comparable to anyone else's.
05 · Comparison
Apdex vs p95 latency: where the two disagree
A p95 alert fires on the shape of the tail; Apdex fires on its size. Most of the time they agree — if a bad deploy makes 30% of requests take 3 seconds, a p95 alert at 1s fires and so does Apdex. The cases worth knowing are the ones where they disagree.
| Situation | p95 latency | Apdex (T = 500ms) |
|---|---|---|
| 30% of requests slow to 3s | Fires | Falls sharply |
| A dependency fails 15% of requests in 40ms | Gets faster, stays quiet | 0.97 → about 0.83 |
| The slowest 3% go from 4s to 40s | Moves a long way | Unchanged |
| Traffic drops to a handful of requests | Noisy | Noisy — needs a minimum sample count |
The second row is the case only Apdex catches: a fast failure is still a frustrated user, so error-driven regressions show up as an Apdex drop while the latency chart looks like an improvement. The third row is the case only percentiles catch: Apdex is a ratio, so a service where the slow 3% take 4 seconds and one where they take 40 seconds score identically.
In practice you want one of each. Apdex is the better thing to page on, because it counts how many people were affected, and a p99 investigation is what you open once you are awake and need to know how bad it got.
06 · Caveats
Where Apdex falls short
It hides magnitude
The score counts frustrated requests without weighting how frustrated they were. Pair it with a p99 signal if the worst case matters.
One T per measurement
A service whose health check and report export share a target is being measured against a number that fits neither. Group by route, or measure them separately.
Sampling shifts it
Under uniform sampling the ratio stays representative. A policy that preferentially keeps slow or failed traces will drag the score below reality.
Low traffic is noisy
A handful of requests moves a ratio a long way. Require a minimum sample count before a score is allowed to trigger anything.
It measures what you instrument
A service that swallows failures internally and returns success from its entry point looks healthy at any threshold.
The bands are a convention
The 4T frustrated boundary and the 0.8 alerting line are inherited defaults, not properties of your users.
07 · In practice
Measuring Apdex from your existing traces
Apdex needs no special instrumentation. If a service already emits OpenTelemetry traces, every ingredient is present: each entry-point span carries a duration and a status, which is exactly the satisfied / tolerating / frustrated decision.
- 01
Pick the request population
Score the spans that represent a request arriving — server and consumer spans, and trace roots — not every internal span. Counting internal work inflates the denominator with requests nobody made.
- 02
Set T per class of traffic
One target per service is usually too coarse. Group by route or operation so a health check and a report export are not judged against the same number.
- 03
Count errors as frustrated
Any span with an error status scores zero regardless of duration. Skip this and you have built a latency metric wearing an Apdex label.
- 04
Calibrate against a healthy week
Check that normal operation reads between 0.9 and 1.0 before you attach a threshold to it.
- 05
Alert with a floor and a delay
Require a minimum number of samples per window and two consecutive bad checks. Both exist to stop a quiet night from paging someone.
Apdex in Maple
A score on every service, and an alert rule behind it
Maple computes Apdex over the entry-point spans of each service from the OpenTelemetry data you already send, draws it on the service page, and ships a Low Apdex score alert template — target 500ms, fires below 0.8 over five minutes, every field editable. The setup walkthrough, the tuning fields that keep it from paging you at 3am, and the API body are in the alerting docs.
Sources and further reading