Most company career sites run on a handful of applicant tracking systems (ATS). Almost every one exposes a public, keyless JSON/XML/GraphQL feed behind the pretty careers page — if you know the URL. This is a developer's reference to those endpoints, plus a ready-to-run Devil Scrapes Apify Actor for each one when you'd rather not maintain the plumbing (proxies, retries, anti-blocking, schema normalization).
Maintained by Devil Scrapes — apify.com/DevilScrapes. PRs welcome for ATS platforms we've missed.
ATS platforms render career pages client-side from an internal API. That API is usually unauthenticated (the jobs are public, after all). Hitting it directly gives you clean structured data — no HTML parsing — but you still have to handle rate limits, IP blocks (several 403 datacenter IPs and need residential proxies), and the fact that every platform's schema is different. The Actors below normalize all of it.
| ATS | Public endpoint (per company) | Format | Ready-made Actor |
|---|---|---|---|
| BambooHR | https://{company}.bamboohr.com/careers/list |
JSON | bamboohr-jobs-scraper |
| Greenhouse | https://boards-api.greenhouse.io/v1/boards/{company}/jobs |
JSON | multi-ats-jobs-scraper |
| Lever | https://api.lever.co/v0/postings/{company} |
JSON | multi-ats-jobs-scraper |
| Ashby | POST https://jobs.ashbyhq.com/api/non-user-graphql |
GraphQL | ashby-jobs-scraper |
| Workday | https://{host}/wday/cxs/{tenant}/{site}/jobs |
JSON | workday-jobs-scraper |
| SmartRecruiters | https://api.smartrecruiters.com/v1/companies/{company}/postings |
JSON | smartrecruiters-jobs-scraper |
| Workable | https://apply.workable.com/api/v1/widget/accounts/{subdomain}?details=true |
JSON | workable-jobs-scraper |
| Teamtailor | https://{company}.teamtailor.com/jobs.json |
JSON | teamtailor-jobs-scraper |
| Recruitee | https://{company}.recruitee.com/api/offers/ |
JSON | recruitee-jobs-scraper |
| Personio | https://{company}.jobs.personio.de/xml |
XML | personio-jobs-scraper |
| Breezy HR | https://{company}.breezy.hr/json |
JSON | breezy-hr-jobs-scraper |
- Datacenter IPs get 403'd on several platforms (Breezy, and most EU marketplaces). Route through residential proxies — the Actors default to this where needed.
- Wrong company slug → 302/307 redirect to the platform's marketing site. Detect
it (
allow_redirects=False) instead of following it and mis-parsing. - Schemas drift:
locationmay be one field, an array, or nested. Normalize once. - No server-side pagination on most of these — you get the whole board in one call.
from apify_client import ApifyClient
client = ApifyClient("YOUR_APIFY_API_TOKEN")
run = client.actor("DevilScrapes/bamboohr-jobs-scraper").call(
run_input={"companies": ["adobe"]}
)
for job in client.dataset(run["defaultDatasetId"]).iterate_items():
print(job["job_title"], "—", job["location_city"])Swap the slug for any Actor in the table. Every one returns a normalized row (title, location, url, department, date posted, full description) as JSON or CSV.
See the full Apify Web Scraping Cookbook for runnable examples across jobs, marketplaces, B2B leads, and events — all powered by Devil Scrapes Actors on the Apify Store.
MIT.