How to track hiring at a list of startups (without writing a scraper)
Hiring is one of the most honest signals a company gives off. A startup that suddenly opens six data-center engineering roles is building something big. A competitor that posts its first "Head of Sales, EMEA" is about to go after your market. A portfolio company that quietly stops posting may be tightening its belt.
The data is public: every company publishes its open roles on its careers page. The problem is that careers pages are all different. Under the hood most startups use an applicant tracking system (ATS) such as Greenhouse, Lever, Ashby, Workable or SmartRecruiters, and each one has its own job-board format.
In this tutorial we'll turn a plain list of eight companies into a single, clean table of open jobs. Then we'll filter it down to recent engineering roles and set it up to send only new postings every morning. No code is required, and everything below comes from a real run on October 31, 2026.
What you'll need
- A free Apify account. The free plan includes $5 of usage every month.
- The Greenhouse, Lever & Ashby Jobs Scraper Actor, which reads each ATS's official public job-board API. It doesn't log in or scrape HTML.
- A list of companies you care about.
Step 1: Write down your company list
You don't need to know which ATS a company uses. The Actor accepts any of these:
- a plain name:
Hugging Face - a careers-page URL:
https://www.figma.com/careers/ - a job-board link:
https://jobs.lever.co/spotify - an explicit hint, which is the fastest option:
greenhouse:anthropic,ashby:ramp
The hint is usually visible in the link when you click any job on a careers page. For example, jobs.ashbyhq.com/notion/... means ashby:notion.
For this tutorial I used eight well-known startups:
{
"companies": [
"ashby:ramp",
"ashby:linear",
"ashby:notion",
"greenhouse:anthropic",
"greenhouse:figma",
"greenhouse:discord",
"https://jobs.lever.co/spotify",
"Hugging Face"
],
"keywords": ["engineer"],
"postedWithinDays": 30,
"includeDescription": false,
"maxResults": 300,
"stateStoreName": "tutorial-startup-hiring"
}
What the filters do:
- keywords: engineer keeps jobs whose title contains "engineer" (case-insensitive).
- postedWithinDays: 30 keeps only roles first published in the last 30 days.
- includeDescription: false skips the long job descriptions, which keeps the output small and readable.
- maxResults: 300 is a safety cap on both size and cost.
- stateStoreName is the "memory" the Actor uses to know which jobs it has already shown you. We'll use it in step 4.
Step 2: Run it
Open the Actor, paste the input above into the JSON editor (or fill in the form fields), and click Start.
The run took about 3 seconds. Its run summary shows what happened behind the scenes:
| Company | Input | Detected ATS | How it was found | Open jobs | Matching jobs |
|---|---|---|---|---|---|
| Anthropic | greenhouse:anthropic |
Greenhouse | hint | 675 | 51 |
| Ramp | ashby:ramp |
Ashby | hint | 171 | 6 |
| Notion | ashby:notion |
Ashby | hint | 148 | 7 |
| Figma | greenhouse:figma |
Greenhouse | hint | 140 | 10 |
| Spotify | jobs.lever.co/spotify |
Lever | URL | 89 | 8 |
| Discord | greenhouse:discord |
Greenhouse | hint | 41 | 4 |
| Linear | ashby:linear |
Ashby | hint | 31 | 2 |
| Hugging Face | Hugging Face |
Workable | API probe | 6 | 0 |
That's 1,301 open jobs across eight companies, narrowed to 88 engineering roles posted in the last 30 days. Note that "Hugging Face" was typed as a plain name and the Actor still found the right board on Workable by itself.
Step 3: Read the results
Each job is one row with the same fields, whichever ATS it came from. Here's a real record (Notion, via Ashby):
{
"company": "Notion",
"ats": "ashby",
"title": "Software Engineer, Developer Platform",
"department": "Engineering",
"location": "San Francisco, California",
"locations": ["San Francisco, California", "New York, New York"],
"workplaceType": "hybrid",
"employmentType": "Full-Time",
"postedAt": "2026-10-19T17:28:06.956Z",
"url": "https://jobs.ashbyhq.com/notion/8c0bf773-e536-45c9-9a6c-8ee478148cc8",
"salary": {
"min": 179000,
"max": 200000,
"currency": "USD",
"interval": "year",
"text": "$179,000 – $200,000 per year",
"source": "description"
},
"isNew": true
}
And a few rows from the same run, as they look in the table view:
| Company | Title | Location | Salary | Posted |
|---|---|---|---|---|
| Anthropic | Applied AI Demo Engineer | San Francisco, CA | New York City, NY | 240,000 - 315,000 USD | Oct 19 |
| Anthropic | Data Center Electrical Engineer – Design | Remote-Friendly, United States | 320,000 - 405,000 USD | Oct 30 |
| Notion | Data Engineer, Product | San Francisco, California | $179,000 - $200,000 per year | Oct 28 |
| Notion | Growth Engineer, Startups & SMBs | New York, New York | $150,000 - $180,000 per year | Oct 21 |
| Linear | Senior / Staff Security Engineer | North America | (not published) | Oct 15 |
| Spotify | AV Engineer - Platform | Stockholm | (not published) | Oct 9 |
A few things jump out from this small sample:
-
Salary transparency is high: 75 of the 88 roles (85%) came with a salary range, either published as structured data by the ATS or parsed from the job text. The
salary.sourcefield tells you which. - Anthropic is hiring heavily for infrastructure. Several of its newest engineering roles are about data centers, power generation and substations, which says more about its plans than any press release.
- Remote is the exception: only 28 of the 88 roles are flagged as remote-friendly.
From the Output tab you can export everything to CSV, Excel or JSON, or open it in Google Sheets.
Step 4: Get only the new jobs every morning
A one-time snapshot is nice, but the real value comes from tracking change. Turn on Only new jobs since last run ("onlyNew": true) and keep the same stateStoreName.
- The first run is the baseline: it returns all matching jobs and remembers their IDs.
- Every later run returns only jobs it hasn't seen before.
To check it, I ran the exact same input again a minute later with onlyNew: true. It finished in 4 seconds and returned 0 jobs, which is correct, because nothing new had been posted in that minute. Tomorrow's run will return just the roles posted overnight.
To automate it:
- Click Save as a new task so your company list and filters are stored.
- Open Schedules, then Create new, choose "Daily at 8:00", and attach the task.
- Optionally add an integration so new jobs are posted to Slack, appended to Google Sheets, or sent to Zapier, Make or n8n.
You now have a private hiring tracker that costs a few cents a day.
What does it cost?
The Actor charges per job returned: $0.003 per job ($3 per 1,000), plus $0.005 per run. Filters are applied before you pay, so you're only charged for jobs that pass them.
- This tutorial's first run: 88 jobs, about $0.27.
- A daily "only new jobs" monitor across 50 companies that sees about 30 new matching jobs a day costs about $0.10 a day.
- Apify's free plan ($5 a month) covers about 1,600 jobs.
Ideas for what to do with it
- Sales and recruiting: a company opening new engineering or sales roles is a good moment to reach out.
- Investors: watch hiring across a portfolio or a watchlist of competitors.
-
Job seekers: get a morning digest of new roles at your 30 dream companies, filtered by
locationsorremoteOnly. - Niche job boards: feed "remote AI jobs at Series A–C startups" from hundreds of boards, refreshed daily.
A note on data sources
Everything here comes from the public job-board APIs that Greenhouse, Lever, Ashby, Workable, SmartRecruiters, Teamtailor, Recruitee and Personio provide so jobs can be shown on career sites and job boards. Nothing is behind a login and no candidate data is involved. If you republish postings, check each company's terms first.
Try it with your own company list: Greenhouse, Lever & Ashby Jobs Scraper on Apify. The same publisher also has an App Store Reviews Scraper and a Bluesky Scraper.
Disclosure: I built the Jobs Scraper.
Top comments (0)