Most company careers pages are a thin wrapper around an applicant tracking system. The 'Careers' link on the main site might look custom, but the job listings and apply buttons usually live on a Workday, Greenhouse, Lever or Ashby board, and that board is where the clean, current data is.
Knowing which ATS a company uses matters if you want its jobs in bulk. Each system has its own URL pattern and its own public feed, Workday hides real posting dates behind labels like 'Posted 30+ Days Ago', and job boards that copy the listings can lag or drop fields such as salary.
This guide shows how to identify the ATS from a careers URL, when you can skip that step by using the built-in index, how to feed boards into the actor, and what to do when a company uses a system that is not supported.
Step by step
Open a job from the company's careers page
Go to the company's careers page and click into any single job. The address bar or the apply button usually reveals the real board: adobe.wd5.myworkdayjobs.com/external_experienced, boards.greenhouse.io/gusto, jobs.lever.co/spotify or jobs.ashbyhq.com/amplitude. Copy the URL up to the company's board, not the individual job.
Identify the ATS from the host name
The host is enough to classify the board. The function below maps the nine systems the actor supports, including EU Greenhouse and Lever hosts, and returns None for anything else, such as SmartRecruiters or a fully custom careers site.
from urllib.parse import urlparse ATS_HOSTS = [ ("myworkdayjobs.com", "workday"), ("greenhouse.io", "greenhouse"), ("lever.co", "lever"), ("ashbyhq.com", "ashby"), ("workable.com", "workable"), ("recruitee.com", "recruitee"), ("teamtailor.com", "teamtailor"), ("jobs.personio.de", "personio"), ("jobs.personio.com", "personio"), ("bamboohr.com", "bamboohr"), ] def detect_ats(url): if "://" not in url: url = "https://" + url host = (urlparse(url).hostname or "").lower() for suffix, ats in ATS_HOSTS: if host == suffix or host.endswith("." + suffix): return ats return None for u in [ "https://adobe.wd5.myworkdayjobs.com/external_experienced", "https://job-boards.greenhouse.io/gusto", "jobs.lever.co/spotify", "https://careers.example.com/jobs", ]: print(u, "->", detect_ats(u))Skip the detective work for indexed companies
The actor ships with an index of 811 Greenhouse, Lever and Ashby companies, YC startups plus well-known employers. For those you can simply put the company name or domain into companies and the actor resolves the board itself. Or set searchAllCompanies to true and filter by keywords and locations to find every indexed company hiring for a role, without naming any of them. Workday and the other ATS always need the careers URL; the actor never guesses slugs.
{ "searchAllCompanies": true, "ats": ["ashby"], "keywords": ["data engineer"], "remoteOnly": true, "maxJobs": 300 }Use the ats:slug shortcut when you know the board name
If you already know a company's board slug, you can write it as ats:slug instead of a full URL, for example greenhouse:gusto. This is handy when you keep a list of target companies in a spreadsheet with one column for the ATS and one for the slug.
Pull every open job and confirm the ATS
Run the actor with your list. Each output row carries an ats field, so you can confirm which system each company's jobs came from, and a jobUrl that points to the original posting. Postings from different systems share one schema: title, company, department, team, location, workplace type, salary where published and posted date. Turn on includeDescription if you need the full text; it costs nothing extra.
Keep a watch on new roles
To follow a set of companies over time, schedule the run and set postedWithinDays so each run returns only recently published jobs. The snippet groups a run's results by company and ATS so you can see who is hiring most.
import os from collections import Counter from apify_client import ApifyClient client = ApifyClient(os.environ["APIFY_TOKEN"]) run = client.actor("actor-forge/career-site-jobs").call(run_input={ "companies": [ "greenhouse:gusto", "https://jobs.lever.co/spotify", "https://adobe.wd5.myworkdayjobs.com/external_experienced", ], "postedWithinDays": 7, "maxJobs": 500, }) counts = Counter() for job in client.dataset(run["defaultDatasetId"]).iterate_items(): counts[(job.get("companyName"), job.get("ats"))] += 1 for (company, ats), n in counts.most_common(): print(f"{company} ({ats}): {n} new jobs")Know what to do when the ATS is not supported
If detect_ats returns None, the company uses something the actor cannot read, such as SmartRecruiters or a custom-built careers page. Those companies are skipped and not charged. For them you would need a general web scraper or a job board that aggregates their listings.
Sample input
{
"companies": [
"greenhouse:gusto",
"https://jobs.lever.co/spotify",
"https://jobs.ashbyhq.com/amplitude",
"https://adobe.wd5.myworkdayjobs.com/external_experienced"
],
"keywords": [
"engineer"
],
"maxJobs": 200
}Sample output
Jobs from Workday, Ashby, Greenhouse and Lever boards come back side by side in one schema:
| title | companyName | ats | location | workplaceType | salaryText | postedAt | jobUrl |
|---|---|---|---|---|---|---|---|
| Sr. Manager Risk & Controls | Adobe | workday | San Jose; Seattle | 2026-10-16 | https://adobe.wd5.myworkdayjobs.com/external_experienced/job/San-Jose/… | ||
| Customer Support Developer (Databases) | Airbyte | ashby | United States | remote | $99K - $115K | 2026-10-08 | https://jobs.ashbyhq.com/airbyte/9d81a132-… |
| Benefits Advising (Renewals) Leader | Gusto, Inc. | greenhouse | Denver, CO - Hybrid | hybrid | 2026-07-29 | https://job-boards.greenhouse.io/gusto/jobs/8078212 | |
| Art Director - Mexico | Spotify | lever | Mexico City | hybrid | 2026-10-… | https://jobs.lever.co/spotify/223f9722-… |
Run it from code
cURL
curl -X POST "https://api.apify.com/v2/acts/actor-forge~career-site-jobs/run-sync-get-dataset-items" \
-H "Authorization: Bearer $APIFY_TOKEN" \
-H "Content-Type: application/json" \
-d '{"companies": ["greenhouse:gusto", "https://jobs.lever.co/spotify", "https://jobs.ashbyhq.com/amplitude", "https://adobe.wd5.myworkdayjobs.com/external_experienced"], "keywords": ["engineer"], "maxJobs": 200}'JavaScript (Node.js)
// Node.js 18+ (built-in fetch). Set APIFY_TOKEN in your environment.
const input = {
"companies": [
"greenhouse:gusto",
"https://jobs.lever.co/spotify",
"https://jobs.ashbyhq.com/amplitude",
"https://adobe.wd5.myworkdayjobs.com/external_experienced"
],
"keywords": [
"engineer"
],
"maxJobs": 200
};
const res = await fetch(
"https://api.apify.com/v2/acts/actor-forge~career-site-jobs/run-sync-get-dataset-items",
{
method: "POST",
headers: {
Authorization: `Bearer ${process.env.APIFY_TOKEN}`,
"Content-Type": "application/json",
},
body: JSON.stringify(input),
}
);
const items = await res.json();
console.log(items.length, items[0]);Python
# pip install apify-client (set APIFY_TOKEN in your environment)
import os
from apify_client import ApifyClient
client = ApifyClient(os.environ["APIFY_TOKEN"])
run_input = {
"companies": [
"greenhouse:gusto",
"https://jobs.lever.co/spotify",
"https://jobs.ashbyhq.com/amplitude",
"https://adobe.wd5.myworkdayjobs.com/external_experienced"
],
"keywords": [
"engineer"
],
"maxJobs": 200
}
run = client.actor("actor-forge/career-site-jobs").call(run_input=run_input)
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
print(item)The run-sync-get-dataset-items endpoint waits for the run (up to 5 minutes) and returns the dataset rows directly. For longer runs, start the run asynchronously and read the dataset when it finishes, as the Python example does. Replace APIFY_TOKEN with your own token from the Apify console.
What it costs
Pulling 200 engineering jobs from four company boards costs 200 x $0.003 = $0.60. A full index scan for remote data roles of about 400 jobs comes to roughly $1.20.
| Charged for | When | Price | Per 1,000 | Worked example |
|---|---|---|---|---|
| Job result | Charged per job posting delivered (description included at no extra cost) | $0.003 | $3 | 100 jobs = $0.30; 1,000 jobs = $3; a full index scan for remote data roles (~400 jobs) ≈ $1.20 |
| Actor Start (platform start fee) | Charged when the Actor starts running. Number of events charged depends on Actor memory (one event per GB, minimum one event). | $0.00005 | — | One run at 1 GB = $0.00005 |
Ready to try it? Career Site Jobs — Workday, Greenhouse, Lever & Ashby Scraper runs on Apify, pay per result.
Run on Apify → Read the full actor pageTips and pitfalls
- Copy the board URL, not a single job URL; the board is what lists every opening.
- Workday searches stop at 2,000 results, but larger sites are split by job family or location automatically, so you do not need to do it yourself.
- Workday makes one detail request per job, so a 300-job site takes about a minute. Big Workday employers make for longer runs than Greenhouse or Lever boards.
- Keyword and location filters are case-insensitive substring matches, so 'engineer' also matches 'Engineering Manager'. Use excludeKeywords to drop titles like 'intern'.
- A Workday site whose robots.txt disallows its job pages is skipped and not charged.
FAQ
How can I tell if a company uses Workday?
Its job links point to a host ending in myworkdayjobs.com, such as adobe.wd5.myworkdayjobs.com. Pass that board URL to the actor to read the jobs with real posting dates.
Can the actor find a company's ATS from its domain alone?
Only for companies in the built-in index of 811 Greenhouse, Lever and Ashby employers. For anyone else, find the board URL on the careers page and pass it in; the actor does not guess company slugs.
Why scrape the ATS instead of a job board?
The ATS is the original source, so listings are fresher, include the employer's own departments and teams, and carry salary ranges where the employer publishes them in structured form.
Does it work for companies on SmartRecruiters?
No. SmartRecruiters and custom-built careers pages are not supported.
Actors used in this guide
Career Site Jobs — Workday, Greenhouse, Lever & Ashby Scraper
Scrape jobs straight from company career sites on Workday, Greenhouse, Lever, Ashby, Workable, Recruitee, Teamtailor, Personio and BambooHR. Search…
$3 per 1,000 · job resultRelated guides
How to Get SEC Form D Startup Funding Data as a Daily Feed
Use Startup Funding Rounds to read EDGAR's daily Form D indexes and parse each filing into a row with issuer, amounts, industry and exemption.…
How to Get Local Business Leads Without Scraping Google Maps
Query the open Overture Maps places dataset by city and category with Business Places Data to get names, addresses, phones, websites and emails for…