Salary Data API: Pay by Company, Job Title and H-1B Filing
Get salary data as JSON for one company, one job title or H-1B filings, with the sample count behind every number. Pay per call from your agent.

Get what a role pays at a company, with the sample behind each number. Paste this into your agent:
Set up https://monid.ai/SKILL.md, and then use Monid to get the salary data for software engineers at Google from Levels.fyi and the median software engineer salary from Indeed, and give me a table with each figure and the number of samples behind it.
One prompt pulls pay from two sources side by side. 1 min setup · 10-24 hrs saved.
You are pricing an offer, a job post or a hire, and you want pay data you can load into a sheet or a model: a company by level, a job title across a market, or the filed wages behind visa sponsorship. This guide shows how to pull each as JSON from your agent, what came back when we ran the endpoints on 2026-10-16, and the traps that make two correct numbers disagree. It runs through Monid, the OpenRouter for agent tools: one key, one balance, every tool priced per call.
What is the easiest way to get salary data through an API?
Monid. Install it once, add a key, and your agent calls a compensation endpoint with a company or a job title and gets the figures back as typed JSON, with no data contract and no vendor account to set up.
- Pay per call. A Levels.fyi company lookup is $0.01 and an Indeed job title lookup is $0.02 (Verified 2026-10-16 on the compensation tools page). Buy calls as you need them: no monthly plan and no minimum top-up. Discovery and inspection are free; only a run bills.
- One install, every source. The same key and balance reach Levels.fyi, Indeed and every other tool on Monid. Pay by company, pay by title and filed wages are three calls on one balance, not three vendors.
- Lives inside your agent. Install the CLI, add your key, run the prompt above, and the tool is there whenever your agent needs a number, with nothing to keep running. Your agent reads
SKILL.mdand learns the rest, so setup is about a minute.
Three lookups, one of each, cost $0.04 when we ran them (Verified 2026-10-16).
How do you get salary data for a company or a job title?
Pick the endpoint by the question, then call it with a company slug, a job title or a year. Levels.fyi answers "what does this company pay at each level", Indeed answers "what does this title pay in a market", and the H-1B endpoint returns filed wage records you can group yourself.
| Route | What you get | Cost shape | Fits |
|---|---|---|---|
| Buy a compensation database | Surveyed pay for many employers | Seat or annual contract | A comp team benchmarking every role |
| Download a public dataset | A static file, as of its date | Free | Learning, models, one-off analysis |
| Build scrapers per site | Whatever you parse | Your hours | A fixed source you will maintain |
| Monid compensation endpoints | Live JSON per company, title or filing page | Pay per call, buy flexibly | Offer checks, role pricing, agents |
For agents
Grab an API key at app.monid.ai, then paste this to your agent and hand it the key:
set up https://monid.ai/SKILL.md
It learns the whole discover, inspect, run workflow itself. More in the agent quickstart.
For humans
npm install -g @monid-ai/cli
monid keys add -k <your-api-key> -l main
Step 1. Get the company and level view
monid run -p levels.fyi -e /get_salary_data \
--query '{"company_slug": "google", "job_family_slug": "software-engineer"}'
company_slug is required. job_family_slug and location are optional, and location takes a slug such as germany or london-metro-area. Leave it out and you get United States data.
Step 2. Get the market view for a title
monid run -p indeed -e /get_salary_info \
--query '{"job_title": "Software Engineer", "location": "Austin TX"}'
job_title is required and location is optional free text. Without a location the local block repeats the national one.
Step 3. Get the filed wage records
monid run -p levels.fyi -e /get_h1b_salaries --query '{"year": 2025, "page": 1}'
year accepts 2020 through 2025 and page starts at 1. One page holds about 100 records, and 2025 has 5,492 pages.
Or hand the whole job to an agent
Give this to your agent![]()
Set up https://monid.ai/SKILL.md, and then use Monid to pull the first ten pages of 2025 H-1B salary records, drop anything above 600000, and give me the median by job title.Ten pages is ten billed calls, so a job like this costs about $0.10 at the per-call price above (Verified 2026-10-16). Hiring teams who also want to see which companies are posting roles can pair this with job postings as hiring signals.
Can you get salary data from a dataset or by scraping?
Yes to both, and the right one depends on whether you need a snapshot or a live lookup. Searching for a salary dataset turns up CSV files on Kaggle and similar sites, which are fine for learning, for training a model or for a one-off analysis. They are frozen at the date they were exported, and they rarely carry the employer, the level and a sample count together.
Scraping a pay page yourself works for one site you will maintain. You write a fetcher, parse a page whose layout can change, handle blocking, and rebuild the parser when it does. The first working version takes hours, and each new source repeats the work.
Monid is the route for anything you will run more than once. The fetching and parsing are maintained for you, the output is typed JSON instead of HTML, and a company lookup is $0.01 (Verified 2026-10-16). A browser is quicker for one number you will read once; an endpoint is for a list of companies or an agent that needs the figure mid-task.
What does salary data look like, and what can you build with it?
Salary data comes back as a distribution plus the population it came from. We ran three endpoints on 2026-10-16, and the fields below are what came back.
The company view
levels.fyi/get_salary_data for Google software engineers returned percentiles in four sets, all labelled United States:
{
"base_salary": { "p10": 142000, "p25": 164500, "p50": 200000, "p75": 232000, "p90": 260000 },
"tc": { "p10": 175000, "p25": 212750, "p50": 285000, "p75": 426000, "p90": 565000 },
"bonus": { "p50": 22200, "p75": 43000, "p90": 60000 },
"stock_grant": { "p50": 70000, "p75": 180000, "p90": 266000 },
"count": 176
}
Read the two p50 rows against each other: base is 200,000 and total compensation is 285,000. At p90 the gap is 260,000 against 565,000, so the share that is salary falls as pay rises. Alongside came averages, one row per level from L3 to L9, each with base, stock, bonus, total, years of experience and two sample counts, and a median block holding one record (an L4 in San Francisco with a base of 205,000 and a total of 285,000).
The title view
indeed/get_salary_info for "Software Engineer" returned a national aggregate with a yearly median of 135,997.50 on 38,285 data points. It also carried a state map (Arizona at 98,776 on 477 samples, Mississippi at 79,750 on 68), top paying cities, top paying companies, related titles and skill and education impacts. The call took about 15 seconds.
The filing view
levels.fyi/get_h1b_salaries for 2025 page 1 reported 549,133 records across 5,492 pages. Each record is a short object with single-letter keys: company, domain, job title, occupational category, wage, level, city, state, year, certification status and filing dates.
What you can build with it
- An offer check. Pull the company view for the employer, then compare each of base, bonus and stock against your offer rather than one total.
- A salary band for a job post. Pull the title view for a market, keep the state or city rows that match where you hire, and store the sample count with each figure.
- A sponsorship wage table. Page through the filed wages for a year and group by company and job title, the way the H-1B data guide does.
- A competitor pay map. Loop the company view over a list of slugs and chart base against total by level.
What goes wrong when you combine salary sources?
Four things, and the first is why two correct numbers disagree. All four are from the 2026-10-16 runs.
Why do two sources give two different numbers for the same job?
Because they measure different populations and different definitions of pay. Indeed's 135,997.50 is a yearly median for software engineers across all employers. Levels.fyi's 200,000 is a base salary median for one employer, and its 285,000 is total compensation for the same people. A page of 2025 H-1B filings for Google software engineers showed wages of 236,000, 234,000, 205,000 and 193,000, which are filed figures for sponsored roles and exclude whatever the employer did not file. None of them is wrong. Match the population and the definition before you compare the number, and store which one you pulled next to it.
Why does a salary period have a count of zero?
Because the headline block derives four of its five periods. In the Indeed response the salaries block gives yearly 135,997.50 on 38,285 data points, and hourly 58.40, daily 604.40, weekly 2,488.30 and monthly 9,628.85, each on numDataPoints 0. All five carry inferred: false. The derived ones come from a multiplier: the response lists a yearly multiplier of 2,328.845783089349 against hourly, which is not the 2,080 hours of a standard work year. A second block, aggregatedSalaries, holds observed counts for the same periods: hourly 60.14 on 8,680, daily 129.58 on 95, weekly 1,991.55 on 31 and monthly 7,705.04 on 195.
So read the count, not the flag. The observed daily median is 129.58 and the derived one is 604.40, so the daily figure you read depends on which block you take it from.
def observed(agg, period):
row = agg["aggregatedSalaries"][period]
return row["estimatedMedian"], row["numDataPoints"]
Why can a top-paying list be built on two records?
Because the ranking does not require a minimum sample. The first three entries in Indeed's topPayingCompanies each carried salary.totalCount 2: a monthly mean of 35,000, a yearly mean of 381,250 and an hourly mean of 150. The first entry's latest record was from 2022. Filter any ranked list by its count before you read the order.
Why do the two Levels.fyi counts not agree?
Because they do not nest. For Google L3 the response gave count 59 and count_last_12_months 782, so the recent count is larger than the headline count. L9 gave 4 and 0, so the top level has no samples from the past year at all. Read both fields for every level and drop a level whose recent count is low before you quote its average.
Why does an H-1B page need cleaning?
Because the top row is not a salary. Page one of 2025 opened with a React Developer at 1,939,644 (certified, level II), then a Java developer at 1,602,432 for the same company, then 445,000. Other rows show a status of "Certified - Withdrawn", a level of null, and one row with the city New York and the state CA. The domain on each row looks built from the company name plus .com, so match companies on the name, not the domain.
CAP = 600_000
clean = [r for r in rows if 30_000 < r["w"] < CAP and r["cs"] == "Certified"]
A crude cap and a status filter are the right first moves, and they belong in your code.
Which endpoint should I use for which job?
| Endpoint | What it does | Input | Billing | Price (Verified 2026-10-16) |
|---|---|---|---|---|
levels.fyi/get_salary_data | One employer by level: percentiles and per-level averages | company_slug (required), job_family_slug, location | Per call | $0.01 |
levels.fyi/get_salary_summary_md | The same idea as a Markdown summary, for a model to read | company_slug (required), job_family_slug | Per call | $0.01 |
indeed/get_salary_info | A job title across a market: periods, state map, cities, companies | job_title (required), location | Per call | $0.02 |
levels.fyi/get_h1b_salaries | Filed wage records, a page at a time | year, page | Per call | $0.01 |
web3.career/get_web3_salaries | Average salaries for Web3 roles | none | Per call | $0.01 |
Prices Verified 2026-10-16 with monid inspect; current figures are on monid.ai/tools. We ran the first, third and fourth rows; the summary and Web3 endpoints were inspected and not run, so the table states their inputs and billing only. All five bill per call, so a page of 100 filings costs the same as a company lookup, and a long run is limited by time rather than volume. The same split between sources shows up when you pull job data board by board.
When is another route a better fit?
You are setting one person's pay. These are market distributions. A percentile describes a population and says nothing about one person's performance, tenure or leverage.
You need regulated pay data. Prevailing wage determinations, union scales and public-sector bands come from official registers. Rules differ by country and by use, so check the ones that apply to you.
Your population is thin in the data. Our runs covered United States queries only. Non-technical roles, small employers and other countries come with smaller samples, which is why the count matters.
You need it current to the week. The Indeed response carried a lastUpdateTimestamp of 2026-10-12, four days before we called it, and Levels.fyi accumulates submissions over time. Neither is live.
You want one number once. A browser will give you that for free, and a public dataset will serve a classroom or a model.
And the disclosure: we resell these endpoints. The most useful thing in this post is the habit of keeping the sample count and the definition next to every figure, and that costs nothing whoever you buy the data from.
Conclusion
The quickest way to get salary data from an agent is one call per question through Monid: $0.01 for a company by level and $0.02 for a job title (Verified 2026-10-16), one key for this and every other tool, about a minute to set up. Datasets and scrapers work for a snapshot or a single site; Monid keeps the fetching and the field mapping maintained.
Two things matter more than the endpoint you pick. Match the population and the definition of pay before you compare two figures. And keep the count that arrived with each number, because a derived period, a two-record ranking and a recent-sample count of zero all look like data until you read it.
How we estimated the time saved: finding, fetching and normalising three pay sources yourself is roughly 3 to 6 hours each, 9 to 18 hours, plus 1 to 6 hours for the cleaning rules (derived periods, outlier wages, count filters). That is 10 to 24 hours. The 1 minute is the install, key and first call once you already have a Monid key.
Free next step: run monid inspect -p indeed -e /get_salary_info to read the schema at no cost, then make one call and compare numDataPoints across the periods. Start at monid.ai.
FAQ
What is a salary data API?
It is an endpoint that takes a company or a job title and returns pay figures as structured data: percentiles, averages by level, or a median by period. On Monid the Levels.fyi and Indeed endpoints do this per call, and a third returns filed H-1B wage records.
Is there a free salary data API?
Not free, but close for small jobs: a company lookup through Monid costs $0.01 (Verified 2026-10-16), with no monthly plan and no minimum top-up. Free CSV datasets exist for study, and they are frozen at the date they were exported.
How do I get salary data for a company?
Call /get_salary_data on Levels.fyi with the company slug and, optionally, a job family and a location. You get percentiles for base, total compensation, bonus and stock, plus an average and two sample counts for each level.
Which is more reliable, Indeed or Levels.fyi?
Neither on its own, because they answer different questions. Indeed gives a market median for a title across employers, with a count per period. Levels.fyi gives one employer's pay by level with the components split out. Compare like with like, and read the sample count before you trust either.
Does it cover countries outside the United States?
The Levels.fyi endpoint takes a location slug for countries and metro areas, the Indeed endpoint takes a free-text location, and the H-1B records are for the United States. Our runs used United States queries, so check the sample counts for any other market.
Published September 2026.


