Compare Startup Salary and Equity Ranges by Role on Wellfound
Pull Wellfound jobs for several roles and cities, then compute median salary and equity ranges per role with the Wellfound Jobs Scraper and Python.
The actor referenced in this article. Pay only for results delivered.
Startup offers are hard to judge on their own. Is $150k to $250k plus 0.5% to 1.0% good for a senior engineer at a ten-person company? It depends on what similar startups list for the same role, and Wellfound is one of the few job boards where startups post salary and equity ranges side by side. Reading a few hundred listings by hand, and keeping currencies and pay periods straight while you do it, is a spreadsheet project nobody finishes.
Try it live: Wellfound Jobs Scraper: Startup Salary, Equity and Funding. Pay per result delivered. Failed and empty results are never charged.
The Wellfound Jobs Scraper reads Wellfound’s job lists without a login and returns the pay terms as parsed numbers, so the medians are a few lines of Python.
What you get and who it is for
| Field | What it holds |
|---|---|
primary_role | Wellfound’s role for the job, the field to group by |
salary_min, salary_max | The two ends of the salary range, as numbers |
salary_currency, salary_period | USD, CAD and so on, and the pay period, such as year |
equity_min_pct, equity_max_pct | The equity range in percent, when the listing has one |
offers_equity | True or false when the listing says so |
compensation | The range as Wellfound writes it, so you can check the parse |
company_stage, company_size | So you compare a seed startup with other seed startups |
Candidates use it to sanity check an offer, founders to set pay bands before they post a role, and recruiters and analysts to track what startups advertise from one month to the next.
The input
{
"roles": ["software engineer", "product manager", "data scientist"],
"locations": ["san francisco", "new york"],
"jobTypes": ["full-time"],
"maxJobsPerSearch": 200,
"includeCompanyDetails": true
}
rolesandlocationsmultiply: each role is searched in each city, so this is six searches of up to 200 jobs each. Use names the way Wellfound writes them. A city Wellfound does not list returns a free status row, never jobs from somewhere else.jobTypesset tofull-timekeeps contract, internship and cofounder listings out of the medians.includeCompanyDetailsreads each job’s own page for the exact salary currency and period, plus the startup’s stage and funding. Keep it on for this job. Off, you still get the salary and equity text with parsed numbers, faster.maxJobsPerSearchruns from 20 to 5,000. Wellfound shows about 40 jobs a page, and the run stops at your number or the end of the list.
What the output looks like
[
{
"job_id": "4791911",
"title": "Senior Software Engineer",
"primary_role": "Software Engineer",
"company_name": "Hazel",
"company_size": "1-10",
"compensation": "$150k to $250k • 0.5% to 1.0%",
"salary_min": 150000,
"salary_max": 250000,
"salary_currency": "USD",
"salary_period": "year",
"equity_min_pct": 0.5,
"equity_max_pct": 1,
"offers_equity": true,
"locations": ["New York City"],
"remote_policy": "onsite"
},
{
"job_id": "4505713",
"title": "Staff Software Engineer, Data",
"primary_role": "Software Engineer",
"company_name": "Checkr",
"company_size": "501-1000",
"company_stage": "Scale Stage",
"compensation": "$224k to $264k",
"salary_min": 224000,
"salary_max": 264000,
"salary_currency": "USD",
"salary_period": "year",
"locations": ["Denver"]
},
{
"job_id": "4437273",
"title": "Data Scientist, Corporate",
"primary_role": "Data Scientist",
"company_name": "YipitData",
"company_stage": "Scale Stage",
"compensation": "Up to $170k",
"salary_max": 170000,
"salary_currency": "USD",
"remote_policy": "remote"
}
]
The first two rows come from our run of software engineer jobs everywhere on October 6, 2026, and the third from a remote data scientist run on October 5, 2026. Neither run used cities, so treat the numbers as a sample, not a San Francisco benchmark. Look at what is missing: Checkr lists no equity, and the YipitData job gives a ceiling with no minimum and no period.
Of the 20 software engineer jobs in that October 6 run, 18 had a full range, with a median of $150,000 at the low end and $209,000 at the high end. Only 7 had an equity range, from 0.01% to 0.05% at Starfish Space (Growth Stage, 51 to 200 people) to 1% to 3% at Nyle.ai (Early Stage, 11 to 50 people). The remote data scientist run had 16 full ranges out of 20, with medians of $157,500 and $192,500, and 2 equity ranges.
Run it in Apify Console
- Open https://apify.com/themineworks/wellfound-jobs-scraper and click Try for free.
- In Roles or keywords, enter one role per line.
- In Cities, enter one city per line, such as san francisco and new york.
- In Job types, pick Full time.
- Set Max jobs per search, and leave Include company and job page details ticked.
- Click Start and watch the Output tab fill.
- Click Export and choose CSV, JSON or Excel.
Run it from Python
pip install apify-client
import csv
from collections import defaultdict
from statistics import median
from apify_client import ApifyClient
client = ApifyClient("YOUR_APIFY_TOKEN")
run = client.actor("themineworks/wellfound-jobs-scraper").call(run_input={
"roles": ["software engineer", "product manager", "data scientist"],
"locations": ["san francisco", "new york"],
"jobTypes": ["full-time"],
"maxJobsPerSearch": 200,
"includeCompanyDetails": True,
})
groups = defaultdict(lambda: {"jobs": 0, "lo": [], "hi": [], "eq_lo": [], "eq_hi": []})
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
if item.get("record_type") != "job":
continue # free status rows, such as an unknown city
key = (item.get("search_location") or "any", item.get("primary_role") or "unknown")
g = groups[key]
g["jobs"] += 1
if (item.get("salary_currency") == "USD" and item.get("salary_period") == "year"
and item.get("salary_min") and item.get("salary_max")):
g["lo"].append(item["salary_min"])
g["hi"].append(item["salary_max"])
if item.get("equity_max_pct") is not None:
g["eq_lo"].append(item.get("equity_min_pct") or 0)
g["eq_hi"].append(item["equity_max_pct"])
def med(values):
return median(values) if values else None
fields = ["city", "role", "jobs", "with_salary", "median_salary_min", "median_salary_max",
"with_equity", "median_equity_min_pct", "median_equity_max_pct"]
with open("wellfound_pay_by_role.csv", "w", newline="", encoding="utf-8") as f:
writer = csv.DictWriter(f, fieldnames=fields)
writer.writeheader()
for (city, role), g in sorted(groups.items()):
if g["jobs"] < 5:
continue # too few listings to say anything
writer.writerow({
"city": city, "role": role, "jobs": g["jobs"],
"with_salary": len(g["lo"]),
"median_salary_min": med(g["lo"]), "median_salary_max": med(g["hi"]),
"with_equity": len(g["eq_hi"]),
"median_equity_min_pct": med(g["eq_lo"]), "median_equity_max_pct": med(g["eq_hi"]),
})
print(f"{city:15} {role:25} {len(g['lo']):>3} salaries {len(g['eq_hi']):>3} equity "
f"median {med(g['lo'])} to {med(g['hi'])}")
The script keeps the count next to every median on purpose. A median of seven equity ranges is a much weaker claim than a median of 60 salaries, and the CSV makes that visible to whoever reads it.
Run it every month
Save the input as a task in Apify Console, open Schedules, create a schedule with a cron expression such as 0 7 1 * * (07:00 on the first of each month) and add the task. This actor has no only-new switch, and for pay tracking you want the full set each month anyway. Add a date column when you append each month’s CSV, and use job_id to tell new listings from ones that are still open.
Send each run’s dataset to a Google Sheet with Apify’s Google Sheets integration, post the summary to Slack through Make, Zapier or n8n, or have a webhook call your endpoint when the run finishes.
What it costs
You pay per job delivered: $2.09 per 1,000 jobs on the Bronze plan, $1.79 on Silver, and $1.49 on Gold, Platinum and Diamond, plus a flat $0.005 start fee per run. Company details cost nothing extra.
The input above is six searches of up to 200 jobs, so at most 1,200 jobs a month: about $2.51 on Bronze plus $0.005. Shorter lists cost less, a job found by two searches in one run is charged once, and jobs your filters skip are never charged.
Limits worth knowing
- Equity is sparse. Most startups leave it blank: 10 of 100 jobs in our October 7 run, and 17% in the actor’s 200-job proof run. Report equity medians with their count. A percentage also means little without a valuation, which is a funding and investor lookup of its own.
- Salary is sometimes partial or absent. 11 of the 100 jobs in that run had no salary at all, and ceiling-only ranges have no
salary_min. - These are posted ranges, not offers. They show what startups advertise, not what they paid.
posted_atshows when a job went live, and a reposted job shows its repost time. - Lists mix roles. Group by
primary_role, not by the role you searched. - Only cities Wellfound lists. A city it does not know returns an
unknown_locationstatus row instead of jobs.
Related
Explore the scraper referenced in this article: inputs, outputs, and pricing, then run it on Apify.
Frequently asked questions
Why do so many jobs have no equity figure? +
Wellfound shows what the startup entered, and most startups leave equity blank. In our run of 100 remote software engineer jobs on October 7, 2026, 89 had a salary range and only 10 had an equity range.
Are all salaries in US dollars per year? +
No. Each row carries salary_currency and salary_period, and the same run had two jobs paid in CAD. Some listings give only a ceiling, such as Up to $170k, with no minimum and no period. Filter on currency and period before you take a median.
Does a role search return only that role? +
Not always. A Wellfound list can show a startup's other open roles next to the one you searched. In our remote software engineer run, 75 of 100 rows had the primary role Software Engineer and the rest were roles such as Full-Stack Engineer and Data Scientist, so group by primary_role.
How many jobs can one search return? +
Up to 5,000 for each role and city pair, and up to 100 searches per run. Wellfound reported 766 software engineer jobs in San Francisco when we checked in October 2026.
Build a List of Funded Startups That Are Hiring on Wellfound
Turn Wellfound job listings into one row per startup with website, stage, total raised and latest round, using the Wellfound Jobs Scraper.
Compare Pay for a Role by Experience Level on Shine
Run one Shine.com search per experience band, keep the listings that show pay, and turn them into salary ranges in lakhs for each band.
Compare Salary Bands in Lakhs by City and Experience on Foundit
Build a salary table for one role across Indian cities and experience bands from Foundit.in listings that publish pay, and drop the placeholder figures.
Post a Daily Feed of New Tech Jobs to Slack or Telegram
Send each morning's new Hirist.tech jobs for your stack and cities to a Slack or Telegram channel, with repeats filtered out by job id, using Python.