ATS Jobs API: Workday, Greenhouse, Lever & 12 more avatar

ATS Jobs API: Workday, Greenhouse, Lever & 12 more

Pricing

$1.50 / 1,000 jobs

Go to Apify Store
ATS Jobs API: Workday, Greenhouse, Lever & 12 more

ATS Jobs API: Workday, Greenhouse, Lever & 12 more

Every open job from 15 ATS: Workday, Greenhouse, Lever, Ashby, SmartRecruiters & 10 more. One clean schema. Paste a company name, board or careers page URL. Monitoring mode returns only new jobs. No login, no proxy. $1.50 per 1,000 jobs.

Pricing

$1.50 / 1,000 jobs

Rating

0.0

(0)

Developer

Steadybyte

Steadybyte

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

3 days ago

Last modified

Share

ATS Jobs Feed: Greenhouse, Lever, Workday & Ashby Jobs API (15 ATS)

A jobs API for every open job a company publishes on Greenhouse, Lever, Workday, Ashby and 11 more applicant tracking systems, in one clean format. Paste board names, job-board links or careers page URLs. Filter by keyword, location, remote and date. Turn on monitoring mode to get only jobs that are new since your last run.

Data comes straight from each ATS's public job feed, the one that powers the employer's own careers page. No login, no cookies, no browser, no proxy.

Supported ATS (15): Greenhouse · Lever (global and EU) · Workday · Ashby · Workable · Recruitee · SmartRecruiters · BambooHR · Personio · Teamtailor · Breezy HR · Rippling · Pinpoint · JazzHR · Gem. The board format for each is in Board formats.

Only need Greenhouse? Greenhouse Jobs Scraper reads Greenhouse boards only, in the same schema plus Greenhouse extras (every department and office, each pay range listed separately, requisition ID).

What can it do?

  • 15 ATS, one schema: Greenhouse, Lever (global and EU), Ashby, Workable, Recruitee, SmartRecruiters, Workday, BambooHR, Personio, Teamtailor, Breezy HR, Rippling, Pinpoint, JazzHR and Gem.
  • Flexible input, one company per line:
    • ats:board, e.g. greenhouse:stripe, bamboohr:svante, workday:nvidia.wd5/NVIDIAExternalCareerSite (formats below)
    • a job-board URL, e.g. https://jobs.lever.co/palantir
    • one job's URL returns only that job (and you pay for one job) on Greenhouse, Lever, Ashby, Workable, Workday and SmartRecruiters, e.g. https://jobs.lever.co/palantir/<job id> or a careers-page link with ?gh_jid=123. A job URL from another ATS reads its whole board.
    • a careers page URL. The Actor looks for a supported board linked or embedded in the page.
    • just a name, e.g. stripe. Every ATS except Workday is tried and the board with the most open jobs wins.
  • Filters:
    • keywords in title, department and team, optionally the full description too (e.g. "Ruby on Rails" roles titled "Software Engineer")
    • exact phrases: Ruby on Rails matches only that phrase
    • "title must contain" words
    • exclude words
    • locations or countries (US = USA = United States, NY = New York, Bangalore = Bengaluru…; see how location matching works)
    • remote only
    • posted within N days
  • Monitoring mode (onlyNewJobs) remembers delivered jobs per Monitor ID. Schedule it daily for a feed of new openings, and pay only for new jobs. Progress is saved after every batch of delivered jobs, so a migration or abort resends nothing. After a hard crash (out of memory, killed container) the next run can resend, and charge again, the last unsaved batches: at most 30 jobs (up to 10 from each of the 3 boards read at a time). The list of delivered jobs is kept in a key-value store named steadybyte-ats-jobs-feed-state in your Apify account; don't start two runs with the same Monitor ID at the same time, or the one that finishes last overwrites the other's list and some jobs may be delivered again.
  • Salary where the ATS publishes it (mainly Greenhouse pay ranges, Ashby, Lever, Recruitee, Rippling, Pinpoint, plus salary text from BambooHR and Breezy), when the employer filled it in.
  • Clean plain-text descriptions, original HTML optional.
  • Pay only for delivered jobs. Filtered-out jobs, duplicates and jobs already seen in monitoring mode are free.

