Sitelet https://maple.dev/docs/alerting/apdex-alerts/
Skip to content
Maple Docs
Open app
Browse the docs
On this page

Apdex alerts

How to set up an Apdex alert in Maple: pick the target latency T, choose the score worth paging for, scope and tune the rule, and create the same rule over the API.

A p95 latency alert tells you that 5% of requests were slower than some number. It does not tell you whether that was 5 requests or 50,000, and it says nothing about the requests that failed outright.

Apdex answers a different question: of everything that hit this service in the last five minutes, what share of it was fast enough to keep a user happy?

A p95 alert says
“The slow 5% crossed 1 second.”
Shape of the tail. Silent when requests fail fast.
An Apdex alert says
“One request in five was slow or broken.”
Size of the tail, counting failures as unhappy users.

The score in one line

Apdex compresses a latency distribution into a single number between 0 and 1: the share of requests that were fast enough to keep a user happy. You set one target time, T. Requests under T are satisfied and score a point, requests under 4T are tolerating and score half, and everything slower is frustrated and scores nothing. A request that failed is frustrated too, at any speed.

Apdex = (satisfied + 0.5 × tolerating) / total
Always between 0 and 1. Both boundaries come from the single value you set for T.

Counting failures as frustrated is what makes this worth alerting on. A dependency that starts failing 15% of requests in 40ms makes your p95 faster, so a latency alert stays quiet while Apdex falls from 0.97 to about 0.83.

What is Apdex? walks through the formula, a worked example, and where Apdex and latency percentiles disagree. The rest of this page is the Maple-specific part: picking T for a rule, and building it.

Choosing T for the rule

T is the whole rule. It sets the satisfied boundary directly and the frustrated boundary implicitly, since 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.

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 budget is small.
500ms
User-facing API endpoints
Maple's default, and a fair starting point for a JSON API behind a UI.
1s – 2s
Full page loads and heavy reports
Users accept more from something that visibly does more work.
n/a
Background and batch endpoints
Nobody is waiting. Alert on throughput or failure rate instead.

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.

To choose, open the service’s Apdex chart in Maple, set T, and check that a healthy week reads somewhere in the 0.9 to 1.0 band. That leaves the 0.8 line meaningful.

Choosing the threshold you defend

0.94 – 1.00 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 common convention. Set the threshold from where your service sits when it is healthy, and how far it has to fall before you would want to be woken up.

Setting up an Apdex alert in Maple

Maple computes Apdex over the entry-point spans of a service: server spans, consumer spans and trace roots. The service’s Apdex chart draws the same set of requests, so a rule you build here matches the chart.

1. Start from the Low Apdex score template

Open Alerts → New rule. Starting from a service page instead (Services → your service → Create Alert) pre-fills the scope.

The rule builder opens on Start with a template. Pick Low Apdex score and every field below is filled: signal Apdex, target 500ms, fires below 0.8, five-minute window. Nothing is locked afterwards.

The Start with a template dialog in Maple's rule builder, with the Low Apdex score template reading “Apdex < 0.8 / 5min (target 500ms)”.
The Low Apdex score template. Every value it sets stays editable.

2. Set the target, then the score you defend

Apdex target (ms) is your T: requests under it count as fully satisfied, and the frustrated line follows automatically at 4T. Then set Condition to < and Threshold to the score worth paging for, typically 0.8.

The panel also states the measurement boundary: built-in signals read entry-point spans only. A service that swallows failures on child spans and returns success from its entry point stays healthy here at any threshold. Use a Query or Raw SQL rule for that case.

The Signal & threshold panel with the Apdex chip selected, Apdex target 500 ms, condition less-than, threshold 0.8 and severity Warning.
Apdex target 500 ms, condition <, threshold 0.8. (The decimal comma is the browser's locale, not a Maple setting.)

3. Scope it

One T fits one class of traffic. If a rule covers many services or routes, set Group by to service.name or attr.http.route so each group is scored on its own and opens its own incident. Scope, grouping and the other fields shared by every signal are described in Alert rules.

The Scope panel of the rule builder, with Services, Environments, Group by and Exclude services fields.
Scope and grouping. Empty means every service and every environment.

4. Window, timing, destinations

Five minutes is the default window. Short windows on low-traffic services are noisy for Apdex in particular, because a handful of slow requests moves the ratio a long way. Keep Min samples at 50 or higher, and raise it on low-traffic services, so a service with 4 requests at 3am cannot post a score of 0.5 and page someone.

The severity toggle set to Warning, and the expanded evaluation timing row: window 5 minutes, 2 breaches to fire, 2 healthy checks to resolve, minimum 50 samples, renotify every 30 minutes.
Severity and evaluation timing. The defaults shown are the ones the template sets.

An Apdex rule skips windows with no requests, so a service that goes quiet overnight does not read as a score of 0. The other timing fields (Breaches to fire, Healthy to resolve, Renotify (min)) work as on every rule. See Evaluation timing.

Pick a Severity, attach your notification destinations, click Test rule to replay the rule over the past week, and save.

Creating the same rule over the API

Every field in the form is a field on the API. apdex_threshold_ms is T in milliseconds. It defaults to 500 when omitted.

curl -X POST https://api.maple.dev/v2/alerts/rules \
  -H "Authorization: Bearer maple_ak_…" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Checkout Apdex below 0.8",
    "signal_type": "apdex",
    "apdex_threshold_ms": 500,
    "comparator": "lt",
    "threshold": 0.8,
    "window_minutes": 5,
    "service_names": ["checkout"],
    "environments": ["production"],
    "severity": "critical",
    "minimum_sample_count": 50,
    "consecutive_breaches_required": 2,
    "destination_ids": ["dest_oybbpTBhtSFGShMjjLiCrh"]
  }'

To see what the rule would have done over a past range before it can wake anyone, send the same object as rule to POST /v2/alerts/rules/preview, with start_time and end_time:

{
	"rule": {
		"name": "Checkout Apdex below 0.8",
		"signal_type": "apdex",
		"apdex_threshold_ms": 500,
		"comparator": "lt",
		"threshold": 0.8,
		"window_minutes": 5,
		"service_names": ["checkout"],
		"severity": "critical",
		"destination_ids": ["dest_oybbpTBhtSFGShMjjLiCrh"]
	},
	"start_time": "2026-07-08T00:00:00.000Z",
	"end_time": "2026-07-15T00:00:00.000Z"
}

See Alert rules and the API reference for the full schema.

Or hand it to an agent

You can also create the rule from an AI assistant. Connect Maple’s MCP server to any MCP client, then ask for the rule in plain language:

Create an Apdex alert on the checkout service. Target 500ms, page me when the score drops below 0.8, and send it to the on-call Slack channel.

The agent builds the same rule you would build by hand. It cannot save an Apdex rule without a target, and it only sees your own organization’s data.

After the rule exists, you can ask which of your Apdex rules would never fire, or why one paged last night. The agent reads the rule’s checks and incidents to answer.

Known limitations

  • Apdex is computed on sampled spans. Under uniform sampling the score stays representative. A sampler that keeps slow or failed traces preferentially, without reporting its sampling weight, drags the score down. See Sampling and throughput.
  • One T per rule. A service whose /healthz and /reports/export share a rule is being measured against a target that fits neither. Group by attr.http.route, or write separate rules.
  • The score hides magnitude. It counts frustrated requests without weighting how frustrated they were. Pair it with a p99 rule.

The What is Apdex? guide covers the formula, a worked example, what counts as a good score, and how Apdex compares to p95 and p99.