ATS API — Job Board, Job Listings & Applicant Tracking | $3/1K avatar

ATS API — Job Board, Job Listings & Applicant Tracking | $3/1K

Pricing

from $3.00 / 1,000 job listings

Go to Apify Store
ATS API — Job Board, Job Listings & Applicant Tracking | $3/1K

ATS API — Job Board, Job Listings & Applicant Tracking | $3/1K

Unofficial — not affiliated with any ATS vendor. ATS aggregator and job board API: give company names, domains or careers page URLs, it detects the applicant tracking system (Greenhouse, Lever, Ashby, Workday, Workable, Personio, Rippling) and normalizes every open role into one schema.

Pricing

from $3.00 / 1,000 job listings

Rating

0.0

(0)

Developer

KENKO

KENKO

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

17 hours ago

Last modified

Share

ATS Jobs Scraper — Greenhouse, Lever, Ashby, Workday | $3/1K

Unofficial. This Actor is not affiliated with, endorsed by, or sponsored by Greenhouse, Lever, Ashby, Workday, Workable, Personio or Rippling. All product names and trademarks belong to their respective owners and are used here only to describe which job boards this Actor can read. It reads each vendor's own public feed, subject to that vendor's robots.txt.

A job listings API for company career pages. Give it company names. Get back every open role, normalized.

{ "companies": ["stripe", "linear.app", "https://jobs.lever.co/spotify"] }

You do not have to know which applicant-tracking system a company uses. The Actor works it out, reads the board, and returns one row shape no matter how many different systems a run touched.

Use it as a Greenhouse job board API, a Lever jobs API, an Ashby jobs API, a Workday job board API or a Workable jobs API — or point it at a mixed list and let it sort out which is which.

Why this exists

Job data on the market is scraped HTML. That breaks every time a career page is redesigned, and it needs proxies and anti-bot handling to collect at all.

This reads the JSON and XML feeds the ATS vendors publish themselvesboards-api.greenhouse.io, api.lever.co, api.ashbyhq.com and four more. Nothing here logs in, uses a proxy, solves a challenge, spoofs a user agent, or parses a layout. The user agent is atsfeed/1.0 and says where it came from.

Six of the seven are documented, unauthenticated vendor endpoints. Workday is the exception and is labelled as one: it is an undocumented career-site endpoint, read from a career-site URL you supply, not a published API. It is included because it is where a large share of enterprise roles actually live — but you should know which of these you are depending on.

What it will not read

Every host is checked against its own robots.txt before the first request, and a published Crawl-delay sets the spacing. If a vendor changes its mind about who may read a feed, this Actor stops on its own rather than waiting for someone to notice.

That check is why two systems are not supported:

  • SmartRecruitersapi.smartrecruiters.com/robots.txt allows exactly one agent, LinkedInBot, and disallows everything else. We are everything else.
  • Breezy — its /json endpoint is undocumented and its terms speak against automated collection.

Requests are also capped per host — at most 4 in flight and 5 per second, and half that into a single Workday tenant. A whole 2,000-role Workday board takes about 2½ minutes rather than 8 seconds. That is deliberate: a job feed that gets its source annoyed is worth nothing next month.

Supported systems

ATSAddressed byDescriptionsEndpoint
Greenhouseslug, boards.greenhouse.io/…yesdocumented
Leverslug, jobs.lever.co/…yesdocumented
Ashbyslug, jobs.ashbyhq.com/…yesdocumented
Workableslug, apply.workable.com/…yesdocumented
Personioslug, *.jobs.personio.deyesdocumented
Ripplingslug, ats.rippling.com/…documented
Workdayfull careers URL (*.myworkdayjobs.com/…)undocumented

Workday is addressed by URL rather than name because its host encodes the data centre and its path encodes both tenant and site — none of which can be derived from a company name.

Input

Everything but companies is optional.

FieldMeaning
companiesNames, domains, board URLs, or provider:slug.
keywords / excludeKeywordsTitle filters. Whole-word, so go does not match Google. Multi-word entries match as phrases.
locations / countries / departmentsFurther filters. countries takes ISO-2 codes.
remoteOnlyRemote only. Hybrid is excluded, and so are roles the board says nothing about.
postedWithinDaysRecency. Undated roles are excluded — they cannot be shown to be recent.
searchDescriptionsMatch keywords against the body text too.
includeDescription / includeDescriptionHtmlAdd the description as plain text and/or original HTML.
maxJobsPerCompany / maxJobsTotalCost ceilings. Large Workday tenants list 20,000+ roles.

