Career Site Jobs Monitor - Greenhouse, Lever, Ashby & more avatar

Career Site Jobs Monitor - Greenhouse, Lever, Ashby & more

Pricing

$1.00 / 1,000 job posting scrapeds

Go to Apify Store
Career Site Jobs Monitor - Greenhouse, Lever, Ashby & more

Career Site Jobs Monitor - Greenhouse, Lever, Ashby & more

Live job postings from career sites on Greenhouse, Lever, Ashby, Workable and SmartRecruiters via their public JSON APIs. No login, no proxies. Paste a careers URL and the ATS is auto-detected. Stable dedup keys, ISO dates and keyword/location/date filters for new-roles-since-last-run monitoring.

Pricing

$1.00 / 1,000 job posting scrapeds

Rating

0.0

(0)

Developer

Imran

Imran

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

3 days ago

Last modified

Share

Career Site Jobs Monitor — Greenhouse, Lever, Ashby, Workable, SmartRecruiters

Pull live job postings straight from company career sites. Give it a list of companies (an ATS board URL, an ats:slug, or just the company's careers page) and it returns every open role as flat JSON — with a stable dedupKey, ISO timestamps and filters, so you can run it on a schedule and get only the roles that are new since last time.

No login, no API key, no proxy. Each supported ATS publishes a public JSON endpoint that companies use to syndicate their own postings. This actor reads those endpoints and nothing else, which is why it's fast and doesn't break the way page scrapers do.

Supported ATS platforms

ATSWhat you can passDescriptions
Greenhousegreenhouse:stripe, https://boards.greenhouse.io/stripe, job-boards.greenhouse.io/…, EU boards✔ (includeDescription)
Leverlever:spotify, https://jobs.lever.co/spotify, EU boards
Ashbyashby:notion, https://jobs.ashbyhq.com/notion✔ (+ published compensation)
Workableworkable:acme, https://apply.workable.com/acme, acme.workable.com✖ (list endpoint only)
SmartRecruiterssmartrecruiters:BoschGroup, https://jobs.smartrecruiters.com/BoschGroup✖ (list endpoint only)

Any careers page URL (https://acme.com/careers) also works: the actor fetches the page, finds the embedded board of any of the five ATSs, and continues from there. If a page uses an unsupported ATS you get a warning naming the URL, never a silent empty result.

What it does

  1. Resolves each company entry to an ATS board (explicit, by URL, or by page discovery).
  2. Reads the board's public JSON API, following pagination where the ATS paginates.
  3. Normalises every posting into the same flat record regardless of ATS.
  4. Applies your filters — title keywords, excluded keywords, locations (incl. remote), postedAfter.
  5. Deduplicates within the run and pushes one record per posting, up to maxItems.

Input

FieldTypeDefaultNotes
companiesarray of stringsRequired. ATS URLs, ats:slug, or careers page URLs
keywordsarray[]Keep postings whose title contains any keyword (case-insensitive)
excludeKeywordsarray[]Drop postings whose title contains any of these
locationsarray[]Substring match on location; include "remote" to also keep remote-flagged roles
postedAfterstringISO date/date-time. Drops older postings; keeps postings with no date
includeDescriptionbooleanfalseAdds plain-text descriptionText (Greenhouse, Lever, Ashby)
maxItemsinteger500Billing guardrail — hard cap on postings pushed per run
proxyConfigurationobjectoffNot needed for these APIs; enable only at very high volume

Example — every engineering role at four companies, UK or remote, posted this month:

{
"companies": ["greenhouse:stripe", "https://jobs.lever.co/spotify", "https://jobs.ashbyhq.com/notion", "https://jobs.smartrecruiters.com/BoschGroup"],
"keywords": ["engineer"],
"locations": ["United Kingdom", "London", "remote"],
"postedAfter": "2026-08-01",
"maxItems": 200
}

Output

One JSON object per posting in the default dataset:

FieldTypeDescription
sourcestringgreenhouse | lever | ashby | workable | smartrecruiters
companystringBoard slug on that ATS
companyNamestring | nullDisplay name when the ATS provides it
jobIdstringPosting ID on the ATS
titlestringJob title
locationstring | nullLocation text
isRemotebooleanATS remote flag, or inferred from the location text
department, teamstring | nullWhen the ATS provides them
employmentTypestring | nulle.g. Full-time
compensationstring | nullPublished pay range (Ashby)
postedAtstring | nullISO 8601 publish date
updatedAtstring | nullISO 8601 last update (Greenhouse)
url, applyUrlstringPublic posting / apply links
descriptionTextstring | nullPlain text, only with includeDescription
dedupKeystringStable key source:company:jobId
scrapedAtstringISO 8601

Monitoring: new roles since last run

dedupKey never changes for a given posting, so the recipe is:

  1. Schedule the actor (daily works well; hourly for hot accounts).
  2. Either set postedAfter to the previous run's time, or diff each run's dataset against the last one on dedupKey — new keys are new roles, missing keys are closed roles.
  3. Send the difference wherever you like (Slack, email, a Google Sheet) with an Apify integration.

Typical uses: hiring-signal alerts for sales and recruitment teams, tracking competitor hiring, feeding a niche job board, watching a shortlist of dream companies.

Pricing

Pay per event: one charge per posting pushed to the dataset. Postings removed by your filters are free. maxItems caps the spend of any single run.

Notes & limitations

  • Workable and SmartRecruiters list endpoints don't include descriptions; includeDescription is ignored for them.
  • A board that returns 404 (wrong slug, or the company moved to a different ATS) is reported in the log and skipped — it doesn't fail the run.
  • Discovery reads the careers page HTML you give it. Boards injected by client-side JavaScript that never appear in the HTML source can't be found that way — pass the board URL directly.
  • Sources are the ATSs' own public APIs; the actor sends a descriptive User-Agent and backs off on 429.

Development

npm install
npm test # unit tests (parsers, normalisers, filters — no network)
apify run # local run with storage/key_value_stores/default/INPUT.json