levels.fyi Salary Scraper avatar

levels.fyi Salary Scraper

Pricing

from $4.00 / 1,000 salary records

Go to Apify Store
levels.fyi Salary Scraper

levels.fyi Salary Scraper

Scrape tech compensation from levels.fyi by company, job family and location. Per-offer rows carry company, level, title, base_salary, avg_annual_stock_grant_value, avg_annual_bonus_value and total_compensation; per-level rows carry averages and percentile bands.

Pricing

from $4.00 / 1,000 salary records

Rating

0.0

(0)

Developer

herus13

herus13

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

4 days ago

Last modified

Share

levels.fyi Salary Scraper extracts tech compensation data from levels.fyi by company, job family and location — either one row per submitted offer, with base, stock, bonus and total pay, or one summary row per level with averages and percentile bands.

What you get

One dataset row per offer in records mode, the default. Export as JSON, CSV or Excel.

{
"company": "Google",
"level": "L3",
"title": "Software Engineer",
"job_family": "software-engineer",
"location": "Los Angeles, CA",
"years_of_experience": 1,
"base_salary": 140000,
"avg_annual_stock_grant_value": 20000,
"avg_annual_bonus_value": null,
"total_compensation": 160000,
"offer_date": null,
"scraped_at": "2026-07-17T09:12:44.118203+00:00"
}

Records also carry focus_tag, the specialization the reporter gave — DevOps, Machine Learning, and so on — levels.fyi's own uuid for the entry, and years_at_company where it was disclosed.

Switch mode to aggregates and you get one summary row per company and level instead: count (how many reports stand behind it), the averages base, stock, bonus and total, the total-compensation bands tc_p10 through tc_p90, the base bands base_p10 through base_p90, and location_name. Percentiles levels.fyi does not publish for a given company, level and location come back as null rather than guessed.

What it costs

EventPrice
Salary Record (salary-record)$0.004
Salary Aggregate (salary-aggregate)$0.002
Actor Start (apify-actor-start)$0.001

You pay per row, not per minute: $0.004 a per-offer record and $0.002 an aggregate band, plus a $0.001 start fee per run. One company's aggregates — about seven levels — comes to roughly $0.015; that company's fifty individual offers to about $0.20; ten companies and five hundred offers to about $2.00. Apify platform usage (compute) is billed to your Apify account on top of the event price; proxy bandwidth is included unless you supply your own proxy URLs.

Input

FieldTypeRequiredDefaultWhat it does
companiesarraynoCompany names or levels.fyi slugs to collect, one per entry — for example google, Meta, netflix. Provide at least one of companies or discover_all; leave empty and switch on discover_all to walk the whole levels.fyi company index instead.
job_familystringno"software-engineer"The levels.fyi job-family slug to collect, for example software-engineer, product-manager, data-scientist or hardware-engineer. One job family per run, because levels.fyi publishes a separate page for each; run the actor again for the next one. Default software-engineer.
locationsarraynolevels.fyi location slugs for metro-level pay, one per entry; each is collected separately and every row records which location it came from. Pay varies enormously by area. Examples: san-francisco-bay-area, greater-seattle-area, new-york-city-area, greater-austin-area, united-states, india, united-kingdom, canada, germany, singapore. For anywhere else, open it on levels.fyi and copy the last part of the address. Leave empty for the nationwide view.
modestringno"records"Which shape of data a run produces. Values: records gives one row per submitted offer — level, specialization, years of experience, exact base, stock, bonus and total pay, and the city; aggregates gives one summary row per level — how many reports stand behind it, average base, stock, bonus and total, and the percentile bands for total pay and for base. Default records. Pick one per run, or half of every exported row is empty; run it twice to get both.
discover_allbooleannofalseWalk every company on the levels.fyi index instead of the list in companies, which is ignored while this is on. Default false. Cap the sweep with max_companies, or a run covers the entire index and costs accordingly.
max_companiesintegernoHow many companies a discover_all sweep may cover. Minimum 1. Leave empty for no cap, which walks the whole index — long, and priced per row collected.
max_recordsintegernoHow many per-offer salary records to collect for each company and location pair. Minimum 1. Leave empty for no cap, which takes every offer levels.fyi publishes for that pair.
proxyUrlsarraynoLeave empty: the run uses the residential proxy this actor ships with, included in the price of the run. To route the run through your own provider instead, add one gateway URL per entry, for example http://user:pass@host:port — works with any provider that issues URLs (DataImpulse, Bright Data, Oxylabs, Smartproxy, or your own). When set, these are used exactly as entered and nothing else is.

