ATS Jobs Feed avatar

ATS Jobs Feed

Under maintenance

Pricing

from $2.00 / 1,000 results

Go to Apify Store
ATS Jobs Feed

ATS Jobs Feed

Under maintenance

Company names, domains or careers URLs in; every open job from Greenhouse, Lever and Ashby out, in one normalized schema. Optional monitor mode returns only new, updated and removed jobs.

Pricing

from $2.00 / 1,000 results

Rating

0.0

(0)

Developer

Agent Factory

Agent Factory

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

3 hours ago

Last modified

Share

ATS Jobs Feed: Greenhouse, Lever & Ashby jobs API

Give it a list of company names, domains or careers-page URLs. It finds each company's job board on Greenhouse, Lever or Ashby and returns every open job in one clean, normalized format, no matter which ATS the company uses. Turn on monitor mode and scheduled runs return only jobs that are new, updated or removed since the last run. That's what you want for hiring-signal alerts, job boards and sales prospecting.

  • ✅ Official public job-board APIs only. No browser, no proxies, no login, no anti-bot fights.
  • ✅ Auto-detection: acme.com → careers page → Greenhouse/Lever/Ashby board.
  • ✅ One schema across all three ATSs: title, department, team, locations, remote flag, employment type, salary (when published), dates, URLs.
  • ✅ Monitor mode with per-board state, so a temporary API error never shows up as "all jobs removed".
  • ✅ Companies whose board can't be found are not charged.

What can I use it for?

  • Hiring signals / sales intelligence: get notified when target accounts open (or close) roles.
  • Niche job boards and newsletters: pull fresh roles from a curated company list every day.
  • Recruiting and market research: see who is hiring, where and at what salary bands.
  • Job alerts: filter by title, location, department or remote, then schedule.

Input

FieldTypeDescription
companies (required)array of stringsAny mix of acme.com, https://acme.com/careers, https://jobs.lever.co/acme, https://job-boards.greenhouse.io/acme, https://jobs.ashbyhq.com/acme, greenhouse:acme, lever:acme, lever-eu:acme, ashby:acme, or plain names like Acme Inc
onlyNewSinceLastRunboolean (default false)Monitor mode: emit only new / updated / removed jobs
stateKeystring (default default)Name of the monitor. Use one key per watchlist/schedule
titleIncludes / titleExcludesarrayCase-insensitive substring filters on the job title
locationsarrayKeep jobs where any location contains one of these strings
remoteOnlybooleanKeep only jobs with isRemote = true
departmentsarrayMatch on department or team
postedWithinDaysintegerKeep jobs first posted in the last N days
includeDescriptionboolean (default false)Add a plain-text descriptionText
includeDescriptionHtmlboolean (default false)Add descriptionHtml
maxJobsPerCompanyintegerNewest N jobs per company
maxItemsintegerStop after N items in total

Example:

{
"companies": ["stripe.com", "https://jobs.lever.co/leverdemo", "ashby:ashby", "Airbnb"],
"titleIncludes": ["engineer"],
"onlyNewSinceLastRun": true,
"stateKey": "engineering-watchlist"
}

How companies are matched

  1. ATS URL or ats:slug: used directly (most reliable).
  2. Domain or careers URL: the Actor fetches the given URL, /careers, /jobs and the homepage (following redirects and a few same-site "Careers"/"Jobs" links, and respecting robots.txt), then looks for Greenhouse, Lever or Ashby board links and embeds.
  3. Plain names (or when step 2 finds nothing): likely board names are probed (Acme Robotics → acmerobotics, acme-robotics …) on Greenhouse → Lever → Ashby. Greenhouse probe hits are checked against the board's company name.

Tip: if a company is not found or the wrong board is matched, pass the board URL or ats:slug explicitly.

Output

One dataset item per job. The shape is the same for every ATS:

{
"company": "ashby",
"ats": "ashby",
"boardSlug": "ashby",
"jobId": "a819e537-3a23-4615-bbf8-4ee065d7e25d",
"title": "HR Business Partner, EMEA",
"department": "People & Talent",
"team": "People & Talent",
"locations": ["United Kingdom"],
"isRemote": true,
"workplaceType": "remote",
"employmentType": "full-time",
"compensation": { "min": 100000, "max": 135000, "currency": "GBP", "interval": "year", "raw": "£100K - £135K" },
"postedAt": "2026-10-07T18:45:13.968Z",
"updatedAt": null,
"jobUrl": "https://jobs.ashbyhq.com/ashby/a819e537-3a23-4615-bbf8-4ee065d7e25d",
"applyUrl": "https://jobs.ashbyhq.com/ashby/a819e537-3a23-4615-bbf8-4ee065d7e25d/application",
"firstSeenAt": null,
"scrapedAt": "2026-10-08T01:35:42.777Z",
"input": "ashby:ashby"
}
FieldNotes
companyGreenhouse company name. For Lever/Ashby it's your input name, or the board slug for URL/domain inputs
ats, boardSluggreenhouse / lever / ashby and the board name
isRemotetrue fully remote, false on-site or hybrid, null unknown
workplaceTyperemote / hybrid / onsite / null
employmentTypefull-time, part-time, contract, internship, temporary, or the ATS's raw value
compensation{min, max, currency, interval, raw} when the ATS publishes it (Lever, Ashby), else null
postedAt, updatedAtISO 8601 UTC. updatedAt is only provided by Greenhouse
descriptionText, descriptionHtmlOnly when requested
changeType, changedFields, firstSeenAtMonitor mode only (see below)
inputThe input string this job came from