Output

One row per role:

provider company companySlug jobId title department team
employmentType location locations[] country workplaceType isRemote
postedAt updatedAt salaryText salaryMin salaryMax salaryCurrency
salaryPeriod url applyUrl description descriptionHtml scrapedAt

Measured coverage over 1,287 live roles from 11 companies across all 7 systems:

FieldPresent
title, url, location100%
country99.3%
postedAt88.7%
department77.0%
workplaceType49.6%
employmentType41.6%
salaryMin13.1%

The lower rows are not gaps in this Actor — they are fields the boards themselves leave blank. Rippling publishes no dates at all; most companies publish no pay. A field is null when the source did not say. Nothing is inferred to fill a column, because a guessed salary is worse than an empty one.

department is 100% on every system except Workday, which drags the average down on its own. Workday postings carry no category at all, only a jobFamilyGroup search facet. Taking a whole Workday board fills department for 100% of its rows by replaying the search once per category — NVIDIA's 2,000 postings, fully attributed. Taking a slice does not, because labelling even 300 rows means sweeping all 2,617 postings, and the run log says so out loud when it declines.

The 0.7% of rows with no country are rows whose location names no place: "Remote", "N/A", "LOCATION".

Four judgement calls worth knowing about:

  • workplaceType is null, not "onsite", when a board is silent. Silence is not evidence of an office.
  • A role open in several places is "remote" if any one of those places reads remote. The order a board lists its offices in is its ordering, not a statement about the role, so reading only the first would decide remoteOnly at random.
  • A bare two-letter code in a location is read as a US state (Austin, TX → US) because US boards write City, ST constantly while non-US boards spell the country out (Berlin, Germany). So Berlin, DE resolves to US — spell the country out and it resolves correctly.
  • A city with one dominant reading resolves its own country (London → GB, Bengaluru → IN). This is only consulted after subdivision codes, so the US homonyms still win where a board qualifies them: London, KY → US.

Pricing

Pay per job returned: $3 per 1,000 roles, flat — the same rate on every plan, with no volume tier that quietly makes the number in the title wrong for whichever tier you happen to be on. Plus a $0.005 run start fee. maxJobsTotal is a hard ceiling, so a run cannot cost more than you allowed it to — including after a platform migration mid-run.

Notes

  • De-duplication: companies commonly run two boards at once (a Greenhouse board and a Workday mirror). The same role from both collapses to one row.
  • Missing companies: if a company self-hosts its careers page or uses an ATS not listed above, the run reports it under SUMMARY.failures rather than failing. Pass a board URL directly if auto-detection misses it.
  • Moved boards: a slug whose board has been closed usually redirects to the vendor's marketing site. That is reported as a miss, not retried.
  • Resumable: progress is checkpointed, so a migrated run neither re-fetches nor re-charges for boards already delivered.
  • Downstream use: apply.workable.com publishes
    Content-Signal: ai-train=no
    . It does not restrict this Actor, but it is a stated preference about what may be done with the rows afterwards, and you should know it exists.

Common questions

How do I scrape company career pages without breaking every month? Don't parse the page. Every system here publishes a machine-readable feed behind the careers page, and that feed changes far less often than the design does. This Actor reads the feed.

Is there a free job board API? The vendor feeds are free and unauthenticated — you can call boards-api.greenhouse.io yourself. What you pay for here is the part that is tedious rather than hard: working out which of seven applicant tracking systems a company is on, addressing each one correctly, obeying seven different robots.txt files and rate limits, and flattening seven incompatible payloads into one row shape with real country and salary parsing.

Can I get a job posting API across many companies at once? Yes — pass a list. Boards are fetched concurrently within the per-host limits, duplicates across a company's two boards collapse to one row, and maxJobsTotal caps what the run can cost you.

Which applicant tracking system API does a given company use? Pass the company name or domain and read the provider column. Detection is the first thing the run does, and it is reported per company in SUMMARY.

Development

npm install
npm test # 66 tests, no network
npm run demo # live run against real boards