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