How to run it

  1. Open the actor on Apify Console.
  2. Enter the companies you want, or switch on Discover all companies to sweep the whole index.
  3. Set the job family, pick the mode, then Run. Export JSON, CSV or Excel from the dataset.
{
"companies": ["google", "meta"],
"job_family": "software-engineer",
"locations": ["san-francisco-bay-area", "greater-seattle-area"],
"mode": "aggregates",
"max_records": 200
}

Minimal input:

{
"companies": ["google"]
}

Available as the MCP tool herus13--levels-fyi-salary-scraper on mcp.apify.com and through the Apify API; send the same JSON.

Why this scraper

  • Two granularities, one actor — the raw distribution when you need to see the spread, or per-level bands when you need a number to quote, each in its own clean single-schema dataset.
  • Percentiles, not just medians — p10 through p90 for total compensation and for base is what turns "the median is X" into a defensible ask or a defensible offer.
  • Metro-level pay — collect several locations in one run and every row says which one it came from, because the same level pays very differently in the Bay Area and in Austin.
  • Company discovery built in — sweep the entire levels.fyi company index instead of hand-curating a list of slugs, capped wherever you want it capped.
  • Validated output — malformed entries are dropped before they reach the dataset rather than shipped with garbage fields.
  • Nothing to set up — no account, no cookies, no proxy configuration; a residential proxy is included in the price of the run.

Use cases

  • Negotiation preparation — The band and its spread, not just the median, is the number that decides what to ask for and what to accept.
  • Compensation-intelligence dashboards — Query by company, job family and location to build the internal benchmark a compensation team needs, refreshed on a schedule instead of bought once a year.
  • Tracking how a company's pay moves — Running the same company set month after month shows band drift, which tends to lead the public announcements about pay policy.
  • Recruiting and market research — Per-offer records beside aggregates let a recruiter quote a credible range for a role in a specific market rather than a national average that fits nobody.
  • Levelling and job architecture — Comparing how one company's levels ladder against another's is the groundwork for mapping titles across an industry.

FAQ

What is the difference between records and aggregates? Records are individual self-reported offers, one row per person — take these for raw, granular data. Aggregates are the per-level rollups levels.fyi computes from those same reports, one row per level with averages and percentile bands — take these for quick benchmarking. You choose one mode per run so each dataset keeps a single clean schema; run the actor twice if you want both.

Why are some percentile fields null? levels.fyi does not publish every band for every company, level and location combination. Only the ones it actually shows are filled in; the rest come back as null rather than estimated.

What does discover_all do, and how do I limit it? It ignores companies and walks every company on the levels.fyi index instead. Set max_companies to cap how many it covers, which is how you control both the length and the cost of a sweep.

Can I collect several job families or locations in one run? One job_family per run, because levels.fyi publishes a separate page for each. locations takes as many slugs as you like, and each is collected for every company.

Is a proxy included? A residential proxy is included in the price of the run. To use your own, paste gateway URLs into Your own proxy URLs. Scraping at volume? Your own DataImpulse account is cheaper per GB.

Is scraping levels.fyi legal? This actor collects only publicly available, self-reported and aggregated compensation data. You are responsible for complying with levels.fyi's terms and applicable laws. Do not use this data to identify or target individual reporters.

Building a compensation or hiring-intelligence pipeline? Pair this actor with: