Hiring Signals Scraper - Company Job Openings from ATS Boards
Pricing
from $7.30 / 1,000 company returneds
Hiring Signals Scraper - Company Job Openings from ATS Boards
For B2B sales and recruiting teams hunting companies that are growing: one row per company with open jobs, new roles in the last 7 and 30 days, roles by department and location, remote counts, and whether sales, engineering or executive seats are open. Stripe showed 613 open jobs on 2026-09-11.
Pricing
from $7.30 / 1,000 company returneds
Rating
0.0
(0)
Developer
NeverEmpty
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
2 days ago
Last modified
Categories
Share
Built for B2B sales and recruiting teams who want to find the companies that are growing their teams this month: give it a list of companies and get back one row per company telling you how hard it is hiring right now — open jobs, new roles in the last 7 and 30 days, roles by department, location and country, remote / hybrid / on-site counts, and whether it is hiring sales, engineering or executive roles. Up to 500 companies per run.
One row, measured: Stripe on 2026-09-11 — openPostings 613, openRoles 611 (the same job posted in two places counted once), newRolesLast30Days 227, and sales / engineering / executive 116 / 147 / 14.
The easiest way to get a hiring signal wrong is to count jobs by their update date, and that is exactly what the boards invite you to do. Greenhouse rewrites updated_at in bulk when a company re-saves its board: 613 of 613 Stripe jobs and 453 of 453 Datadog jobs showed as updated within 7 days, which would report both as hiring their whole team this week. This Actor uses first_published there, which goes back to 2023 at Stripe and 2019 at Datadog — and the one date each other system actually publishes (createdAt at Lever, where Palantir still has live postings created in December 2009; publishedAt at Ashby).
And it counts roles, not postings. Stripe's 613 postings are 611 roles; Datadog's 453 are 410; Airbnb's 167 are 164 — merged only on Greenhouse's own job ID plus an identical title, so two different jobs are never folded into one.
A board that does not exist is not reported as a company that is not hiring. Workable answers with an empty but live board for most common names: on 2026-09-11 it returned HTTP 200 with 0 jobs for 16 of 20 well-known companies. And Workday's job list stops counting at 2,000 — NVIDIA's board said total: 2000 while Workday's own filters added up to 2,614 — so that ceiling is reported rather than passed off as the company's size.
One charge per company row, whatever the number of jobs behind it. A company with 600 open jobs costs the same as one with 3, and note rows are never charged. The data comes from each company's own public job board on Greenhouse, Lever, Ashby, Workable and Workday — the same boards the company's careers page shows. No login, no proxies, no API key. Export as JSON, CSV or Excel.
Unofficial. This Actor is not affiliated with, endorsed by, or sponsored by Greenhouse, Lever, Ashby, Workable or Workday. All product names are trademarks of their respective owners.
What one row looks like
| Column | Example (Stripe, measured 2026-09-11) | What it means |
|---|---|---|
openPostings | 613 | Job postings on the board right now |
openRoles | 611 | Jobs after counting the same job posted in several places once (2 postings merged) |
newRolesLast7Days | 64 | Jobs whose first publication was in the last 7 days |
newRolesLast30Days | 227 | Same, last 30 days |
salesRoles / engineeringRoles / executiveRoles | 116 / 147 / 14 | Open jobs in each group (definitions below) |
byDepartment | [{"name": "...", "roles": 20}, ...] | Jobs per department, largest first |
byLocation | [{"name": "Singapore", "roles": 43}, ...] | Jobs per location |
topTitles | [{"title": "Software Engineer, New Grad", "roles": 6}, ...] | The most common job titles |
newestRoles | title, date, location and link of the 5 newest jobs | |
boardUrl | https://job-boards.greenhouse.io/stripe | The public list of jobs |
New means first published, never last updated
The easiest way to get hiring signals wrong is to count jobs by their update date. Greenhouse's board API has two dates on every job, first_published and updated_at, and companies re-save their boards in bulk: on 2026-09-11 all 613 of Stripe's jobs had an updated_at in the last 7 days, and so did all 453 of Datadog's. Counted by updated_at, every job at those companies looks new. Counted by first_published, Stripe had 64 jobs first published that week.
This Actor uses exactly one date per system and names it in the newRolesDateField column:
| System | Date used | Why this one (measured 2026-09-11) |
|---|---|---|
| Greenhouse | first_published | updated_at is rewritten in bulk (613 of 613 Stripe jobs, 453 of 453 Datadog jobs updated within 7 days). first_published goes back to 2023 at Stripe and 2019 at Datadog. |
| Lever | createdAt | The only date Lever publishes. It is not moved by edits: Palantir still has live postings created in December 2009. |
| Ashby | publishedAt | The only date Ashby publishes; OpenAI's oldest live posting shows 2023-05-25. Ashby does not say whether re-publishing a job resets it. |
| Workable | created_at | Workable also has published_on, which is later than created_at for 14 of Blueground's 21 jobs (up to about ten months later), so it can move when a job is published again. created_at never does. |
| Workday | postedOn | Workday only shows a relative "Posted 3 Days Ago" (and "Posted 30+ Days Ago" after a month). Days are counted from that text; "30+" is never counted as new. Workday does not say whether reposting resets it. |
A job posted in several places counts as new only when its earliest posting is new, so adding a second city to an old job does not make it look like fresh hiring. If any job on a board has no date, the new-role counts stay empty and newRolesNote says why - they are never counted from part of the board.
A job posted in five cities is counted once
Many companies post the same job once per location. Counting postings overstates hiring, so openRoles merges postings under these rules - and only these:
| System | Merged when | What can still differ between merged postings | Measured 2026-09-11 |
|---|---|---|---|
| Greenhouse | The postings share Greenhouse's own job ID (internal_job_id) and have the same title (ignoring case and spaces) | Location | Stripe 613 postings = 611 roles; Datadog 453 = 410; Airbnb 167 = 164. Postings under one job ID with different titles stay separate roles: Datadog lists seven different "Manager I, Engineering - …" titles under one ID, and Stripe posted two differently titled Account Executive roles under one ID on 11 June and 25 August. An English and a French posting of the same Airbnb job stay two roles, because their titles differ |
| Lever, Ashby | Title, department, team and the full description text (including requirement lists) are identical, ignoring whitespace | Location, and also workplace type and pay: of Plaid's 7 merged groups, 6 mix hybrid and on-site postings and 4 show different pay; of OpenAI's 6, 4 and 3 | Palantir: 84 of 310 postings merged (226 roles); Plaid (Ashby) 12 of 99; OpenAI (Ashby) 7 of 784 |
| Workable | Title, department and full description identical | Location | Blueground: none |
| Workday | Never - every Workday posting carries its own requisition number | - |
A merged role takes its date, workplace type and title from its earliest posting, and is counted in every location it is posted in. Same title with a different description stays two roles (Spotify has several same-titled jobs with different descriptions; none are merged). The number merged is in duplicatePostingsCollapsed, and rolesCountedBy names the rule used.
Not hiring, not found, and not readable are different rows
rowType | ok | Meaning | Charged? |
|---|---|---|---|
company | true | The board was read and lists at least one open job | Yes |
no-open-roles | true | You named the board (lever:acme or its URL) and it is live with 0 open jobs | No |
name-only-empty-boards | false | You gave only a name, and the only boards with that name are empty - see below | No |
ambiguous-name | false | You gave only a name and more than one system has a board with open jobs under it. Both are listed; pick one | No |
board-not-found | false | No board with that name exists (HTTP 404) | No |
duplicate-input | false | This entry points at a board an earlier entry already asked for - another spelling, capitalisation, URL form or trailing slash of it, or a name that resolved to it. The board is returned and charged once; every repeat gets this row, so no entry disappears | No |
not-queried-time-limit | false | The run was about to reach its time limit, so this company was not fetched (see Run time below) | No |
unavailable | false | The board could not be read after retries; note says why | No |
bad-input | false | The entry is not a board name or board URL (a company website such as stripe.com gets a note pointing to ATS Job Board Finder) | No |
budget-reached | false | The run hit the Max total charge you set; every company not fetched gets this row | No |
Why a name alone is checked on all four systems. Workable answers an empty, live board for most common names: on 2026-09-11 it returned HTTP 200 with 0 jobs for 16 of 20 well-known companies (stripe, ramp, openai, figma, notion …) whose real boards are on other systems. A tool that stops at the first board it finds would report those companies as "not hiring". This Actor checks Greenhouse, Lever, Ashby and Workable, uses the one board that has jobs, and never turns an empty board found by name alone into "not hiring". A row found by name alone says so in lookup (name-only), lists the systems that only had an empty board under that name in otherAtsEmptyBoards, and lists any system that could not be read in otherAtsUnreadable - in that case another company's board with the same name there could not be ruled out, and notes says so.
Workday's 2,000 limit
Workday's job list stops counting at 2,000: NVIDIA's board said total: 2000 while Workday's own Time Type filter added up to 2,614 jobs, and asking for postings beyond number 2,000 returns the same jobs again (4,000 postings read, 2,000 distinct). So for boards at the limit this Actor takes the count from the Time Type filter (every job has exactly one value) and marks it in openPostingsExact. Departments, countries and remote counts for Workday come from Workday's own filters, which cover the whole board. Some of those filters put one job under several values, so their counts can add up to more than the number of jobs - Red Hat's Job Function filter adds up to 151 for 141 postings and its Country filter to 150. When that happens the row says so in notes; read those breakdowns as "jobs tagged with this value", not as shares that sum to the total.
Workday returns 20 postings per request (about 1.4 seconds each), so up to workdayMaxPostings are read per company. When a board is larger than that, the counts that need every posting - new roles, sales / engineering / executive roles, keyword matches - are left empty with a note, instead of being counted from part of the board. allPostingsRead tells you which case you are in.
Role groups
A job counts in a group when its title matches, or its department does (a job can be in two groups, e.g. "Sales Engineer"):
| Group | Title contains | Or department contains |
|---|---|---|
| Sales | sales, account executive, account manager, account director, business development, BDR, SDR | sales |
| Engineering | engineer, engineering, developer, software, SRE, DevOps, programmer | engineering |
| Executive | chief … officer, CEO, CFO, CTO, COO, CMO, CIO, CISO, CHRO, CRO, president, vice president, VP, SVP, EVP, head of, general manager, managing director - but not assistant, chief of staff, coordinator, intern, "office of the", "to the CEO" | - |
"Account Executive" is a sales job, not an executive one. executiveTitles lists the matching titles so you can check.
When a board's department field is useless - Plaid's Ashby board puts "All Departments" on all 99 jobs, and Palantir's Lever board leaves it empty - the team field is used instead, and departmentSource says ats-team-field.
What each system publishes
| Greenhouse | Lever | Ashby | Workable | Workday | |
|---|---|---|---|---|---|
| First-published date | yes | yes | yes | yes | relative text |
| Department | yes (extra request) | yes | yes | sometimes | from filters |
| Remote / hybrid / on-site | no | yes | yes | remote or not | if the board has the filter |
| Country | no | yes | yes | yes | if the board has the filter |
| Company name | yes | no | no | yes | no |
Greenhouse's public API does not say whether a job is remote, so the remote columns stay empty for Greenhouse boards; remoteHintRoles counts jobs whose location text mentions remote, kept separate so a guess never passes as a fact.
Input
| Field | Type | Default | What it does |
|---|---|---|---|
companies | array | Stripe, OpenAI, Spotify, Blueground, Red Hat (used only when the list is empty) | One entry per company, up to 500: "<ats>:<board name>", the job board URL, or just the board name |
roleKeywords | array | (none) | Words to count in job titles, e.g. SDR, Salesforce. Each gets its own count in keywordMatches |
workdayMaxPostings | integer | 1000 | Postings read per Workday board (20 to 2,000) |
maxRetries | integer | 3 | Total attempts per board for temporary errors (HTTP 429 / 5xx). A 404 is not retried |
Ways to name a company
{"companies": ["greenhouse:stripe", // <system>:<board name>"https://jobs.lever.co/spotify", // the job board URL"https://redhat.wd5.myworkdayjobs.com/en-US/jobs",// Workday needs its board URL"plaid" // name only: Greenhouse, Lever, Ashby and Workable are checked],"roleKeywords": ["SDR", "Account Executive"]}
The board name is the part after the system's domain: stripe in job-boards.greenhouse.io/stripe. Don't know it? ATS Job Board Finder turns a company website into its board name.
Output columns
rowType, ok, note, input, ats, boardSlug, companyName, boardUrl, lookup, otherAtsUnreadable, otherAtsEmptyBoards, openPostings, openPostingsExact, openRoles, duplicatePostingsCollapsed, rolesCountedBy, newRolesLast7Days, newRolesLast30Days, newRolesDateField, newRolesNote, rolesWithoutPostedDate, salesRoles, engineeringRoles, executiveRoles, hasSalesRoles, hasEngineeringRoles, hasExecutiveRoles, executiveTitles, remoteRoles, hybridRoles, onsiteRoles, remoteUnknownRoles, remoteSource, remoteHintRoles, byDepartment, departmentSource, byLocation, byCountry, countrySource, topTitles, newestRoles, keywordMatches, postingsRead, allPostingsRead, notes, source, checkedAt
Every row - including the note rows - has all 47 columns. A value the board does not publish is null, never 0 or false: hasExecutiveRoles is only false when every posting was read and none matched.
Pricing
One charge per company row, whatever the number of jobs behind it. A company with 600 open jobs costs the same as one with 3. Note rows are never charged.
Measured, not estimated
- Every count on this page was taken from the live boards on 2026-09-11 and is kept in the test suite, which recounts them from the raw board responses in a second, independent way.
- Test suite: 78 checks. 65 deliberate defects injected into the source (counting by update date, merging different titles under one Greenhouse job ID, trusting Workday's 2,000, charging an empty board, charging the same board twice, dropping a repeated input without a row, ignoring the time limit …) - every one is caught. Paging, retries, failures part-way and the charge limit are all exercised through the real reader with recorded responses.
Typical uses
- Sales prospecting - which of my 500 target accounts opened the most new roles this month, and which are hiring their first sales team?
- Account-based marketing - companies hiring a VP or Head of a function you sell to.
- Recruiting and RPO - which companies are scaling engineering right now.
- Market research - hiring by department and country across a sector.
Run time
Measured on the Apify platform on 2026-09-11: Greenhouse, Lever, Ashby and Workable take about 1 to 3 seconds per company plus a one-second pause between companies (the five default companies took 17 seconds; six-company runs took 8 to 11 seconds). Workday takes about 1.4 seconds per 20 postings: three large Workday boards at 1,000 postings each took 88 seconds, and NVIDIA at 2,000 took 94 seconds.
Before each company the Actor checks how much of the run's time limit is left. If the next company could not be finished in time (it allows 20 seconds for an ATS board, 30 for a name that has to be checked on four systems, and about 1.6 seconds per 20 Workday postings you asked for), it stops and writes a free not-queried-time-limit row for that company and every one after it, so a long list never ends with companies silently missing. Raise the run timeout, or split the list, for long runs with many large Workday boards.
Notes and limits
- Only public job boards are read, from each system's public job board API (Workday: the JSON its careers site loads). No login and no private data.
- One company per second at most (Lever's robots.txt asks for a one-second crawl delay).
- Postings are counted as the board lists them; a board does not say how many people a job is for.
- Up to 500 companies per run.
Other tools by NeverEmpty
- ats-jobs-api - every open job as its own row, from the same five systems
- ats-board-finder - a company website to the job board name this Actor takes
Support
Found a board that is counted wrongly, or want another system supported? Open an issue on the Issues tab and include the board name - it will be checked against the live board before anything is changed.