ATS Jobs Scraper: Workday, Greenhouse, Lever, Ashby avatar

ATS Jobs Scraper: Workday, Greenhouse, Lever, Ashby

Pricing

from $3.00 / 1,000 job delivereds

Go to Apify Store
ATS Jobs Scraper: Workday, Greenhouse, Lever, Ashby

ATS Jobs Scraper: Workday, Greenhouse, Lever, Ashby

Type a company name, get its open jobs. Auto-detects the career site on 13 ATS (Workday, Greenhouse, Lever, Ashby, SmartRecruiters...) and returns one clean format: salary in USD, skills, seniority, visa sponsorship. New and closed job alerts. Pay per job delivered.

Pricing

from $3.00 / 1,000 job delivereds

Rating

0.0

(0)

Developer

Sinan Dogan

Sinan Dogan

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

16 hours ago

Last modified

Share

What does ATS Jobs Scraper do?

Type a company name, get its open jobs. ATS Jobs Scraper finds a company's career site on 13 applicant tracking systems (ATS) (Workday, Greenhouse, Lever, Ashby, SmartRecruiters, Workable, Recruitee, Teamtailor, Personio, Breezy HR, Pinpoint, BambooHR and Rippling) and returns every open job in one clean, comparable format. It works as a Workday, Greenhouse and Lever jobs API: no ATS account, no API key, no browser.

  • 🔎 No URL hunting: enter Stripe, nvidia.com or a job-board URL. The Actor detects the ATS itself, follows "Careers" links on the company website, and tells you how sure it is (matchConfidence).
  • 💰 Salaries you can compare: published pay ranges, plus the ones written in the description, annualised and converted to USD. Filter on "at least 100,000 USD a year" across hourly, monthly and yearly offers.
  • 🧠 Every job enriched, free: about 180 skills detected, seniority, visa sponsorship, years of experience, remote/hybrid/on-site, contract type, company domain and LinkedIn page.
  • 📊 Hiring signals per company: one row per company with open jobs by department, location and seniority, top skills and remote share. Built for Clay, CRMs and account scoring.
  • 🔔 New and closed job alerts: schedule it and get a Slack, Discord or Telegram message (or a webhook) the day a company opens, changes or closes a role.
  • 💲 Pay only for the jobs you get: $3 per 1,000 jobs delivered. Jobs dropped by your filters cost nothing, and a monitoring run where nothing changed costs nothing per job.

Who uses it:

You areYou use it to
Sales and growth teamsSpot companies hiring for the roles your product serves (a new "Head of Security" means a security budget) and push the signal to Clay, HubSpot or Salesforce.
Recruiters and staffing agenciesWatch target accounts and be first on every new opening, with the salary and the skills already extracted.
Job boards and HR techAggregate fresh jobs straight from the source ATS, deduplicated (jobKey) and in one schema.
Investors and analystsTrack hiring velocity, departments and locations of portfolio and competitor companies over time.

How to scrape Workday, Greenhouse and Lever jobs

  1. Keep the two example Companies, or enter your own company names or career URLs.
  2. Keep Max jobs per company at 20 for a small first run.
  3. Click Start, then inspect the dataset, the Run summary and the Markdown Report outputs.
  4. Download the dataset as JSON, HTML, CSV or Excel. Some sources do not publish every field; missing values remain null.
{
"companies": ["Stripe", "https://jobs.lever.co/spotify"],
"maxJobsPerCompany": 20,
"includeDescription": true
}

