datagrit

datagrit › Guides › Ashby Salary Scraper - Startup Job Pay Ranges

Guide

How to scrape salary ranges from Ashby job boards with Python

Pull startup job postings with pay ranges from Ashby boards, annualised to yearly amounts, with currency and remote filters and an only-new feed.

Published 2026-10-01 · uses the Ashby Salary Scraper - Startup Job Pay Ranges Actor

Many startups, Ramp, OpenAI and Zapier among them, hire through Ashby. Their careers pages at jobs.ashbyhq.com/<company> often show a pay range next to each role, partly because employers in some regions must publish pay. On 30 September 2026, 677 of OpenAI's 838 postings and 148 of Ramp's 155 carried a salary. That makes Ashby boards a useful public source of real salary bands.

Who needs that data? Compensation analysts benchmarking bands for a new level. Recruiters who want a feed of new roles at a list of target companies. Job seekers who want to know what Ramp or OpenAI pays for a role before an interview. Sales teams who treat a burst of new postings as a buying signal. They all want the same thing: postings as rows, with pay normalised so a monthly range and a yearly range can sit in one column.

Where the data comes from

Ashby provides a public job board API for employers who publish roles: api.ashbyhq.com/posting-api/job-board/<board>, with includeCompensation=true to get pay data. No login, plain JSON. It looks easy, and for a single board it is. Turning it into a reliable dataset across many boards is where it gets fiddly:

The Ashby Salary Scraper Actor on Apify handles each of these. It annualises pay (monthly × 12, weekly × 52, daily × 260, hourly × 2080), filters on the currency of the headline range while listing every tier currency, decides "remote" by Ashby's workplaceType instead of isRemote, matches locations as whole words, and keeps an only-new memory in a storage on your own Apify account.

Step by step

1. Define the input

Engineering roles with a published salary at three companies, at least 150,000 per year in USD, US locations:


{
  "companies": ["ramp", "openai", "zapier"],
  "keywords": ["engineer"],
  "departments": ["Engineering"],
  "locations": ["US", "New York", "San Francisco"],
  "remoteOnly": false,
  "onlyWithSalary": true,
  "minAnnualSalary": 150000,
  "salaryCurrencies": ["USD"],
  "publishedWithinDays": 30,
  "onlyNewSinceLastRun": false,
  "includeDescription": false,
  "maxItems": 500
}

companies accepts board names or full Ashby URLs. Boards that do not exist are skipped and listed in the run status; if none can be read, the run fails rather than returning an empty dataset. keywords match the title only, departments the department and teams the level below it. Department names vary between employers (OpenAI files sales roles under "Go To Market"), so look at a first run before filtering on them.

2. Run it from Python


from apify_client import ApifyClient

client = ApifyClient("YOUR_APIFY_TOKEN")

run_input = {
    "companies": ["ramp", "openai", "zapier"],
    "keywords": ["engineer"],
    "onlyWithSalary": True,
    "minAnnualSalary": 150000,
    "salaryCurrencies": ["USD"],
    "maxItems": 500,
}

run = client.actor("datagrit/ashby-salary-scraper").call(run_input=run_input)
postings = [p for p in client.dataset(run["defaultDatasetId"]).iterate_items() if p.get("found")]

for p in postings[:10]:
    print(p["company"], p["title"], p["salaryAnnualMin"], p["salaryAnnualMax"], p["salaryCurrency"])

3. Or with cURL


curl -X POST \
  "https://api.apify.com/v2/acts/datagrit~ashby-salary-scraper/run-sync-get-dataset-items?token=YOUR_APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"companies": ["ramp"], "onlyWithSalary": true, "maxItems": 50}'

What you get

One row per posting. The fields that matter most for pay analysis:

FieldExampleMeaning
companyzapierAshby board name
titleFinance Manager, MarketingJob title
departmentFinanceDepartment set by the employer
workplaceTypeRemoteRemote, Hybrid or OnSite
salaryMin / salaryMax158300 / 237500Headline range as published
salaryInterval1 YEARPay period of the published range
salaryAnnualMax237500Upper bound converted to a yearly amount
salaryCurrencyUSDCurrency of the headline range
payTierCurrenciesUSD, CADCurrencies of all location pay tiers
equityOfferedtrueCompensation includes equity

You also get bonusMentioned, commissionMentioned, salarySummary (the text shown on the posting), payTierSummaries when there are several tiers, allLocations, publishedAt, daysSincePublished, jobUrl and applyUrl. descriptionText (cut to 8,000 characters) is added only if you ask for it.

Recipes

Daily feed of new roles into Slack or a CRM

Set onlyNewSinceLastRun: true and schedule the Actor daily. The first run returns everything that matches; later runs return only postings not delivered before for the same companies and filters. Postings cut off by maxItems are not remembered, so they come back in the next run. If two schedules use identical filters but must each receive every posting (say, two recruiting teams), give them different stateKey values. Hook the run's webhook to Slack, Zapier or n8n and post each new row.

Pay benchmark by department in pandas


import pandas as pd

df = pd.DataFrame(postings)
usd = df[(df["hasSalary"]) & (df["salaryCurrency"] == "USD")]

bench = (
    usd.groupby(["company", "department"])
       .agg(roles=("id", "count"),
            p50_min=("salaryAnnualMin", "median"),
            p50_max=("salaryAnnualMax", "median"),
            equity_share=("equityOffered", "mean"))
       .sort_values("roles", ascending=False)
)
print(bench.head(20))

Keep the currency filter in the analysis: amounts are annualised but not converted between currencies.

Fully remote roles only

Set remoteOnly: true. It keeps workplace type Remote and drops hybrid and on-site roles; when an employer did not set a workplace type, Ashby's isRemote flag decides, and without that the word "remote" in the primary location. On 30 September 2026 this returned 29 of 838 openai postings, while the 524 hybrid postings on that board also carried isRemote.

Limitations

The Actor reads only the public job board API that Ashby offers employers for publishing roles. It does not log in or touch candidate data.

Wrapping up

If your target companies hire through Ashby, this gives you a clean, comparable pay dataset and a daily new-roles feed; it is priced pay per result, listed on Apify, with every field documented in the documentation.

Try Ashby Salary Scraper - Startup Job Pay Ranges on ApifyActor documentation

More guides