Board formats

ATSats:board exampleBoard name is…
Greenhousegreenhouse:gitlabboards.greenhouse.io/<board>
Leverlever:palantir, lever-eu:<board>jobs.lever.co/<board>
Ashbyashby:linearjobs.ashbyhq.com/<board>
Workableworkable:huggingfaceapply.workable.com/<board>
Recruiteerecruitee:bunq<board>.recruitee.com
SmartRecruiterssmartrecruiters:Equinoxjobs.smartrecruiters.com/<board>
Workdayworkday:nvidia.wd5/NVIDIAExternalCareerSite<tenant>.<wdN>.myworkdayjobs.com/<site> or wdN.myworkdaysite.com/recruiting/<tenant>/<site>, written <tenant>.<wdN>/<site>. Easiest: paste the careers site URL.
BambooHRbamboohr:svante<board>.bamboohr.com/careers
Personiopersonio:ohpen<board>.jobs.personio.de
Teamtailorteamtailor:bluefors or teamtailor:careers.example.com<board>.teamtailor.com, or the employer's own career-site host
Breezy HRbreezy:vagaro<board>.breezy.hr
Ripplingrippling:rsa-securityats.rippling.com/<board>/jobs
Pinpointpinpoint:hazelcast<board>.pinpointhq.com
JazzHRjazzhr:karman<board>.applytojob.com
Gemgem:vantacajobs.gem.com/<board>

Use cases

  • Job seekers and job boards: a daily feed of, say, remote Rails or data roles at a hand-picked list of companies.
  • Recruiters and sales teams: spot companies hiring for a function, a "hiring signal", and see which teams are growing.
  • Market research: track open roles, locations and published salary ranges across competitors.
  • AI agents and automations: call it from the Apify API, MCP, Make, n8n or Zapier and pipe new jobs to Slack, Sheets or a database.

Input example

{
"companies": ["stripe", "https://jobs.lever.co/palantir", "ashby:linear", "https://www.notion.com/careers"],
"keywords": ["Ruby on Rails"],
"keywordsInDescription": true,
"titleKeywords": ["engineer", "developer"],
"locations": ["Remote", "India"],
"onlyNewJobs": true,
"monitorId": "rails-remote-feed",
"includeDescription": true
}

All fields are explained in the Input tab.

Output example

One item per job (description shortened):