The run also writes an OUTPUT record to the default key-value store, so you can see which companies were matched and which were missed:

{ "mode": "full", "totals": { "companies": 3, "resolved": 2, "unresolved": 1, "jobsEmitted": 967 },
"companies": {
"figma.com": { "resolved": true, "ats": "greenhouse", "slug": "figma", "jobCount": 153, "resolvedVia": "careers-page" },
"openai.com": { "resolved": true, "ats": "ashby", "slug": "openai", "jobCount": 814, "resolvedVia": "careers-page" },
"Totally Unknown Widgets LLC": { "resolved": false, "reason": "No Greenhouse, Lever or Ashby board found" } } }

You can export the dataset as JSON, CSV, Excel or XML, or read it through the Apify API.

Monitor mode (only new, updated and removed jobs)

Set onlyNewSinceLastRun: true and schedule the Actor (e.g. daily).

  • First run = baseline: every current job is emitted with changeType: "new".
  • Later runs emit only:
    • new: job wasn't on the board last time.
    • updated: title, department, team, locations, remote/workplace, employment type, compensation, URLs or description text changed. changedFields lists what changed. (Greenhouse's updated_at timestamp alone does not count, because it changes on internal edits.)
    • removed: job is no longer listed. The item is the last snapshot we saw.
  • firstSeenAt = when the job was first seen by this monitor.
  • State lives in the named key-value store ats-jobs-feed-state, one record per board plus the cached company → board matches. Use a different stateKey per watchlist.
  • If a board fails to load (API down), its state is left untouched, so you won't get false "removed" events.
  • If a run is cut short by maxItems or your spending limit, that board's state isn't saved, and the missed changes are emitted next run.
  • Filters apply to emitted changes, so you can change filters without corrupting history.

Pricing (pay per result)

You pay $2 per 1,000 results ($0.002 per item in the dataset), plus a tiny Actor-start fee of $0.00005 per run.

  • Full mode: one result = one open job.
  • Monitor mode: one result = one new, updated or removed job. Runs with no changes cost only the start fee.
  • Companies whose board can't be found produce no items, so they cost nothing.

Example: 50 companies with 2,000 open jobs = about $4 for a full pull. A daily monitor of the same 50 companies with ~40 changes per day costs about $0.08 per run. Use maxItems / maxJobsPerCompany to cap a run. The Actor also respects your run's maximum charge limit and stops cleanly when it is reached.

FAQ

A company wasn't found. What now? Check OUTPUT for the reason. The company may use a different ATS (Workday, iCIMS, SmartRecruiters … not supported yet), host jobs on a custom site, or block automated page fetches. Pass the board URL or greenhouse:/lever:/ashby:slug directly if you know it.

The wrong board was matched for a plain name. Name probing can't always tell two companies with the same name apart. Use a domain or board URL for those companies.

Why is compensation empty for Greenhouse jobs? Greenhouse's list endpoint doesn't include pay ranges (they need one extra request per job). Lever and Ashby include salary when the company publishes it.

Do I need proxies? No. These are public, unauthenticated JSON APIs.

Limitations

  • Supports Greenhouse, Lever (global and EU) and Ashby. Other ATSs aren't covered.
  • Greenhouse: no compensation; team is the second department level when present. Prospect / "General application" posts are skipped.
  • Lever and Ashby don't expose an updatedAt timestamp.
  • Ashby jobs marked isListed: false (direct-link only) are skipped.
  • Careers-page detection reads static HTML only. Boards loaded purely by client-side JavaScript from a non-obvious URL may need the board URL as input.
  • Some company websites block automated requests; the Actor then falls back to name probing.

The Actor only calls the ATS vendors' documented public job-board endpoints, which publish data that companies have deliberately made public:

  • Greenhouse Job Board API: "Job Board data is publicly available, so authentication is not required for any GET endpoints" (docs).
  • Lever Postings API: "all job postings in the published state are publicly viewable. These jobs may be scraped by third parties" (docs).
  • Ashby public Job Postings API (docs).

It identifies itself in the User-Agent, keeps at most 5 concurrent requests per host, follows Lever's 1-second crawl delay, backs off on 429/5xx, respects robots.txt on company websites, and only fetches the companies you list (no crawling). Job postings contain no personal data beyond what employers publish. You are responsible for how you use the data, including any rules in the destination where you republish it.

Run locally (no Apify account needed)

npm install
mkdir -p storage/key_value_stores/default
echo '{"companies":["ashby:ashby","leverdemo.com"]}' > storage/key_value_stores/default/INPUT.json
node src/main.js # results in storage/datasets/default, OUTPUT in storage/key_value_stores/default
npm test # offline tests on recorded fixtures