ATS Jobs Feed (Greenhouse, Lever, Ashby & more) avatar

ATS Jobs Feed (Greenhouse, Lever, Ashby & more)

Pricing

from $2.00 / 1,000 job exporteds

Go to Apify Store
ATS Jobs Feed (Greenhouse, Lever, Ashby & more)

ATS Jobs Feed (Greenhouse, Lever, Ashby & more)

Job listings from the public JSON APIs of Greenhouse, Lever, Ashby, Workable and SmartRecruiters. One clean record per job (Markdown description, department, locations, remote flag, salary when published, apply URL) plus a diff mode that flags new, updated and closed jobs between runs.

Pricing

from $2.00 / 1,000 job exporteds

Rating

0.0

(0)

Developer

Vellum Kasane

Vellum Kasane

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

3 days ago

Last modified

Categories

Share

ATS Jobs Feed (Greenhouse, Lever, Ashby, Workable, SmartRecruiters)

Get every open job of any company that runs its careers page on Greenhouse, Lever, Ashby, Workable or SmartRecruiters as clean, structured records, straight from the vendors' public job-board JSON APIs. No HTML scraping, no browser, no proxies: a run over four boards finishes in a few seconds.

Each job comes out as one record with the description converted to Markdown, department and team, all locations, an inferred remote flag, employment type, the published salary range when the company discloses one, the hosted job URL and the apply URL. Turn on diff mode and a scheduled run becomes a change feed: every record is marked new, updated or closed compared with the previous run, so you only pay for what changed.

Built for:

  • Job boards and aggregators that want one normalised schema across five ATS vendors instead of five parsers.
  • Recruiting and sales intelligence: watch competitors' or target accounts' hiring (new engineering roles in Berlin, closed positions, salary bands).
  • AI agents and MCP clients that need job descriptions as Markdown (call it from the Apify MCP server, read the dataset).
  • Alerts: schedule hourly with diff: true, plug the dataset into Slack, email or a webhook via Apify integrations.

