Career Page Job Postings Scraper & API — Any Company, 14 ATS
Pricing
from $2.00 / 1,000 jobs
Career Page Job Postings Scraper & API — Any Company, 14 ATS
Job postings API for 14 ATS: Greenhouse, Lever, Ashby, Workday, Workable, BambooHR, SmartRecruiters, Teamtailor, Recruitee, Personio, Rippling, Breezy, Pinpoint, Dover. Paste a company domain — it finds the board and returns every open role in one schema, with structured salary and seniority.
Pricing
from $2.00 / 1,000 jobs
Rating
0.0
(0)
Developer
Blackcube
Maintained by CommunityActor stats
0
Bookmarked
3
Total users
3
Monthly active users
2 days ago
Last modified
Categories
Share
| Career Site & ATS Jobs Suite • 12 Actors, one codebase, one job posting billed once | ||
| Career Page Job Postings Scraper & API ➤ You are here | SmartRecruiters Jobs Scraper & API Any SmartRecruiters Board | Workable Jobs Scraper & API Any Workable Board |
| Recruitee Jobs Scraper & API Any Recruitee Board | Personio Jobs Scraper & API Any Personio Board | BambooHR Jobs Scraper & API Any BambooHR Board |
| Rippling Jobs Scraper & API Any Rippling Board | Breezy Jobs Scraper & API Any Breezy Board | Teamtailor Jobs Scraper & API Any Teamtailor Board |
| Pinpoint Jobs Scraper & API Any Pinpoint Board | SmartRecruiters Jobs Scraper Full Job Posts, Any Board | Workable Jobs Scraper Full Job Posts, Any Board |
More from this account: YouTube Transcript Suite · Website Contact & Email Suite · Google News Suite · Keyword Research Suite · Shopify Store Intelligence Suite · eBay Data Suite · Amazon Reviews Suite · Reddit · Meta Ad Library · Vinted · Trustpilot Review Intelligence Suite · App Store & Google Play Reviews Suite · Business Reviews Suite · Amazon Product Data Suite
Give it a company domain. Get back every open role from that company's own career page — live, in one normalized schema.
Paste robinhood.com and this Actor works out that Robinhood runs Greenhouse, finds the board, and returns all 121 open roles with salary ranges attached. Paste ramp.com and it finds Ashby. Paste huggingface.co and it finds Workable. Paste a list of 500 companies from your CRM and it does that for every one of them, then hands you a single flat table where a Greenhouse job, a Workday job and a Recruitee job all have exactly the same 34 columns.
No board slugs to look up. No "view page source and open the Network tab" instructions. No pre-built database that might be hours stale.
You pay per job returned. A company we can't resolve is free. A board with no open roles is free. A run that returns nothing costs nothing — there is no start fee on any plan. Every company that produces no jobs still comes back as a row telling you why, so a gap in your list is explained rather than silent.
Unofficial. Not affiliated with, endorsed by, or connected to any applicant tracking system or employer whose board it reads. It reads only the vacancies employers publish for the public on their own career pages.
Why this one
1. It finds the board for you
Every other multi-ATS scraper makes company discovery your problem — you supply the board slug, and if you don't know it, you go read the page source. That single friction point is why they stay small.
This one takes whatever you already have:
| You paste | What happens |
|---|---|
robinhood.com | Detects Greenhouse, resolves the slug, returns the board |
https://figma.com/careers | Reads the careers page, finds the linked board |
https://jobs.ashbyhq.com/ramp | Uses the board directly |
greenhouse:figma | Exact board, no detection needed |
On a 20-company benchmark of well-known employers, a bare domain was resolved to the right board 90% of the time — including for the many companies whose careers page is a JavaScript app with no visible link to the board behind it. Where a company matters and resolution misses it, pass its board URL directly and it always works.
A plausible match is not good enough, and you are never billed one. Company names collide across platforms: an obvious-looking guess at one well-known employer's board lands on a live board of the same name belonging to somebody's unrelated test account, with real postings on it. Every resolved board is confirmed to belong to the company you actually asked for before a single row is billed. Where that cannot be confirmed you get a free atsNotFound row — because returning nothing is honest, and putting a stranger's postings on your invoice is not.
2. Live from the source, not a database snapshot
The large incumbents in this category are not scrapers — they are pre-crawled databases you query, with a stated indexing delay and expired postings purged on a daily cycle. That is a reasonable design, and it means their rows can describe jobs that are already closed.
This Actor reads the employer's own board at the moment you run it. Every row carries scrapedAt, and every row was live on that board at that timestamp. If a job was pulled down an hour ago, it isn't here.
That is the difference between "a job that was open when we last crawled" and a job that is open now. No ghost jobs, no expired reposts, no aggregator duplicates — because the data never passes through an aggregator.
3. Repeat runs only charge for what's new
A jobs feed gets polled. If you track 200 companies daily, naive per-row billing charges you the full price for the same 4,000 unchanged postings every single day.
Turn on Only new or changed jobs since my last run and the Actor returns — and bills for — only what actually moved. The first run returns the full board; every run after it returns the delta.
| Day 1 | Day 2 | Day 30 | |
|---|---|---|---|
| Full mode | 4,000 jobs | 4,000 jobs | 4,000 jobs |
| Only new/changed | 4,000 jobs | ~40 jobs | ~40 jobs |
"Changed" is defined, not vague. A posting counts as changed when its title, department, location, salary, employment type or workplace type differs from last time — so a role that gains a published salary range comes back to you instead of being silently withheld forever. Description edits deliberately do not count: employers re-render and re-word descriptions constantly, and billing a whole board again for a typo fix is precisely the surprise invoice this design exists to avoid.
Every row also carries isNew, isChanged and firstSeenAt whether or not the filter is on, so a scheduled run doubles as a hiring-signal feed: a new job requisition is a buying trigger, and this tells you the exact day it appeared.
Each feed keeps its own memory, derived from your company list, so a second schedule over an overlapping list does not start out reporting zero new jobs. Set Feed name explicitly to control that grouping yourself.
4. One schema across all fourteen platforms
Fourteen ATS platforms publish fourteen completely different shapes of data. Some nest departments inside offices, some bury pay in free text, some date a posting only as "30+ days ago", some publish no dates at all — and normalizing that by hand is weeks of work that breaks again every time one of them changes.
Every one of them comes out of here with the identical 34 keys. That is the whole product — and it is verified by a test asserting the key sets are byte-identical across all fourteen, plus a live run across all fourteen that produced exactly one distinct key set over 579 jobs.
5. Fields the boards don't actually publish
Roughly half the value is enrichment, because the raw feeds are thin:
salaryMin/salaryMax/salaryCurrency/salaryPeriod— taken from structured pay data wherever a platform publishes it, and recovered from the posting itself where it doesn't. On Greenhouse, where most tools report no salary at all, 74% of rows in a live run carried a real range. Hourly and annual pay are told apart correctly, so$20.40/hourand$222,800/yearnever end up in the same column pretending to be the same thing — the single most common way a salary filter silently lies to you.salaryRawalways keeps the original text, so a parse you disagree with is visible rather than hidden.seniority— normalized to 11 comparable levels (intern…executive), because almost no ATS publishes it at all. "Senior Staff Engineer" comes back asstaff, notsenior. Null where there is no real signal, never a guess.isRemote/workplaceType— normalized from whatever the board provides. Hybrid is deliberately not remote.nullmeans the posting said nothing either way, which is a different answer fromfalse.employmentType— one enum, so the same job is the same value on every platform instead of three spellings you have to reconcile yourself.applyUrl— the real application link on the employer's own ATS. Not a job-board mirror.
6. Filters run before billing
Every filter is applied before a row is ever charged for. Pull only senior remote engineering roles posted in the last week and you pay for those, not for the 4,000 you discarded.
maxJobs is honoured exactly — never over, never silently under. When a cap is the reason results were truncated, the run log says so explicitly, so "not enough data" and "we throttled you" are never ambiguous.
What each platform actually gives you
Published rather than glossed over, because a coverage claim you can't check is worth nothing. Measured on a real 579-job run across all fourteen platforms — these are field fill rates, not promises:
| Platform | Dept | Seniority | Location | Remote | Type | Salary | Posted | Apply URL |
|---|---|---|---|---|---|---|---|---|
| Breezy | 100% | 0% | 100% | 33% | 67% | 100% | 100% | 100% |
| Ashby | 100% | 62% | 100% | 89% | 100% | 97% | 100% | 100% |
| Pinpoint | 100% | 46% | 100% | 100% | 100% | 94% | 0% | 100% |
| Greenhouse | 100% | 72% | 100% | 5% | 1% | 74% | 100% | 100% |
| Lever | 100% | 80% | 100% | 100% | 50% | 62% | 100% | 100% |
| Recruitee | 100% | 90% | 100% | 100% | 0% | 50% | 100% | 100% |
| Workable | 100% | 83% | 100% | 100% | 87% | 48% | 100% | 100% |
| SmartRecruiters | 100% | 100% | 100% | 100% | 100% | 0% | 100% | 100% |
| Personio | 100% | 100% | 100% | 0% | 100% | 0% | 100% | 100% |
| BambooHR | 95% | 5% | 100% | 0% | 100% | 0% | 0% | 100% |
| Dover | 0% | 40% | 100% | 100% | 0% | 0% | 100% | 100% |
| Teamtailor | 0% | 66% | 86% | 0% | 3% | 0% | 100% | 100% |
| Rippling | 100% | 25% | 100% | 29% | 0% | 0% | 0% | 100% |
| Workday | 0% | 86% | 54% | 6% | 0% | 0% | 100% | 100% |
The zeroes are the platforms' limits, not shortcuts here: Rippling, Pinpoint and BambooHR publish no dates at all, and Teamtailor and Workday publish only a handful of fields per role. Where a field is unavailable you get an explicit null rather than a missing key or a fabricated value.
How to scrape Greenhouse jobs
Set companies to any Greenhouse employer — greenhouse:figma, https://job-boards.greenhouse.io/vercel, or just figma.com.
Greenhouse is one of the boards where a naive read quietly loses most of the data: department, office and structured pay can all come back empty on a board that is in fact publishing every one of them. On one 567-posting board the difference was 0 rows with department and office versus all 567. Here you get the full record every time, whether or not you asked for descriptions — which is why the salary coverage in the matrix above is 74% rather than the 0% a simpler reader reports.
How to scrape Lever jobs
Pass lever:ro, https://jobs.lever.co/ro, or the company domain. Lever is strong on workplace type, commitment and country, and all three land in the normalized schema. Many Lever teams state pay in prose instead of a pay field — those ranges are recovered too, so you are not left filtering on a column that looks half empty.
How to scrape Ashby jobs
Pass ashby:ramp, https://jobs.ashbyhq.com/ramp, or ramp.com. Ashby is among the richest boards of the fourteen: compensation with currency and interval, an explicit remote flag, employment type, department and team — 97% salary coverage in a live run. Bonus and equity are excluded, so salaryMin/salaryMax always mean base salary and stay comparable across employers.
How to scrape Workable jobs
Pass workable:huggingface, https://apply.workable.com/huggingface, or the company domain. Workable names the employer on every posting and is strong on structured city, state and country, a genuine remote flag, and an experience level — one of the better boards here for filtering before you pay.
How to scrape SmartRecruiters jobs
Pass smartrecruiters:<company> or the company domain. Paginated automatically. SmartRecruiters publishes structured city/region/country plus an explicit remote flag and an experience level.
How to scrape Recruitee jobs
Pass recruitee:hygraph, https://hygraph.recruitee.com, or the company domain. Recruitee is one of the few boards with a genuinely structured salary object (min, max, period, currency) — delivered as strings, converted here to numbers. It can flag a posting both remote and hybrid; remote wins, so workplaceType never contradicts isRemote.
How to scrape Teamtailor jobs
Pass teamtailor:southpole, https://southpole.teamtailor.com, or the company domain. Teamtailor names the employer and location on every role and has the strongest Nordic coverage here. Be aware it publishes thinly — no department and no employment type — as the matrix above shows; both come back null rather than invented.
How to scrape Personio jobs
Pass personio:<company> or the company domain. Personio publishes its own seniority and schedule fields, which most boards do not, and they come through in the same schema as everything else. Strong DACH coverage that US-centric competitors under-serve.
How to scrape Rippling jobs
Pass rippling:<company> or https://ats.rippling.com/<company>/jobs. Rippling's board is minimal — title, department, location and URL. It publishes no dates, and postedAt comes back null rather than fabricated.
How to scrape Breezy jobs
Pass breezy:<company> or the company domain. Breezy is one of the few boards with a plain-text salary field and an explicit remote flag, both of which are parsed.
How to scrape Dover jobs
Pass dover:<company> or an app.dover.com board URL. Dover is a smaller board used mostly by early-stage startups, and it comes through in the same 34-column schema as everything else — useful when your target list mixes big employers with seed-stage ones.
How to scrape BambooHR jobs
Pass bamboohr:<company> or a <company>.bamboohr.com URL. BambooHR is the
largest under-served platform in this category — roughly 30,000 customers against
the ~7,000 the biggest rival indexes.
One trap worth knowing, because it decides whether you can trust a result: a
subdomain that is not a BambooHR customer redirects to their marketing site,
which answers 200 with a normal-looking HTML page. A status check alone would
mark it a valid board. This Actor checks the response body, so a wrong slug comes
back as a free boardNotFound row rather than as an empty board — "this company
has no openings" and "this is not their board" are different answers.
Its list is thin: department, location and employment type, but no dates and no salary. Nulls, not guesses.
How to scrape Pinpoint jobs
Pass pinpoint:<company> or a <company>.pinpointhq.com URL. Pinpoint is one of
the best-structured boards of the fourteen: 94% of postings in a live run carried
a real salary range, with currency and period as separate fields, plus department,
workplace type and employment type.
It also publishes a compensation-visibility flag. Where an employer chose not to show pay, this Actor leaves the salary fields null rather than publishing what the employer deliberately withheld.
How to scrape Workday jobs
Workday is set up per employer, so it needs a real board URL rather than a bare domain — for example https://nvidia.wd5.myworkdayjobs.com/en-US/NVIDIAExternalCareerSite. Paste the URL exactly as it appears in your browser and the rest is handled.
Workday publishes thinly at the list level: no department, no employment type, dates only as "30+ days ago", and the literal text "3 Locations" in place of a city where a role spans several sites. Relative dates are converted to an approximate real date, and "3 Locations" is never recorded as a place — that field comes back null rather than wrong, so your location filter stays trustworthy. Turn on full descriptions to fill in the description, the true posted date and the employment type.
Ready-made company lists
Don't have a list yet? Pick a preset and run it immediately. Every company in these was verified against its live board:
| Preset | Companies |
|---|---|
top-tech | 28 — Stripe, Figma, Databricks, Discord, Cloudflare, Coinbase, Robinhood, Airtable … |
ai-ml | 22 — OpenAI, Anthropic, Notion, Ramp, Linear, Supabase, PostHog, Zapier … |
devtools | 22 — Vercel, Netlify, Grafana, Sentry, HashiCorp, ClickHouse, Retool … |
fintech | 16 — Brex, Mercury, Wise, Monzo, Klarna, Adyen, Carta, Deel … |
Input
| Field | Type | Default | What it does |
|---|---|---|---|
| companies | array | ["stripe.com"] | Domains, careers URLs, board URLs, or ats:slug pairs — mix them freely. |
| presets | array | (empty) | Curated verified company bundles, merged with the above. |
| onlyNewJobs | boolean | false | Return only postings that are new, or whose material fields changed. |
| postedWithinDays | integer | (unset) | Keep postings published within N days. |
| titleKeywords | array | (empty) | Job title must contain one of these. |
| excludeTitleKeywords | array | (empty) | Job title must not contain any of these. |
| locationKeywords | array | (empty) | Matches any location on a multi-site posting. |
| remoteOnly | boolean | false | Remote roles only. Hybrid and unknown are excluded. |
| seniority | array | (empty) | 11 levels from intern to executive. |
| employmentType | array | (empty) | fullTime, partTime, contract, internship, temporary, volunteer. |
| departments | array | (empty) | Department contains one of these. |
| hasSalary | boolean | false | Only postings with a parsed salary range. |
| maxJobs | integer | (unset) | Hard cap for the run. Honoured exactly. |
| maxJobsPerCompany | integer | (unset) | Cap per company, newest first. |
| includeDescription | boolean | false | Add full description as HTML and text. |
| atsPlatforms | array | (all) | Restrict detection to specific platforms. |
| maxConcurrency | integer | 10 | Boards fetched in parallel. |
| stateLabel | string | (derived) | Names this feed's change-tracking memory. Derived from your company list when empty. The former name stateKey is still accepted. |
Output
One row per job posting. See .actor/sample-output.json for complete records from a real run.
// abridged from a real run{"jobId": "ashby:ashby:7458d4e9-da2e-47bd-98cb-adfda43d42b2","ats": "ashby","companySlug": "ashby","companyDomain": "ashbyhq.com","title": "Engineering Manager - EU","department": "Engineering","team": "EMEA Engineering","employmentType": "fullTime","seniority": "manager","location": "Remote - European Union","locations": ["Remote - European Union", "Spain", "Italy", "Germany"],"isRemote": true,"workplaceType": "remote","salaryMin": 110000,"salaryMax": 185000,"salaryCurrency": "EUR","salaryPeriod": "year","salaryRaw": "€110K - €185K","postedAt": "2026-06-02T09:14:11.000Z","applyUrl": "https://jobs.ashbyhq.com/ashby/7458d4e9.../application","isNew": true,"isChanged": false,"firstSeenAt": "2026-08-13T08:41:04.718Z","scrapedAt": "2026-08-13T08:41:04.718Z"}
Multi-location postings are one row, not many. A role open in six cities returns a single record with six entries in locations — not six near-identical rows you get charged six times for and then have to deduplicate yourself.
jobId is stable across runs, so it works as a primary key for an incremental sync into your own database.
Dataset views
Jobs and Companies with no jobs (free). Views project columns but cannot filter rows, so both kinds appear in both with the other's columns blank. Split on errorReason in your own code: it is absent on a job row and set on a free row.
When a company returns no jobs
Every one of these is a free row — never billed — that says what happened.
errorReason | Meaning |
|---|---|
atsNotFound | No supported ATS found, or the board found did not belong to that company. |
boardNotFound | The board no longer exists at that address — slug changed, or the company moved ATS. |
boardEmpty | Board reached fine; the company currently has no open roles. |
noJobsMatchedFilters | Jobs exist, but none matched your filters. |
requestFailed / timeout / blocked | The board could not be read; errorMessage has the detail. |
invalidInput | Not a usable domain, board URL, or ats:slug pair. |
chargeLimitReached | The run hit its maximum total charge before reaching this company. |
What it's used for
- Sales and GTM — a new job requisition is a buying trigger. Track your target account list and learn the day a company starts hiring for the role that signals budget.
- Recruiting and talent — monitor a set of employers for openings the aggregators haven't picked up yet, with the real apply URL.
- Niche job boards — back a board with roles verified open at the moment you pulled them. No reposts, no dead links, and
onlyNewJobskeeps the daily refresh cheap. - Competitive and market intelligence — headcount direction, which teams a rival is growing, which markets they are opening.
- Investors — hiring velocity across a portfolio, refreshed on a schedule.
Honest limits
- Coverage is the fourteen platforms listed. A company on an ATS not covered here, or on a fully custom careers page, returns a free
atsNotFoundrow. It is not silently dropped. - Resolving a bare domain to the right board works about 90% of the time on a 20-company benchmark, not 100%. If a company matters and detection misses it, pass its board URL directly and it will always work.
- Per-platform field coverage varies a lot — see the matrix above. It is published so you can check it rather than discover it after paying.
postedAton Workday is approximate, derived from a relative string.- Change tracking is per feed, per Apify account, and remembers a posting for 120 days.
- This Actor reads job listings only. It does not collect candidate or applicant data of any kind.
Legal & responsible use
This Actor reads only publicly published job listings — the vacancies employers deliberately publish for candidates to find. It does not log in, bypass authentication, solve captchas, or touch applicant data.
Job postings are company information rather than personal data, which keeps this well clear of the GDPR questions that surround people-data scraping. Where a description happens to name a hiring manager, that text is reproduced exactly as the employer published it and is not enriched, cross-referenced, or resolved to a person.
You are responsible for how you use the output, including any outreach you base on it.
Unofficial project. Uses only publicly available data. Not affiliated with any applicant tracking system or employer.
Use it from n8n, MCP, the API or a schedule
Built to be called by a workflow, not only from the Store form. The Actor is vonsensey/career-page-job-postings-scraper-api; every snippet below sends {}, which runs the defaults shown on the form — replace it with your own input.
n8n
Install the Apify community node (@apify/n8n-nodes-apify under Settings → Community Nodes, or search "Apify" on n8n Cloud). Add Apify → Run Actor with Actor vonsensey/career-page-job-postings-scraper-api and your input JSON, then Apify → Get Dataset Items on the run's defaultDatasetId and pipe the rows anywhere. For scheduled runs, the On new Apify Event trigger fires when a run of this Actor finishes.
MCP (Claude, Cursor, VS Code, any MCP client)
{"mcpServers": {"apify": {"url": "https://mcp.apify.com?tools=vonsensey/career-page-job-postings-scraper-api","headers": {"Authorization": "Bearer <YOUR_APIFY_TOKEN>"}}}}
Your agent then calls vonsensey/career-page-job-postings-scraper-api as a tool with the same input the form takes and reads the dataset back.
REST API (one call, rows in the response)
curl -X POST "https://api.apify.com/v2/acts/vonsensey~career-page-job-postings-scraper-api/run-sync-get-dataset-items?token=$APIFY_TOKEN" \-H "Content-Type: application/json" -d '{}'
Python
from apify_client import ApifyClientclient = ApifyClient("<YOUR_APIFY_TOKEN>")run = client.actor("vonsensey/career-page-job-postings-scraper-api").call(run_input={})for row in client.dataset(run["defaultDatasetId"]).iterate_items():print(row)
JavaScript
import { ApifyClient } from 'apify-client';const client = new ApifyClient({ token: process.env.APIFY_TOKEN });const run = await client.actor('vonsensey/career-page-job-postings-scraper-api').call({});const { items } = await client.dataset(run.defaultDatasetId).listItems();
Make, Zapier, LangChain, CrewAI
The Apify app in Make and Zapier has a Run an Actor module: pick vonsensey/career-page-job-postings-scraper-api. In LangChain and CrewAI the Apify tool wrappers take the same Actor id. A daily schedule needs nothing but the Console: Schedules → Create → this Actor → cron, and the dataset fills on its own.
Run it without configuring anything — Scrape jobs from any company career page, a ready-made example you can start as-is or copy.
Use cases
- Hiring as a buying signal. A company posting roles in a function you sell to is a company with budget — track a target list and act on the change.
- Build a niche job board. Pull real postings straight from company career pages, with the apply URL intact, instead of re-listing an aggregator.
- Watch a competitor's headcount plan. What a rival hires for, and where, tells you what it is about to ship.
- Talent-market research. Departments, seniority, location and employment type arrive as structured fields, so a market map is a pivot table away.
Run it on a schedule
A one-off pull answers a question; a schedule answers it every day without you. Open Schedules in the Apify Console, point a cron at this Actor, and the dataset keeps filling on its own — no server, no cron box, no babysitting. Everything here is built to be re-run: you are billed per job posting delivered, so a scheduled run that finds nothing new costs nothing.
FAQ
Do I need an API key for each ATS?
No. Give it a company domain or a board slug and it detects the applicant tracking system behind the page for you.
Can I get only the jobs that are new since my last run?
Yes. Turn on change tracking and a scheduled run returns only new or changed postings — which is also all you are charged for.
What happens if a company has no open roles?
You get a free row saying so. "No jobs today" and "we could not reach the board" are different answers, and neither one costs you anything.
Which applicant tracking systems are supported?
Greenhouse, Lever, Ashby, Workday, Workable, BambooHR, SmartRecruiters, Teamtailor, Recruitee, Personio, Rippling, Breezy, Pinpoint and Dover — 14 in all.
Something wrong, or a field you need that is missing? Open an issue on the Issues tab — it is read and it gets fixed. If this saved you time, a rating on the Store page helps the next person find it.