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.