How we compute salary percentiles
Last reviewed 2026-06-11.
The numbers
We compute four percentiles on every entity page: p25, p50, p75, and p90. Each marks the salary value below which 25%, 50%, 75%, and 90% of postings fall.
The formula
We use Postgres's PERCENTILE_CONT(p) WITHIN GROUP (ORDER BY salary_mid), which interpolates linearly between the two nearest observed values. This is preferred over PERCENTILE_DISC(discrete) because it handles small samples gracefully — you don't snap to a single observed value when sample size is low.
salary_midis the midpoint of each posting's disclosed range: (salary_min + salary_max) / 2. Postings with no disclosed band are excluded from the percentile computation.
The sample window
Entity pages show the last 26 weeks. Snapshots show a single closed calendar month. Both windows are anchored to posted_at, not the crawl timestamp.
Edge cases
- Wide ranges. If a posting discloses a band wider than 50% of the midpoint, we still include it — but wide bands pull the p90 up disproportionately.
- Missing currency. We assume USD for US-anchored postings. Non-USD postings are excluded from US-scoped percentile views.
- Tiny samples.Pages with fewer than 5 qualifying postings render with a density guard: the page either shows — or the route returns 404.
See it on a live page
- /stacks/typescript — p25/p50/p75/p90 for postings mentioning TypeScript.
- /roles — curated role directory; each role page shows the same percentile block.