Apdex is the score a dashboard shows when it wants to say "the app is fine" in one number. It compresses a whole latency distribution into a single value between 0 and 1, which is genuinely useful for a status page or a board slide and genuinely misleading if you try to debug with it. Worth understanding both for what it tells you and for what it quietly hides.
The formula
satisfied + (tolerating / 2)
Apdex = ──────────────────────────────
total
where:
satisfied = requests that completed in ≤ T
tolerating = requests that completed in ≤ 4T but > T
frustrated = requests that completed in > 4T (counted as 0)If you pick T = 500ms, then a request that finishes in 300ms is satisfied, 1,500ms is tolerating, and 5,000ms is frustrated.
A worked example
10,000 requests over an hour with T = 500ms:
Satisfied (≤ 500ms): 8,200 requests
Tolerating (500-2000ms): 1,500 requests
Frustrated (> 2000ms): 300 requests
Total: 10,000 requests
Apdex = (8200 + 1500/2) / 10000
= (8200 + 750) / 10000
= 0.895
Verdict: "Good" (0.85 - 0.94 range)Conventional Apdex ratings
| Apdex score | Rating |
|---|---|
| 0.94 - 1.00 | Excellent |
| 0.85 - 0.93 | Good |
| 0.70 - 0.84 | Fair |
| 0.50 - 0.69 | Poor |
| < 0.50 | Unacceptable |
How to pick T
T should reflect what users find responsive for this class of request:
- Plain API endpoints: 200-500ms
- Web pages with render: 1-2s
- Dashboard-style interactive views: 1-3s
- Report generation, large data exports: 5-10s
- Background async jobs: Apdex usually doesn't apply — use duration percentiles
Pick T per endpoint class. A single global T gives you a number but destroys the signal.
Two services, same Apdex, different problems
The weakness is easiest to see with an example. Both of these score 0.90 at T = 500ms:
Service A: 90% of requests finish in 100ms, 10% take 5 seconds. Fast for almost everyone, catastrophic for a tenth of traffic — that's a tail problem, probably an N+1 or a lock hitting a subset of requests. Apdex = (0.90 + 0) = 0.90.
Service B: 80% finish at 450ms, 20% at 1.5s. Nobody is having a catastrophe, but nothing is crisp either — more likely a baseline that's drifted up across the board. Apdex = (0.80 + 0.20/2) = 0.90.
Same headline score, two different investigations. That's why a sane setup keeps Apdex for the summary and reaches for p95/p99 the moment something's actually wrong.
Why percentiles often beat Apdex
Apdex compresses two numbers (how many were fast, how many were frustrated) into one. That's useful for execs — "site health is 0.91" — but bad for debugging. An Apdex of 0.85 could mean "most requests are fast, a few are catastrophic" or "most requests are mediocre, none are catastrophic." These need different fixes.
Raw percentiles (p50, p95, p99) expose the shape of the distribution. See our p95 vs p99 explainer.
Apdex in a Laravel APM
Most APMs compute Apdex per route over a rolling window. NightOwl shows both Apdex and p50/p95/p99 side by side per route, so you can use Apdex for executive summaries and percentiles for debugging.