Skip to content
ansezz.

▸ Free tool

Latency Percentile Calculator.

Paste a column of response times. Get p50 through p99.9 by both standard methods, a histogram, and a straight answer about whether your sample is even big enough to have a p99.

▸ Sample size decides everything

A p99 computed from 40 requests is your slowest request wearing a hat. This tool flags that instead of printing it with a straight face.

▸ One per line, or separated by commas, spaces or tabs. Unit suffixes like ms are stripped; anything that isn't a number is skipped and counted. Commas always split values, so strip thousands separators before pasting — 1,204 reads as two numbers.

Parsed 0 values · skipped 0

▸ The unit is a label only — no conversion is applied. Paste values that are already in one unit.

▸ The sample

Paste response times above — one per line, or comma separated.

Runs in your browser · nothing uploaded

Percentiles.

ok enough samples · noisy will move between runs · = your max no data above it
Percentiles of the pasted sample under both estimators, with a sample-size check for each
Percentile Nearest-rank Linear (R-7) Enough samples?

Distribution.

count p50 p95 p99

Paste values to draw the distribution.

Why the mean hides the tail

Take 1,000 requests. 990 finish in 40 ms, ten take four seconds. The mean is 79.6 ms — a number nothing in that sample came close to, and one that looks perfectly healthy on a dashboard. The p99 is 4,000 ms. Ten people sat and waited, and the average quietly absorbed them.

Latency distributions are right-skewed by construction. They have a hard floor (you cannot respond in negative time) and no ceiling at all — a retry, a cold cache, a GC pause, a lock, a DNS timeout. Every statistic that assumes symmetry describes a shape your data does not have. That includes standard deviation, which is why this tool prints it but does not lead with it.

You can see the skew in the histogram above. On a linear axis a single two-second request stretches the range so far that 90% of your sample collapses into the first bar — which is honest, but not readable. Switch the x axis to log and the tail spreads out into something you can actually inspect. That is the same reason latency dashboards and HdrHistogram bucket logarithmically rather than uniformly.

p99 is the number your users feel

"Only 1% of requests" sounds like a rounding error until you count per user instead of per request. A page that fires 50 API calls is only fast if all 50 are fast, and the odds of that are 0.99 to the 50th — about 61%. Four users in ten hit the slow tail on a single page load. The percentile that covers 1% of your traffic covers something closer to 39% of your sessions.

Tail amplification and fan-out

The same arithmetic runs on the server side. One inbound request that touches N services is only fast when every one of them is fast, and that probability collapses far quicker than people expect:

Probability that every dependency responds inside its p99, by fan-out width
Services touched All inside p99 Hits a slow one
1 99.0% 1.0%
5 95.1% 4.9%
10 90.4% 9.6%
20 81.8% 18.2%
50 60.5% 39.5%
100 36.6% 63.4%

At a fan-out of 100 — not unusual for a microservice request graph — a p99 that everybody signed off on becomes the common case. This is the real argument for hedged requests, hard per-dependency timeouts, and deleting calls from the critical path rather than optimising them.

You cannot average percentiles

The mean of ten shard p99s is not the fleet p99. Percentiles are order statistics; they are not linear, and averaging them is not an approximation, it is a different number with no interpretation. If one shard is melting, the average dilutes it. If traffic is skewed across shards, the weighting is wrong before you start. The same bug shows up in time: taking a per-minute p99 and averaging 60 of them does not give you the hourly p99, and it will usually understate it.

The fix is to merge the underlying data, not the summaries. That means keeping raw samples, or keeping something mergeable — HdrHistogram, Prometheus histogram buckets summed and then run through histogram_quantile, t-digest, or DDSketch. If your dashboard shows an average of p99s, it is lying to you on a schedule.

Nearest-rank vs linear, and how this tool computes both