Why use this jobs scraper?

  • Thirteen ATS, one format: compare roles without normalizing thirteen different exports. Workday, Greenhouse, Lever, Ashby, Workable, Recruitee, SmartRecruiters, Teamtailor, Personio, Breezy HR, Pinpoint, BambooHR (https://<company>.bamboohr.com/careers) and Rippling (https://ats.rippling.com/<company>/jobs) are read through the public feeds their own career pages use.
  • Workday career sites: paste a Workday URL such as https://acme.wd5.myworkdayjobs.com/en-US/External or https://wd3.myworkdaysite.com/recruiting/acme/careers (locale segments and job links work too), or a company website that links to it. Every job is paginated; sites with more than 2,000 jobs are split by job category, because Workday searches stop at 2,000. Descriptions, contract type, country, all locations and the exact posting date are fetched job by job (5 at a time), only for saved jobs or when description filters (keywords, required skills, visa sponsorship, experience) need them.
  • Careers pages followed: for a company website, the Actor checks the page you gave and the homepage, then follows up to two same-company links such as "Careers", "Jobs", "Join us", "Work with us", "Emploi", "Recrutement", "Karriere" or "Carreras", then up to two such links on those careers pages (large companies often link their Workday site from a job search page), including redirects to the ATS, and finally tries /careers and /jobs.
  • Company names reach Workday: for a plain name, the Actor also looks for the company homepage at <name>.com, .fr, .io, .ai, .de and .co, and accepts one only when its site name or title names the company (a parked stripe.de or a placeholder revolut.io is rejected). It then follows that website's careers links like above, so "Nvidia" finds nvidia.wd5.myworkdayjobs.com. A board linked from the website wins over a board guessed from the name. No third-party lookup service is called. Sites behind bot protection (for example Airbus or Revolut) cannot be read: paste their job-board URL.
  • Flexible company input: use names for convenience or exact job-board URLs for precise targeting. Website and name discovery are heuristics, so every company gets a matchConfidence in the summary: certain (job-board URL), high (linked from the company website you gave or found from the name, or name confirmed by the ATS), medium (board name only) or ambiguous (namesakes found on several ATS, listed so you can pick the right URL).
  • Filters that match how people search: workplace type (remote, hybrid, on-site), contract type (full-time, part-time, contract, internship, temporary), minimum salary, jobs with a published salary only, title keywords and exclusions, location, department, publication age, seniority level, required skills, visa sponsorship and maximum years of experience. Many companies do not publish contract or workplace types; the filters let you keep or drop those jobs explicitly instead of hiding them.
  • More salaries: structured pay ranges from the ATS, plus ranges written in the job description (e.g. "$120,000 - $150,000") when the ATS fields are empty. salarySource tells you which. salaryMin, salaryMax, salaryCurrency and salaryPeriod keep the published amounts, currency and period; the annualised and USD fields below make them comparable.
  • Comparable salaries: salaryAnnualMin and salaryAnnualMax give the pay per year in the published currency (hourly x 2,080, daily x 260, weekly x 52, monthly x 12, yearly x 1). salaryAnnualUsdMin and salaryAnnualUsdMax convert them to US dollars with a static exchange-rate table of 2026-10-29 (European Central Bank reference rates, 30 currencies). These conversions are approximate: rates move every day. Annual amounts are null when the period is not published; USD amounts are null for currencies outside the table.
  • Minimum annual salary in your currency: Minimum annual salary compares each job's highest annualised salary, converted to Minimum salary currency (default USD), so a $60/hour contract passes a 100,000 USD filter and a 100,000 EUR job is compared with its dollar value. A salary without a published period is compared as published, as a yearly amount.
  • Skills and seniority, free: every job gets detectedSkills (about 180 tools, languages and certifications such as Python, Kubernetes, Salesforce, SAP or IFRS, matched as whole words in the title and description) and a seniority level read from the title (intern, junior, mid, senior, staff, principal, lead, manager, director, executive). No AI call and no extra request.
  • Visa sponsorship, experience and recruiting contact, free: visaSponsorship is true when the description offers sponsorship ("visa sponsorship available", "we sponsor H-1B"), false when it rules it out ("unable to sponsor", "without sponsorship", "requiring sponsorship will not be considered") and null when it says nothing. experienceYearsMin reads "5+ years of experience", "at least 3 years" or "3-5 years" (3), ignoring company-age phrases such as "founded 20 years ago". contactEmails lists only generic recruiting mailboxes written in the description (jobs@, careers@, recruiting@, talent@, hr@, rh@, recrutement@, hiring@...); addresses of individual people are never extracted. All three are read from the description with rules (English, plus some French and German for experience), with no AI call. They need descriptions: with Include job description off, they are only filled when a description filter (keywords, required skills, visa sponsorship, experience) downloads them.
  • Company hiring summaries: set Output to "Company summaries" (or "Both") to get one row per company with totalOpenJobs, departmentBreakdown, locationBreakdown, seniorityBreakdown, topSkills, remoteShare and, with change tracking, newJobsCount and closedJobsCount. Summaries count every job that matches your filters, regardless of Max jobs per company. In "Company summaries" mode, SmartRecruiters and Workday descriptions are not downloaded job by job, so their topSkills come from job titles only.
  • Sort order before the cap: Sort jobs by orders each company's matching jobs before Max jobs per company keeps the first ones, so the jobs whose details are downloaded and saved are the ones you asked for. "Newest first" (default) sorts by publishedAt, then updatedAt, jobs without a date last. BambooHR and Rippling job lists have no dates, so with "Newest first" their jobs keep the career site order, unless a salary or publication-age filter downloads their details first. "Highest salary first" sorts by salaryAnnualUsdMax (else salaryAnnualUsdMin), jobs without a USD salary last; SmartRecruiters, Workday, BambooHR and Rippling often publish salaries only in the job details, which are downloaded after the sort, so their jobs mostly keep the career site order. "Career site order" keeps the order of the source.
  • Markdown report: every run also stores a REPORT record (Markdown) in the key-value store: one table of the companies found (company, ATS, domain, LinkedIn, open jobs, matching jobs, top 5 skills, remote share, USD salary range), the new jobs since the previous run when change tracking is on (up to 50, with links), and the companies not found with how to fix them. Paste it into a doc, a ticket or a chat message.
  • Description keywords: keep only jobs whose description mentions at least one of your keywords, e.g. "Kubernetes" or "HubSpot". Whole words, case-insensitive.
  • Seniority, skills, visa and experience filters: these run on the fields above, before any job is saved.
    • Seniority levels keeps the selected levels. A title without a level ("Software Engineer") has an unknown seniority and is dropped unless you also select Unknown; select it when you look for mid-level roles, which often have no qualifier.
    • Required skills keeps jobs whose detectedSkills include any of your skills (default) or all of them (Required skills match). Names are matched case-insensitively against the skill dictionary, and common aliases work ("golang" for Go, "k8s" for Kubernetes, "postgres" for PostgreSQL). Unknown names are ignored with a warning; if none is known, the run stops with an error rather than returning unfiltered jobs. Use Description keywords for free text.
    • Only jobs offering visa sponsorship keeps jobs whose visaSponsorship is true; jobs that do not mention it are dropped.
    • Maximum years of experience required keeps jobs whose experienceYearsMin is at most your value, and jobs whose description states no requirement (null), since most descriptions do not.
    • Skills, visa and experience filters download descriptions (job by job on SmartRecruiters, Workday, BambooHR and Rippling, only for jobs that pass the other filters), even with Include job description off.
  • Company domain for enrichment: companyDomain holds the company website domain and companyDomainSource says where it comes from: input (a website you entered), ats (a company URL published by the ATS) or resolved (the homepage found from a plain name and checked against it, as described above). The domain kept is the one tried when the site only redirects to a country version (philips.com), and the landing domain otherwise (datadoghq.com). It is null when none is found.
  • Company LinkedIn page and description, free: when the Actor downloads the company homepage or careers page to find the job board (website input, or a website found from a name), it also reads companyLinkedinUrl (a linkedin.com/company/... link on the page; personal /in/ profiles are never taken) and companyDescription (the page's meta description, else og:description, trimmed to 300 characters). No extra request: both are null for job-board URL inputs, and when the page has none. Monitors remember them with the resolved board.
  • Hiring changes: identify new, updated, unchanged and closed roles between scheduled runs. Updated jobs list their changedFields (title, location, salary, description...). Capped changes stay pending for a later run. jobKey is a stable identifier to deduplicate across runs.

What data do you get?

FieldsMeaning
title, url, applyUrlRole and application links
companyName, companySlug, atsCompany and source
companyDomain, companyDomainSourceCompany website domain, and whether it came from input, ATS or resolution
companyLinkedinUrl, companyDescriptionLinkedIn company page and meta description from the company website
recordTypejob, or companySummary for company summary rows
seniorityLevel read from the title, or null
visaSponsorshipSponsorship offered (true), ruled out (false) or not stated (null)
experienceYearsMinMinimum years of experience required, or null
contactEmailsGeneric recruiting mailboxes from the description (never personal ones)
detectedSkillsTools and skills named in the title or description
location, locations, countryPublished locations
workplaceType, isRemoteSource work arrangement or a remote keyword signal
employmentType, employmentCategoryContract type as published, and normalized
salaryMin, salaryMax, salaryCurrency, salaryPeriodPublished pay, with unknown parts left null
salaryText, salarySourceSalary as published, and whether it comes from the ATS or the description
salaryAnnualMin, salaryAnnualMaxSalary per year in the published currency
salaryAnnualUsdMin, salaryAnnualUsdMaxAnnual salary in USD, static FX table of 2026-10-29 (approximate)
publishedAt, updatedAtDates supplied by the ATS (approximate for Workday jobs without details)
descriptionText, descriptionHtmlOptional full description
changeStatus, changedFieldsChange relative to the saved monitoring snapshot, and what changed
jobKeyStable identifier: ats:company:jobId

Company summary rows (output modes "Company summaries" and "Both") have recordType: "companySummary", companyName, companyInput, ats, companySlug, companyDomain, companyDomainSource, companyLinkedinUrl, companyDescription, totalOpenJobs, departmentBreakdown, locationBreakdown, seniorityBreakdown, topSkills, remoteShare (0 to 1), newJobsCount and closedJobsCount (null without change tracking; on a company's first monitoring run every job counts as new). The dataset's Company summaries view shows them; in "Both" mode, filter on recordType to separate them from jobs.

[
{
"recordType": "companySummary",
"companyName": "Example Company",
"ats": "workday",
"companySlug": "examplecompany/External",
"companyDomain": "example.com",
"companyLinkedinUrl": "https://www.linkedin.com/company/example-company",
"companyDescription": "Example Company builds payment tools for online businesses.",
"totalOpenJobs": 42,
"departmentBreakdown": { "Unknown": 42 },
"locationBreakdown": { "Paris, France": 15, "Remote": 10, "Other": 17 },
"remoteShare": 0.24,
"topSkills": { "Python": 12, "AWS": 8 },
"seniorityBreakdown": { "senior": 14, "unspecified": 20, "junior": 8 },
"newJobsCount": null,
"closedJobsCount": null
}
]

Monitor new and closed jobs

Enable Track changes between runs and Only output changes, then save a task and schedule it. Give each independent monitor a different Snapshot store name. The first run reports current matching jobs as new; later runs compare with the saved state. A closed job means it disappeared from the fetched board.

{
"companies": ["Stripe"],
"maxJobsPerCompany": 20,
"monitorChanges": true,
"onlyChanges": true,
"stateStoreName": "my-hiring-monitor"
}

Slack, Discord and Telegram alerts

Add a Notification webhook URL to a monitor and each run that finds changes posts a short summary with links to the jobs:

  • Slack: an incoming webhook URL (https://hooks.slack.com/services/...).
  • Discord: a channel webhook URL (https://discord.com/api/webhooks/...). Messages are kept under Discord's 2,000-character limit.
  • Telegram: https://api.telegram.org/bot<token>/sendMessage?chat_id=<chat id>.
  • Anything else (n8n, Make, Zapier, your API) receives JSON: event, text, counts, datasetUrl and up to 100 jobs with changeStatus, title, companyName, location and url.

The URL is stored encrypted and never written to the log. A company's first monitoring run sets the baseline and sends nothing. A failed delivery is logged as a warning and never fails the run.

Every run remembers which job board each company name or website resolved to, so later runs on the same companies skip detection (about 30 requests per company). Monitors keep this in their state store; other runs keep it in a small key-value store named ats-jobs-scraper-cache in your account. A board that disappears or fails is detected again (without change tracking, so is a board left with no jobs), and name and website matches are re-checked every 30 days; resolvedFromCache in the run summary shows which companies used it. Job board URLs need no detection and are not cached. To force a fresh detection, delete the ats-jobs-scraper-cache store or paste the job board URL.

Keep company inputs and filters stable for a monitor. A publication-age filter excludes jobs without a published date; a remote flag does not mean the employer accepts applicants from every country. Consult the location and job description before using a result.

Example output

This is an illustrative excerpt with anonymized companies. Real runs contain the full schema and source values.

[
{
"ats": "greenhouse",
"companyName": "Example Company",
"companySlug": "examplecompany",
"jobId": "1234567",
"title": "Backend Engineer",
"url": "https://job-boards.greenhouse.io/examplecompany/jobs/1234567",
"location": "Remote, France",
"isRemote": true,
"salaryMin": null,
"salaryMax": null,
"salaryCurrency": null,
"salaryPeriod": null,
"descriptionText": "Build and maintain backend services.",
"changeStatus": "new"
},
{
"ats": "lever",
"companyName": "Example Company Two",
"companySlug": "examplecompanytwo",
"jobId": "abcd1234",
"title": "Product Designer",
"url": "https://jobs.lever.co/examplecompanytwo/abcd1234",
"location": "London, United Kingdom",
"workplaceType": "hybrid",
"isRemote": false,
"salaryMin": 60000,
"salaryMax": 80000,
"salaryCurrency": "GBP",
"salaryPeriod": "year",
"salaryAnnualMin": 60000,
"salaryAnnualMax": 80000,
"salaryAnnualUsdMin": 79901,
"salaryAnnualUsdMax": 106535,
"seniority": "senior",
"detectedSkills": ["Figma", "Jira"],
"companyDomain": "example.com",
"companyDomainSource": "resolved",
"changeStatus": "new"
}
]

How much does it cost to scrape company jobs?

$3 per 1,000 jobs delivered ($0.003 per job), every feature included. You pay for the jobs that land in your dataset, plus a fraction of a cent per run. Check the Pricing tab for the price that applies to your run.

Run start: each run also has a small start fee of $0.001 per GB of memory, charged automatically by Apify: $0.004 per run at the default 4 GB. It covers starting the container, so a run that finds nothing costs you less than half a cent.

What happensCharged?
A job is saved to your dataset (new, updated, unchanged or closed)Yes, one job
A job is dropped by your filters (title, location, salary, skills, seniority, visa, age...)No
A company is not found, or its board is entered twiceNo
Company detection, salary normalisation, skills, seniority, visa and experience detectionNo, included
Company summaries saved with the jobs, the Report, the Run summary, Slack/Discord/Telegram alertsNo, included
"Company summaries" output only (no job rows)One job per job counted in each summary

What it costs in practice:

  • 20 jobs from each of 50 target companies: 1,000 jobs = $3.
  • A 700-job board filtered down to the 12 remote senior roles you want: 12 jobs = $0.036.
  • A daily monitor of 100 companies with Only output changes: you pay only for the new, updated and closed jobs of the day.
  • Apify's free plan includes $5 of monthly credit: about 1,600 jobs a month for free.
  • Each run adds the $0.004 start fee: 1,000 jobs in one run = $3.004.

Spending limit: set a maximum cost per run in the run options. The Actor saves the jobs the remaining budget pays for (the first ones in your sort order), then stops cleanly: the run still succeeds, with spendingLimitReached: true and jobsCharged in the Run summary. Max jobs per company caps both the output and the cost per company.

No browser or proxy is required. Company discovery can make several requests, and sources that need individual description requests take longer. Start with two companies and 20 jobs each to see the results; turn off descriptions when you only need hiring signals, it makes runs faster.

The Actor supports the thirteen listed ATS. Breezy HR does not publish descriptions in its public feed, so description keywords drop its jobs, its skills come from the title only, and the visa sponsorship filter drops its jobs. It does not cover Taleo, iCIMS or career sites that load their jobs with JavaScript from their own domain.

Workday specifics. A Workday address combines a tenant, a data center (wd1, wd5...) and a site name that cannot be guessed, so a company name reaches Workday only through the company website found from it, when its careers pages link the Workday site within two clicks. Many large companies put their own career site in front of Workday (Phenom, Radancy, Eightfold...) without linking it: paste the Workday URL then. The Workday job list only says "Posted Today", "Posted 3 Days Ago" or "Posted 30+ Days Ago": without descriptions, publishedAt is that day at midnight UTC, and "30+ days" is dated 30 days ago, so it can be older. With descriptions, the exact posting date replaces it. Contract type, country and extra locations also come with descriptions; filters run on the list data, so a contract-type filter keeps Workday jobs only when "Unknown" is selected, unless descriptions are on. Workday does not publish departments per job, so department filters and breakdowns see them as unknown. Without filters, change tracking or summaries, Max jobs per company stops the download early, with "Newest first" or "Career site order" only: Workday lists jobs newest first, while the highest salaries can be anywhere on the site. Large sites (several thousand jobs) take one request per 20 jobs, plus one per job for descriptions. An ambiguous name, an unsupported source or a blocked endpoint is reported in the summary. A result cap limits saved jobs, not necessarily the number of job records the source API returns.

BambooHR and Rippling specifics. Their job lists have titles, locations, departments and workplace types; descriptions, salaries, publication dates and (Rippling) contract types come from one request per job, made for saved jobs when Include job description is on, and for every job that passes the other filters when a salary or publication-age filter is set. With descriptions off and no such filter, those fields stay null. BambooHR publishes pay as free text (salaryText); a range or single amount with a currency is parsed into numbers.

Troubleshooting

Company not found? Paste its exact supported job-board URL. For Workday, open the company's job search and copy the address that contains myworkdayjobs.com or myworkdaysite.com. Check the detected ATS, selected company and alternatives in the summary.

Missing salary or date? The source may not publish it. Salaries written in descriptions are only read when Include job description is on. Null is intentional and should not be interpreted as zero.

No new results? Check the filters and monitoring state. With changes-only output, a successful run can legitimately return no rows. A run where no company is found, or no job matches, still succeeds: the status message, the Run summary and the Report say which company was not found and why.

Automate with n8n, Make, Zapier, Clay and AI agents (MCP)

Every run can be started from another tool, and its results read back as JSON, CSV or Excel. The Actor ID is dgn-lab/ats-jobs-scraper (dgn-lab~ats-jobs-scraper in API URLs). Get an API token in Apify Console > Settings > API & Integrations and keep it in your tool's credential store: never paste it into shared workflows, public files or prompts.

Apify API: run and get the results in one request

curl -X POST "https://api.apify.com/v2/actors/dgn-lab~ats-jobs-scraper/run-sync-get-dataset-items?token=<YOUR_APIFY_TOKEN>&format=json&maxTotalChargeUsd=5" \
-H "Content-Type: application/json" \
-d '{"companies": ["Stripe", "https://jobs.lever.co/spotify"], "maxJobsPerCompany": 20}'

The request body is the Actor input (same fields as the Input tab) and the response is the dataset items. Use format=csv or format=xlsx for a file, and maxTotalChargeUsd to cap what one run can cost. This synchronous endpoint answers with HTTP 408 when the run lasts more than 300 seconds. Company discovery and job-by-job descriptions (Workday, SmartRecruiters, BambooHR, Rippling) take longer, so keep synchronous calls to a few companies, or turn off Include job description. For longer runs, start the run with POST https://api.apify.com/v2/actors/dgn-lab~ats-jobs-scraper/runs and read GET https://api.apify.com/v2/datasets/<defaultDatasetId>/items once it has finished, or let a webhook tell you (below). The API tab shows the same calls in JavaScript, Python and cURL.

n8n

  1. Install the official Apify node: on n8n Cloud, search Apify in the nodes panel; self-hosted, go to Settings > Community Nodes and install @apify/n8n-nodes-apify.
  2. Add the node with the operation Run an Actor and get dataset, select dgn-lab/ats-jobs-scraper and paste your input JSON in Custom input.
  3. Send the rows to Google Sheets, your ATS or CRM, or a Slack channel. For hiring signals, set "outputMode": "companySummary" to get one row per company.

To process scheduled runs instead, start the workflow with the On new Apify Event trigger on this Actor (or on your saved task). In an AI Agent workflow, add the Apify Tool so the agent can run the Actor itself. Guide: Apify n8n integration.

Make

Add the Apify app, module Run an Actor with Run synchronously set to Yes, then Get Dataset Items with the run's default dataset ID, then for example Google Sheets Bulk Add Rows. Make limits how long a synchronous module can wait; for longer runs use the Watch Actor Runs trigger and start the Actor from a schedule. Guide: Apify Make integration.

Zapier

Zapier waits at most 30 seconds for a synchronous Run Actor action, so for this Actor use the trigger Finished Actor run (or Finished task run for a saved task), then the search Fetch dataset items, then your next app (Google Sheets, a CRM, Slack, Gmail). Start the runs from an Apify schedule or from another Zap with Run Actor (asynchronous). Guide: Apify Zapier integration.

Clay

There is no Apify app inside Clay; use Clay's own HTTP API integration with the synchronous endpoint above (method POST, token in the URL from your Clay secrets). For account enrichment, send one company per row, for example {"companies": ["{{Company domain}}"], "outputMode": "companySummary", "includeDescription": false} with your column inserted in place of {{Company domain}}, then map totalOpenJobs, departmentBreakdown, topSkills, remoteShare and companyLinkedinUrl to new columns.

AI agents and MCP clients

Add the Apify MCP server to Claude, ChatGPT, Cursor, VS Code or any MCP client with this URL, which loads only this Actor as a tool:

https://mcp.apify.com?tools=dgn-lab/ats-jobs-scraper

On the first connection your browser opens to sign in to Apify (OAuth), so no token is written in the client configuration. The agent can then run the Actor and read its dataset (get-dataset-items). With the Apify CLI, apify mcp install <client> writes the configuration for you.

Schedules and webhooks

Save your input as a task, then create a schedule in Apify Console > Schedules with a cron expression and time zone. To push results to your own system, open the Actor's or task's Integrations tab and add a webhook on the ACTOR.RUN.SUCCEEDED event: Apify sends a POST request with the run details, including defaultDatasetId, to your URL. See webhooks.

FAQ, disclaimer and support

Can I scrape Workday jobs from a company name?

Often, but not always. A Workday address cannot be guessed, so a name reaches Workday only when the company website found from it links the Workday site within two clicks. Otherwise paste the Workday URL (it contains myworkdayjobs.com or myworkdaysite.com). See Workday specifics above.

Do I need an ATS account or API key?

No. The Actor reads the public job-board feeds that the career pages themselves use.

Which career sites are not supported?

Taleo, iCIMS and career sites that load their jobs with JavaScript from their own domain. Sites behind bot protection cannot be read either: paste their job-board URL when it is on a supported ATS.

How do I find which ATS a company uses?

Enter the company name or website: the Actor tries the 13 supported ATS, follows the careers links of the website, and returns the board it found with ats, matchConfidence and any alternatives in the Run summary. It is also a quick way to map the ATS used by a list of accounts.

Is there a Greenhouse, Lever or Workday jobs API?

Greenhouse, Lever and Ashby publish public job-board feeds, and Workday career sites load their jobs from an internal endpoint. This Actor reads all of them for you and returns one schema, so you call one API (the Apify API, below) instead of thirteen.

How do I get alerts when a company posts a new job?

Turn on Track changes between runs and Only output changes, add a Slack, Discord or Telegram Notification webhook URL, save the input as a task and schedule it daily. See Monitor new and closed jobs.

How much does it cost to scrape 10,000 jobs?

$30 at $3 per 1,000 jobs delivered, everything included. Jobs your filters drop are free, so a narrow search costs only what it returns.

Our Actors are ethical and only read what companies publish on their public career sites. This Actor saves generic recruiting mailboxes written in job descriptions (jobs@, careers@...) but never personal email addresses or personal LinkedIn profiles. Results can still contain personal data, such as a hiring manager named in a description. Personal data is protected by the GDPR in the European Union and by other regulations around the world. You should not scrape personal data unless you have a legitimate reason to do so. If you're unsure whether your reason is legitimate, consult your lawyers.

Support

Use the API tab for code examples. For a bug or a missing ATS, open the Issues tab with the input and run ID. Remove credentials from anything you share.