{
"id": "ashby:ashby:86a60834-ba64-484d-9658-afa1bc97a957",
"ats": "ashby",
"companySlug": "ashby",
"companyName": "Ashby",
"jobId": "86a60834-ba64-484d-9658-afa1bc97a957",
"title": "Mid Market Account Executive - EMEA (French Speaking)",
"department": "Sales",
"team": null,
"location": "France",
"locations": ["France", "United Kingdom", "Belgium", "Germany"],
"country": "FR",
"isRemote": true,
"workplaceType": "remote",
"employmentType": "FULL_TIME",
"postedAt": "2026-09-08T16:27:26.694Z",
"updatedAt": null,
"url": "https://jobs.ashbyhq.com/ashby/86a60834-ba64-484d-9658-afa1bc97a957",
"applyUrl": "https://jobs.ashbyhq.com/ashby/86a60834-ba64-484d-9658-afa1bc97a957/application",
"salaryMin": 110000,
"salaryMax": 195500,
"salaryCurrency": "EUR",
"salaryPeriod": "YEAR",
"salaryText": "€110K – €195.5K • Offers Equity • Offers Commission • Multiple Ranges",
"descriptionText": "WHY JOIN SALES AT ASHBY\n\nSales at Ashby is a unique opportunity …",
"descriptionHtml": null,
"isNew": null,
"sourceInput": "ashby:ashby",
"scrapedAt": "2026-10-26T13:05:12.000Z"
}
  • id is stable and lower-case (ats:board:jobId), whatever form you typed the company in. Use it to dedupe.
  • country is always an ISO 3166-1 alpha-2 code (US, GB, IN) or null. It comes from the ATS's own country field; when there is none, from the job's locations if they all name the same country ("Austin, TX" → US, "Berlin, Germany" → DE). Locations that name no country ("Remote", "Hybrid", "EMEA", "North America") are ignored. A job in two countries, or with a location that doesn't say its country ("Dublin, Ireland" + "Berlin"), gets null, as do text that isn't a country ("European Union") and ambiguous places on their own ("Springfield, CA" with no country). A major city on its own counts (London → GB, Berlin - Hybrid → DE); cities that share a name with a well-known place elsewhere (Vancouver, Cambridge, Portland) don't.
  • employmentType is FULL_TIME, PART_TIME, CONTRACT, TEMPORARY, INTERN or VOLUNTEER when recognised, otherwise the ATS's own text. salaryPeriod is YEAR, MONTH, WEEK, DAY or HOUR; a period that isn't one unit ("biweekly", "2 WEEKS", "Semi-monthly") is kept as the ATS wrote it. A salary of 0 is null.
  • Greenhouse salary comes from the board's pay ranges: salaryMin / salaryMax / salaryCurrency are the lowest and highest amounts in the first range's currency, and salaryText lists every range (e.g. per US zone). salaryPeriod is HOUR for hourly ranges and otherwise null, because Greenhouse doesn't say.
  • isRemote follows the ATS's workplace type when it has one. Otherwise it is true when any of the job's locations says "Remote" (or "Homeoffice"), or the title has a remote tag ("Engineer (Remote)", "Engineer (Hybrid/Remote)", "Remote Customer Support Agent"). "Remote Sensing Scientist" is not remote.

A per-company run summary (board used, jobs found, delivered, status) is saved as RUN_SUMMARY in the run's key-value store, and the run's status message sums it up, e.g. "Delivered 120 jobs from 8 boards · 10 boards read · 1 not found". "From N boards" counts the boards that delivered jobs. When a limit stops a board early, its summary says "Stopped early": with how many open jobs the board has when the whole board was already listed, otherwise found is only what was listed so far. For a job URL whose job is closed, the summary says "Job … is not open". Companies that can't be read (unknown board, unsupported input, a board that errors) are logged as warnings and skipped; the run still succeeds. RUN_SUMMARY has ok (false if any company could not be read) and failedCompanies (each company with its reason), and if no company could be read at all the status message says so, so automations can tell broken input from "no new jobs". Skipped companies are never charged. While a large board is listed, the status shows progress ("Listing workday:…: 4,000 of 19,613 jobs").

Jobs whose details can't be fetched are skipped, not delivered half-empty. For ATS that need a second request per job (Workday, BambooHR, Rippling, SmartRecruiters), a job whose detail request still fails after retries is not delivered and not charged, and monitoring doesn't remember it, so the next run picks it up. The company's summary counts it (detailsFailed), its status is partial, and the run's status message says how many were skipped. Jobs that closed between listing and the detail request are dropped (closedBeforeDetails). If 10 detail requests in a row fail, the Actor stops reading that board.

How much does it cost?

Pricing is pay per event: you pay a fixed price for each job delivered to the dataset, plus nothing else. The current price is shown on the Pricing tab. Use Max jobs in total or a maximum cost per run to cap spend; the run stops exactly at the cap. Jobs removed by your filters, duplicates, and jobs skipped by monitoring mode are never charged.

How keyword matching works