How it works

  1. Give the Actor a list of companies. Each entry is { "ats": "greenhouse", "slug": "gitlab" } or just the careers URL (https://jobs.lever.co/spotify, https://jobs.ashbyhq.com/linear, https://apply.workable.com/blueground/, https://careers.smartrecruiters.com/Bosch). The ATS and the board slug are detected from the URL.
  2. The Actor calls the vendor's public JSON endpoint for that board (see Data sources), normalises every job and converts the HTML description to GitHub-flavoured Markdown.
  3. Filters (keywords, location, remote, department) are applied, then records are written to the dataset, one per job, and billed per record.
  4. In diff mode the Actor keeps a compact snapshot of every job it has seen in a named key-value store and emits only changes.

Input

FieldTypeDefaultNotes
companiesarrayrequired{ ats, slug } objects and/or board URLs. ats is one of greenhouse, lever, ashby, workable, smartrecruiters. Lever EU boards: { "ats": "lever", "slug": "acme", "region": "eu" } or the jobs.eu.lever.co URL.
keywordsstring[][]Keep jobs whose title or description contains any of these (case-insensitive).
locationContainsstring[][]Keep jobs whose location contains any of these.
remoteOnlybooleanfalseKeep only jobs with remote: true.
departmentsstring[][]Keep jobs whose department / team contains any of these.
maxJobsinteger500Hard cap on exported records per run (all boards). Caps your cost.
diffbooleanfalseDiff mode: mark new / updated / closed versus the previous run.
emitUnchangedbooleanfalseDiff mode: also export unchanged jobs (status unchanged).
stateStoreNamestringats-jobs-feed-stateNamed key-value store holding the diff state. Use one per schedule / filter set.
includeHtmlbooleanfalseAdd the original description HTML (description_html).

Example input:

{
"companies": [
{ "ats": "greenhouse", "slug": "greenhouse" },
"https://jobs.ashbyhq.com/posthog",
{ "ats": "workable", "slug": "blueground" },
{ "ats": "smartrecruiters", "slug": "smartrecruiters" }
],
"keywords": ["engineer"],
"remoteOnly": true,
"maxJobs": 200,
"diff": true
}

Finding the slug: it is the token in the company's careers URL, e.g. boards.greenhouse.io/**gitlab**, job-boards.greenhouse.io/**gitlab**, jobs.lever.co/**spotify**, jobs.ashbyhq.com/**linear**, apply.workable.com/**blueground**, careers.smartrecruiters.com/**Bosch**. Unknown boards are reported per board in SUMMARY.json and skipped; the run still succeeds for the other boards.

Output

One dataset item per job (you can download it as JSON, CSV, Excel or HTML):

{
"company": "PostHog",
"company_slug": "posthog",
"ats": "ashby",
"board_url": "https://api.ashbyhq.com/posting-api/job-board/posthog?includeCompensation=true",
"job_id": "2f1e5d7c-3c1e-4a0f-9b1d-6f0a3d2c1b9e",
"title": "Product Engineer",
"department": "Engineering",
"team": "Product",
"location": "Remote - EMEA",
"locations": ["Remote - EMEA", "Remote - US"],
"remote": true,
"workplace_type": "remote",
"employment_type": "Full-time",
"salary": {
"min": 150000,
"max": 220000,
"currency": "USD",
"interval": "year",
"summary": "$150K - $220K",
"source": "ats"
},
"description_markdown": "## About the role\n\nYou will ship features end to end...\n\n- Own a product area\n- Talk to users every week",
"job_url": "https://jobs.ashbyhq.com/posthog/2f1e5d7c-3c1e-4a0f-9b1d-6f0a3d2c1b9e",
"apply_url": "https://jobs.ashbyhq.com/posthog/2f1e5d7c-3c1e-4a0f-9b1d-6f0a3d2c1b9e/application",
"published_at": "2026-08-12T09:14:02.000Z",
"updated_at": null,
"first_seen": "2026-08-12T09:14:02.000Z",
"status": "listed",
"closed_at": null,
"content_hash": "6d0c5c0b8f7c0d5c3c1e9a3b0f2a7d4e1c8b6a9f0e3d2c1b4a5f6e7d8c9b0a1f",
"fetched_at": "2026-09-22T14:46:40.278Z"
}
FieldMeaning
statuslisted (diff off) or new / updated / unchanged / closed (diff on). Closed records carry the last known title, location, department and URLs; description_markdown is null.
remote / workplace_typeFrom the ATS flag when it exists (Ashby isRemote / workplaceType, Lever workplaceType, Workable telecommuting, SmartRecruiters location.remote), otherwise inferred from the location and title ("Remote", "Hybrid", "Work from home", ...).
salaryStructured pay range from Greenhouse (pay_input_ranges), Lever (salaryRange) or Ashby (compensation), source: "ats". When the ATS has no field but the description states a range such as $120,000 - $160,000, it is parsed with source: "description". Otherwise null.
updated_atOnly Greenhouse exposes it. Change detection uses content_hash (title + location + department + type + description), so it works for every vendor.
first_seenDiff mode: the first run in which the job was seen. Otherwise the ATS publish date.

SUMMARY.json in the key-value store lists, per board, the API URL, jobs fetched, jobs matching the filters, jobs exported, and new / updated / unchanged / closed counts, plus any per-board error.

Diff mode

With diff: true the Actor stores STATE in the named key-value store (stateStoreName): one compact snapshot per job (title, location, URLs, first_seen, content_hash). On the next run:

  • job not in the state: new
  • job in the state with a different content_hash: updated
  • job in the state that is no longer on the board: closed (with closed_at)
  • same hash: unchanged (only exported when emitUnchanged is on)

Filters are applied before diffing, so the state only tracks jobs matching your filters; changing the filters therefore produces one wave of new / closed records. A board that fails to load leaves its part of the state untouched (no false closures). State is saved after every board, so a run that hits maxJobs or its budget resumes cleanly.

Pricing

This Actor uses pay-per-event pricing:

EventPriceWhen
job-exported$0.002 per record ($2 per 1,000)Once per record written to the dataset, including closed records in diff mode. Filtered-out jobs, unchanged jobs (unless emitUnchanged), duplicates and failed boards are never charged.
Actor start$0.005 per runApify's standard start event; covers the first seconds of compute.

Examples: a one-off pull of a 300-job board costs about $0.61. An hourly diff schedule over 50 boards that together change 40 jobs a day costs about $0.20 a day in events plus 24 starts ($0.12). Set Max total charge per run to hard-cap spend; the Actor stops gracefully before exceeding it.

Data sources and limits

ATSEndpoint usedDocumentationNotes
GreenhouseGET https://boards-api.greenhouse.io/v1/boards/{slug}/jobs?content=true&pay_transparency=truehttps://docs.greenhouse.io/job-board.html"Job Board data is publicly available, so authentication is not required." The description is delivered entity-encoded and is decoded before conversion. No pagination (whole board in one response, several MB for large companies).
LeverGET https://api.lever.co/v0/postings/{slug}?mode=json (EU: api.eu.lever.co)https://github.com/lever/postings-apiPublic, no key for reads. Description, sections (lists) and closing text are merged into one document. Boards with no public postings return [].
AshbyGET https://api.ashbyhq.com/posting-api/job-board/{slug}?includeCompensation=truehttps://developers.ashbyhq.com/docs/public-job-posting-apiUnlisted jobs (isListed: false) are skipped. Compensation tiers are summarised into salary.
WorkableGET https://www.workable.com/api/accounts/{subdomain}?details=true (redirects to apply.workable.com/api/v1/widget/accounts/{subdomain})https://workable.readme.io/reference/jobs-1Public widget endpoint ("security": [{}]), returns published jobs with descriptions. No structured salary. Accounts without a public careers page return Not Found.
SmartRecruitersGET https://api.smartrecruiters.com/v1/companies/{id}/postings?limit=100&offset=N + GET .../postings/{postingId}https://developers.smartrecruiters.com/docs/endpointsPublic Posting API; the description lives in the detail call, so each job costs one extra request (fetched with concurrency 4, only for jobs that pass the metadata filters). No structured salary.

Other limits:

  • The Actor reads only what the vendors publish on their public job-board APIs; it does not scrape careers pages, so companies on other ATS vendors (Workday, Taleo, iCIMS, BambooHR, Personio...) are not supported.
  • Job ids are stable per vendor, but a company migrating from one ATS to another changes every id (one wave of closed + new).
  • Vendors do not document rate limits for these endpoints; the Actor fetches boards sequentially and retries 429/5xx with backoff. Runs with hundreds of boards may take a few minutes.

Tips

  • Follow a competitor set: put 20-50 boards in companies, diff: true, schedule hourly, and connect the dataset to Slack. Only changes are billed.
  • Salary research: keywords: ["engineer"], then filter the dataset on salary.source == "ats" for disclosed ranges (Greenhouse pay-transparency boards and Ashby publish the most).
  • Remote roles only: remoteOnly: true combines the vendor flag with text inference; check workplace_type for hybrid.
  • Different filter sets: use a separate stateStoreName per schedule so the diffs do not interfere.

Verified

Local run on 2026-09-22 with apify run --purge (Apify CLI 1.10.0, Apify SDK 3.7.2, Node 24), prefill input above (four boards, maxJobs: 40):

  • 4 boards fetched (greenhouse:greenhouse 21 jobs, ashby:posthog 9, workable:blueground 7, smartrecruiters:smartrecruiters 1) - 38 records exported, 0 errors, runtime about 3 seconds.
  • Output spot-checked per vendor: Markdown descriptions without leftover tags or entities, locations, remote inference, employment type, salary from Ashby compensation, Greenhouse updated_at.
  • Diff mode tested with a separate state store: first run marked every job new; after editing the stored state (one job removed, one hash changed, one phantom id added), the second run produced exactly one new, one updated and one closed record and no others.
  • Pay-per-event path tested with ACTOR_TEST_PAY_PER_EVENT=1 ACTOR_MAX_TOTAL_CHARGE_USD=5 (local test price $1/event): 5 job-exported events charged, then the Actor stopped with "Run budget reached (maxTotalChargeUsd)".
  • Unit tests: npm test (13 tests over real API fixtures: ATS detection from URLs, all five normalisers, entity decoding, salary parsing, remote inference, filters, diff).
  • Endpoint checks the same day: Greenhouse boards greenhouse, gitlab, stripe, figma, duolingo, anthropic respond; Lever leverdemo, spotify, palantir respond (netlify, plaid return 404 - those companies no longer use Lever); Ashby notion, linear, posthog, ramp, openai respond; Workable blueground, skroutz, nuvei respond; SmartRecruiters smartrecruiters responds.

Support

Open an issue on the Actor's Issues tab. Issues are answered within one business day; bug fixes ship as new builds without changing the input schema.

Made by Vellum Labs.