Performance metrics 10 min read

What is Apdex, and how do you use the score?

Apdex compresses a latency distribution into one number between 0 and 1: the share of requests that were fast enough to keep a user happy, with failed requests counted as unhappy users.

By Maple
apdex / at a glance 0 → 1
  1. 01
    Formula one point, half a point, none

    (satisfied + ½ tolerating) / total

  2. 02
    Input e.g. 500ms

    A single target time, T

  3. 03
    Bands frustrated beyond, or failed

    Satisfied < T · Tolerating < 4T

  4. 04
    Healthy with T chosen honestly

    0.94 and above

  5. 05
    Page-me a fifth of traffic suffering

    Below 0.80 for 5 minutes

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.

Satisfied

Finished faster than T. The user got what they asked for and did not notice the wait. Worth one point.

Tolerating

Between T and 4T. Slow enough to feel, not slow enough to leave. Worth half a point.

Frustrated

Slower than 4T, or failed at any speed. Worth nothing.

Apdex = (satisfied + 0.5 × tolerating) / total

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.

9,000 satisfied
800
9,000 under 500ms 800 between 500ms and 2s 150 over 2s 50 errors
(9,000 + 0.5 × 800) / 10,000 = 0.94

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.

1.00

Every request beat T. Usually a sign that T is too generous rather than that the service is perfect.

0.94 – 0.99

Healthy, with a normal slow tail.

0.85 – 0.94

Degraded, and users can feel it.

below 0.80

The common alerting line. Roughly a fifth of traffic is frustrated or worse.

below 0.50

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.

Situationp95 latencyApdex (T = 500ms)
30% of requests slow to 3sFiresFalls sharply
A dependency fails 15% of requests in 40msGets faster, stays quiet0.97 → about 0.83
The slowest 3% go from 4s to 40sMoves a long wayUnchanged
Traffic drops to a handful of requestsNoisyNoisy — 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

01

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.

02

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.

03

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.

04

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.

05

It measures what you instrument

A service that swallows failures internally and returns success from its entry point looks healthy at any threshold.

06

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.

  1. 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.

  2. 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.

  3. 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.

  4. 04

    Calibrate against a healthy week

    Check that normal operation reads between 0.9 and 1.0 before you attach a threshold to it.

  5. 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

FAQ

Common questions

What does Apdex stand for?
Apdex stands for Application Performance Index. It is an open standard, originally published by the Apdex Alliance, that condenses a set of response times into a single satisfaction score between 0 and 1 using one configured target response time called T.
What is the Apdex formula?
Apdex = (satisfied + 0.5 x tolerating) / total. A request is satisfied if it finished faster than the target time T, tolerating if it finished between T and 4T, and frustrated if it took longer than 4T or failed. Satisfied requests score one point, tolerating requests score half a point, frustrated requests score nothing.
What is a good Apdex score?
Above 0.94 is generally considered healthy and 0.8 is the usual line for alerting, but both numbers only mean something relative to the T you chose. A score measured against a 2 second target is not comparable to one measured against a 300 millisecond target, so publish T alongside the score.
Do failed requests count against the Apdex score?
Yes. A request that returns an error counts as frustrated no matter how fast it was, because a 200 millisecond 500 Internal Server Error did not satisfy anybody. This is what makes Apdex a proxy for user experience rather than a pure latency statistic, and it is why Apdex catches fast-failing dependencies that a latency alert sleeps through.
Should I alert on Apdex or on p95 latency?
Both, for different reasons. Apdex measures how many users had a bad time, including the ones whose requests failed, so it is the better single page-me signal. Percentiles measure how bad the slow tail got, which is what you want once you are already looking. Alert on Apdex, debug on p99.
Why is my Apdex score flat at 1.0?
The target time T is larger than nearly every request the service serves, so everything lands in the satisfied bucket. Lower T until a healthy week reads somewhere between 0.9 and 1.0. Otherwise the score has no room to move and any threshold you set below it can never be reached.

See your first trace today.

Add the SDK, point OTLP at Maple, and watch traces arrive — most setups take under five minutes.

maple.dev — observability on OpenTelemetry