Nearest-rank takes the value at index ceil(p ÷ 100 × n) in the sorted sample. It always returns a measurement that actually happened, which means you can go and find that request in your traces. Linear interpolation (R-7, the default in numpy, pandas and Excel's PERCENTILE.INC) computes h = (n − 1) × p ÷ 100 and blends the two neighbouring values. It moves smoothly as data arrives, which is what you want when comparing runs.

They diverge most exactly where percentiles are already shaky: small samples. On four values, nearest-rank p50 is the second value while linear p50 is the average of the second and third — the textbook median. That is why the summary panel reports a true median separately from the p50 row, and why both columns are shown side by side rather than hidden behind a toggle. Standard deviation here uses the n−1 denominator, and the mean and variance come from a single Welford pass so large values do not lose precision.

Turning this into an SLO

"p99 under 300 ms" is a snapshot, not an objective. An SLO is a threshold, a compliance target and a window: 99% of requests complete in under 300 ms, measured over 28 rolling days. That phrasing buys you an error budget — 1% of requests, and when it is spent you stop shipping features and go fix latency. It also sidesteps the averaging trap, because "fraction of requests under 300 ms" is a ratio of counts, and counts add up across shards and time buckets. Percentiles cannot be added. Counts can.

When not to use this

This page works on a sample you already have in your clipboard: a log slice, a load-test output, a CSV column. For a live system you want streaming quantile structures instead, because you cannot hold every request in memory and you need percentiles that merge across instances. And no percentile of a five-minute window tells you anything about a system whose traffic pattern changes hourly — sample the period you actually care about, or you are measuring the wrong distribution precisely.

Three numbers, three jobs

  Mean p50 / median p99
Answers Total time ÷ requests What a typical request feels Where the worst 1% begins
One 30s outlier moves it? A lot Barely Only on small samples
What it hides The entire tail Everything above the middle The top 1% and the max
Safe to average? Only weighted by request count No No — merge raw data instead
Use it for Capacity and cost math Is the common path healthy SLOs, alerting, user pain

Questions people ask.

What is p95 latency?

p95 is the response time that 95% of requests came in under, and 5% came in over. It is a point on the sorted list of your measurements, not an average: sort every request by duration, walk 95% of the way up the list, read the value. Unlike the mean it cannot be dragged around by a single 30-second outlier, and unlike the max it does not report one freak event as if it were normal.

How many samples do I need for a reliable p99?

About 1,000. The rule of thumb is 10 divided by (1 minus p): a p99 is determined entirely by the slowest 1% of your data, so you need roughly 100 samples before a 99th percentile exists at all, and around 1,000 before it stops jumping between runs. p95 needs about 200, p99.9 needs about 10,000. Below the first threshold this tool says so outright, because your p99 is just your maximum with a fancier name.

Why is p50 different from the median?

Because nearest-rank and linear interpolation disagree on even-sized samples. With four values, nearest-rank p50 returns the second one; the median averages the second and third. Linear interpolation (R-7, the default in numpy and Excel's PERCENTILE.INC) matches the textbook median exactly. This page shows both columns so the gap is visible instead of surprising, and reports the true median separately in the summary.

Can I average p99 across servers or time buckets?

No, and it is the most common dashboard bug in the industry. The mean of ten shard p99s is not the fleet p99: if one shard is on fire, averaging dilutes it, and if traffic is uneven the arithmetic is weighted wrong from the start. The same applies to averaging a per-minute p99 over an hour. Merge the raw samples instead, or use a structure built to merge — HdrHistogram, Prometheus histogram buckets via histogram_quantile, t-digest, or DDSketch.

Should I use nearest-rank or linear interpolation?

Nearest-rank when you want a number some real request actually produced, which covers most SLO and alerting work — you can go find that request in your traces. Linear interpolation when you need to match numpy, pandas, Excel, or a monitoring backend, because R-7 is what they default to. Above a few thousand samples the two agree to within noise; below a hundred they can differ sharply, which is exactly the regime where neither deserves your trust.

Does this percentile calculator upload my data?

No. There is no request, no analytics event, and no storage. The textarea contents are parsed, sorted and summarised by JavaScript inside your tab, so you can paste production latency numbers without them leaving the machine. Cut the network after the page loads and every button still works.

Keep reading.