You typeMatchesDoes not match
go (short single word)"Go Engineer""Google", "Golang"
engineer (single word, 4+ letters)"Engineers", "Engineering Manager""Reengineering"
sales, java, data"Sales Lead", "Java Developer", "Data Engineer""Salesforce", "JavaScript", "Database"
"engineer" (in double quotes)"Software Engineer""Engineering Manager"
Ruby on Rails (several words)"Ruby on Rails", "ruby-on-rails""payment rails", "Ruby and Rails", "Rails on Ruby"
full-stack (hyphenated)"Full Stack", "Full-Stack""Fullstack"
c (one letter)"Embedded C Engineer", "C, Rust""C++", "C#", "Objective-C", "C-level"
.net".NET Developer", "ASP.NET Core""Network Engineer"

Matching is case-insensitive and ignores accents. A single word of 4+ letters also matches its plain endings (-s, -es, -er, -ers, -ing, -ed, -ment, -ist, plus "manage" → "Managing", "technology" → "Technologies"), never a longer, different word. Terms with no letter or digit (", --) are ignored.

  • Keywords (any) are checked against the title, department and team. Turn on Also match keywords in description to search the description too.
  • Title must contain (any) is optional and checks the job title only. When you fill in both, a job must pass both.
  • Descriptions often list many languages and tools. A single word like ruby or rails in description mode can match roles that only mention it in passing (for example, payments roles that talk about "payment rails"). For cleaner results:
    • use a phrase (Ruby on Rails) for description search, or
    • put the technology in Title must contain to get only roles whose title names it, or
    • combine description keywords with title words such as engineer or developer.

How location matching works

  • A job matches when its location, any of its other locations, or its country matches one of your entries. Common spellings are the same place: US / USA / United States, UK / United Kingdom / Great Britain, NY / New York / NYC, Bangalore / Bengaluru, Munich / München.
  • A two-letter entry that is a country code means the country: CA is Canada, IN is India, GA is Gabon, DE is Germany. US state codes that aren't country codes (NY, TX, WA) mean the state. For any US state you can type its name (California, Indiana) or US-CA. The run log notes how an ambiguous entry was read.
  • A country entry matches places in that country only: Mexico doesn't match "New Mexico", Ireland doesn't match "Belfast, Northern Ireland", and US doesn't match "North America" or "Latin America". A job whose country is another country never matches by text.
  • "Georgia" is the country. "Atlanta, Georgia" and "Remote - Georgia" are read as the US state; use US-GA (or Georgia, US) for it.
  • US also finds US jobs written the US way without a country ("San Francisco, CA", "Austin, Texas").
  • A country also finds jobs listed only by a major city in it (United Kingdom finds "London", Germany finds "Munich" and "10245 Berlin"); for smaller towns add the town name too.
  • Dashes and spacing don't matter in phrases: Remote - Europe matches "Remote-Europe" and "Remote – Europe".
  • Regions such as Europe, EMEA or APAC are matched as text in the location ("Remote - EMEA"), not by country. "London, ON" is text too: it doesn't match "London, Ontario, Canada"; use London or Ontario.

Tips

  • Find a company's ATS: paste its careers page URL. If the board isn't found, open a job on the careers page: its URL usually shows the ATS board. Paste the board part (https://jobs.lever.co/<company>) to get every job; the full job URL returns only that job on the ATS listed above.
  • Use one Monitor ID per feed. Changing filters while keeping the same Monitor ID still skips jobs delivered earlier. A job stays remembered while it is on its board; jobs gone for 120 days are forgotten.
  • Exclude words match whole words plus plain endings: intern drops "Intern" and "Internship" but keeps "Internal" and "International".
  • Fewer, larger runs are cheaper and faster. Put many companies in one run.

Limitations (please read)

  • Only published, public jobs are returned, exactly as each ATS exposes them. Internal postings are not included.
  • Salary for Workable, Workday, Gem, Personio, Teamtailor and JazzHR is not in their public feeds. It often appears inside the description text instead. Greenhouse has it only when the employer publishes pay ranges.
  • No description for Breezy HR (its public list has none) or for Personio boards whose XML feed is switched off (titles and offices only).
  • No posting date for Pinpoint and JazzHR, or for Personio boards without the XML feed. "Posted within N days" skips those jobs. Workday gives a date without a time.
  • Workday, BambooHR, Rippling and SmartRecruiters need one extra request per job for its description (and date). The Actor makes it only for jobs that pass your filters and fit your limits, so large boards with no limits still take a minute or more.
  • Workday search returns at most 2,000 jobs on many boards. The Actor first checks whether the board pages past 2,000 (CVS Health's ~19,600 jobs list in under 3 minutes). Otherwise it reads the board in parts, by a facet that counts each job once and needs the fewest requests (NVIDIA: 15 job categories, ~2,700 jobs in under a minute; Target: 52 states, ~14,200 jobs in about 4.5 minutes), 4 requests at a time. Very large capped boards take much longer: Accenture (~42,000 jobs, slow responses) lists about 24,000 jobs in the first 20 minutes, and some of its parts are too large to split exactly, so some of its jobs may be missing (the run summary says so). If one part can't be read, the others still are and the company is marked partial. If a board can't be split, the run summary says jobs may be missing. With descriptions on, every listed job that passes your filters also needs a detail request, so a full 15,000-job board takes longer; use filters or limits for very large boards.
  • Workday "Posted within N days" uses the list's "Posted N Days Ago" to skip detail requests for jobs that are clearly older.
  • Workday boards are matched on myworkdayjobs.com URLs. A bare name can't find a Workday board; paste the careers site URL.
  • Where a feed has no company display name (Lever, Ashby, Gem, Pinpoint, BambooHR, Workday), companyName is derived from the board name, e.g. palantir becomes "Palantir".
  • Careers-page detection reads the page HTML. Boards that only load later via JavaScript may not be detected; use the job-board URL or ats:name form instead.
  • A job URL still lists the board until that job is found (one request on Greenhouse, Ashby and Workable; page by page on Lever, Workday and SmartRecruiters), but only that job is delivered and charged. Ashby jobs marked unlisted are reported as not open.
  • A request that gets no answer within 30 seconds is tried at most 3 times in all, and a download may take up to 3 minutes; the warning names the URL. Errors such as 401 or 403 are not retried.
  • A bare name can match an unrelated or empty board on another ATS. The run summary shows which board was used.
  • The -eu suffix (lever-eu:board) is only for Lever's EU host. On another ATS it is ignored with a warning.
  • SmartRecruiters paging occasionally repeats a few rows and skips others; when fewer jobs come back than the board reports, the Actor pages once more to fill the gap.
  • Workable data comes from Workable's public careers-widget endpoint (apply.workable.com/api/v1/widget/accounts/<name>), which is less formally documented than the others.

The Actor reads only data that employers publish for the public through their ATS job-board APIs:

  • Greenhouse states that job board data is publicly available and requires no authentication for GET endpoints.
  • Lever notes that published postings are publicly viewable and may be scraped by third parties.
  • Ashby documents its public Job Postings API.
  • SmartRecruiters documents its public Posting API, which needs no authentication.
  • The other sources are the public feeds behind each employer's careers page: Workday's careers-site JSON, BambooHR's careers list, Personio's XML feed, Teamtailor's RSS feed, Breezy's /json list, Rippling's job-board API, Pinpoint's postings JSON, JazzHR's job export feed and Gem's job board API.
  • Requests are modest: three boards at a time and a few requests at a time per board, with back-off when a server asks to slow down.

The output contains job postings, not personal data about candidates. Recruiter contact mailboxes, application forms and screening questions included in some feeds are deliberately left out. You are responsible for using the data in line with applicable laws and each employer's terms.

Support

Found a board that doesn't work, or need another ATS? Open an issue on the Issues tab and include the company's careers page URL. Built and maintained by Steadybyte.