ATS Jobs API — 1.3M Jobs from 24,000+ Companies, One Search avatar

ATS Jobs API — 1.3M Jobs from 24,000+ Companies, One Search

Pricing

from $1.50 / 1,000 job listings

Go to Apify Store
ATS Jobs API — 1.3M Jobs from 24,000+ Companies, One Search

ATS Jobs API — 1.3M Jobs from 24,000+ Companies, One Search

One search over 1.3M open jobs from 24,000+ company career sites on Workday, Greenhouse, Lever, Ashby, SmartRecruiters, Workable and 4 more ATS, with full descriptions. No company list needed: the index is rebuilt daily. Or track named companies with only-new alerts. $2 per 1,000 jobs.

Pricing

from $1.50 / 1,000 job listings

Rating

0.0

(0)

Developer

Dmitry Melikhov

Dmitry Melikhov

Maintained by Community

Actor stats

0

Bookmarked

1

Total users

0

Monthly active users

5 days ago

Last modified

Security

Limited permissions

Share

ATS Jobs API — 1.3M Jobs from 23,000+ Companies, One Search

1.3M open jobs from 23,000+ companies in one search. The index of every live career board we know (Workday, Greenhouse, Lever, Ashby, SmartRecruiters, Workable, Recruitee, Teamtailor, Breezy HR, Personio) is rebuilt every night, so you do not need a list of companies or career-site URLs. A search over the whole index takes about 30 seconds.

Search open jobs straight from company applicant-tracking systems. Type a job title and a location and get matching postings from ~20,000 live career boards on Greenhouse, Lever, Ashby, Workday, SmartRecruiters, Workable, Recruitee, Teamtailor, Breezy HR and Personio — or give a list of companies and track every new role they open. No login, no browser, no API keys.

  • Search mode — no company list needed: {"keywords": ["data engineer"], "locations": ["Berlin", "remote"]}. Reads a daily index of every live board, newest jobs first (vendors alternate within each day, so one ATS does not fill the page), with the full description fetched for every job delivered.
  • Company mode — names (Stripe, NVIDIA), ATS boards (greenhouse:gitlab) or websites (notion.so): fetched live, with full descriptions.
  • Only-new monitoring — schedule it and get only postings that appeared since the last run. You pay only for new rows.
  • Filters that save money — title keywords, exclusions, locations, remote-only, posted-within-N-days, ATS platform. Jobs that do not match are never charged.
  • $2 per 1,000 jobs, full descriptions included. No subscription; a run start costs $0.00005.

Found it useful? Leave a ⭐ review — it helps other people find this Actor → https://apify.com/suitable_sunspot/keyless-ats-jobs-api/reviews

Who uses it

You areYou give itYou get
Job seeker / job boardTitle keywords + locationsFresh postings from 20,000+ employers' own career sites, before aggregators pick them up
Sales / SDR team selling to growing companiesTarget-account list + a daily schedule"Acme opened 3 Data Engineer jobs" — a buying signal, every morning
Recruiting agencyClient and prospect companiesEvery new opening with a direct apply URL
Investor / analystWatch-list companiesHiring velocity by company, department and location
AI agent (MCP / API)keywords or companiesStructured jobs in one call

Quick start

Search every board (no company list):

{
"keywords": ["machine learning", "data engineer"],
"excludeKeywords": ["intern"],
"locations": ["London", "remote"],
"postedWithinDays": 7,
"maxItems": 100
}

Track specific companies — names, ATS boards or websites:

{
"companies": ["Stripe", "notion", "greenhouse:gitlab", "https://nvidia.wd5.myworkdayjobs.com/NVIDIAExternalCareerSite"],
"keywords": ["engineer"]
}

Company names are looked up in a directory of ~35,000 public ATS boards; anything not found there is treated as a website and its ATS is detected automatically.

Monitoring mode — schedule it daily:

{
"companies": ["Stripe", "Notion", "GitLab"],
"onlyNewJobs": true,
"stateKey": "sales-targets"
}

The first run returns all current jobs (your baseline). Every next run returns only postings that appeared since — and you pay only for those rows. Works in search mode too: a saved search for "rust engineer, remote" becomes a daily alert.

Output — one row per job

{
"vendor": "greenhouse",
"company": "GitLab",
"title": "Senior Backend Engineer",
"url": "https://job-boards.greenhouse.io/gitlab/jobs/8860302002",
"location": "Remote, US",
"department": "Engineering",
"employmentType": null,
"postedAt": "2026-10-01T00:00:00-04:00",
"remote": null,
"jobId": "8860302002",
"description": "…",
"salaryRaw": null,
"salaryMin": null,
"salaryMax": null,
"salaryCurrency": null,
"salaryPeriod": null,
"sourceUrl": "https://boards-api.greenhouse.io/v1/boards/gitlab/jobs?content=true",
"fetchedAt": "2026-10-16T12:00:00+00:00",
"statusCode": 200
}

Same schema for all ten ATS vendors. null means the vendor does not publish that field. The five salary* fields are null unless the vendor exposes a salary; Breezy HR publishes one and it is parsed into salaryMin/salaryMax (numbers), salaryCurrency (ISO code) and salaryPeriod (year/month/hour/day), with the raw text preserved in salaryRaw.

Integrations

New jobs every day → Google Sheets + Slack (n8n). Schedule the Actor with onlyNewJobs: true and one stable stateKey per watch-list: the first run is your baseline, every later run returns only the postings that appeared since — and you are not billed again for rows you already received. In n8n, chain Schedule Trigger → HTTP Request → Google Sheets (Append rows) → Slack, using the request below. Zero new jobs returns zero rows, so nothing is appended or posted.

  • Method: POST
  • URL: https://api.apify.com/v2/acts/suitable_sunspot~keyless-ats-jobs-api/run-sync-get-dataset-items?timeout=180&maxTotalChargeUsd=0.1
  • Header: Authorization: Bearer <APIFY_TOKEN>
  • Body:
{
"companyWebsites": ["stripe.com", "notion.so", "gitlab.com"],
"onlyNewJobs": true,
"stateKey": "sales-targets",
"maxItems": 200,
"maxJobsPerCompany": 50,
"includeDescriptions": false
}

Prefer the native node? Install @apify/n8n-nodes-apify and use Run actor and get dataset with the same body. Sheet headers: detected_at, company, title, location, department, employmentType, postedAt, vendor, salaryMin, salaryMax, salaryCurrency, url, jobId.

Python (pip install apify-client):

import os
from apify_client import ApifyClient
client = ApifyClient(os.environ["APIFY_TOKEN"])
run = client.actor("suitable_sunspot~keyless-ats-jobs-api").call(
run_input={
"companyWebsites": ["stripe.com", "notion.so", "gitlab.com"],
"onlyNewJobs": True,
"stateKey": "sales-targets",
"maxItems": 200,
"includeDescriptions": False,
},
max_total_charge_usd=0.10, # cost ceiling for this run
)
for job in client.dataset(run["defaultDatasetId"]).iterate_items():
print(job["company"], "|", job["title"], "|", job["url"])

JavaScript (Node 18+):

const res = await fetch(
"https://api.apify.com/v2/acts/suitable_sunspot~keyless-ats-jobs-api/run-sync-get-dataset-items?timeout=180&maxTotalChargeUsd=0.1",
{
method: "POST",
headers: { Authorization: `Bearer ${process.env.APIFY_TOKEN}`, "Content-Type": "application/json" },
body: JSON.stringify({
companyWebsites: ["stripe.com", "notion.so", "gitlab.com"],
onlyNewJobs: true,
stateKey: "sales-targets",
maxItems: 200,
includeDescriptions: false,
}),
},
);
const jobs = await res.json();
console.log(`${jobs.length} new jobs`);

curl:

curl -sS -X POST \
-H "Authorization: Bearer $APIFY_TOKEN" \
-H "Content-Type: application/json" \
-d '{"companyWebsites":["stripe.com","notion.so","gitlab.com"],"onlyNewJobs":true,"stateKey":"sales-targets","maxItems":200,"includeDescriptions":false}' \
"https://api.apify.com/v2/acts/suitable_sunspot~keyless-ats-jobs-api/run-sync-get-dataset-items?timeout=180&maxTotalChargeUsd=0.1"

Pricing

$2.00 per 1,000 jobs delivered (event job-listing; volume discounts on higher Apify plans). You are never charged for jobs removed by your filters, errors, retries, companies where no ATS was found, jobs you already received in monitoring mode, or the coverage report. Apify platform usage is included.

Example: if your watch-list produces 300 new postings in a week, that week costs $0.60.

Coverage report (key-value store COVERAGE)

One entry per company, so you always know what you did and did not get:

{ "vendor": "greenhouse", "company": "stripe.com", "handle": "stripe",
"jobsFound": 200, "jobsExpected": 731, "coverageStatus": "capped",
"gapReason": "stopped at maxJobsPerCompany=200 (731 reported)",
"newJobsDelivered": 12, "detectedFrom": "stripe.com: probed greenhouse API with handle 'stripe'" }

coverageStatus: complete | partial | capped | failed | not_attempted | not_detected.

How ATS detection works

  1. If you paste an ATS URL (jobs.lever.co/acme, acme.wd5.myworkdayjobs.com/External) it is parsed directly.
  2. Otherwise the Actor reads the company site, /careers and /jobs and looks for embedded ATS links or widgets.
  3. As a last resort it tries the domain name as a Greenhouse, Lever or Ashby handle. A match found this way is marked "probed … with handle" in detectedFrom — check it for companies with generic names. For exact control, pass companies with {vendor, handle} instead.

Limits

  • Search mode reads an index rebuilt every day, so a job posted a few hours ago may appear in company mode before it reaches search. Search mode fetches descriptions only for the jobs it delivers; set includeDescriptions: false for a faster, title-only search.
  • The board directory comes from public sources (Common Crawl board tokens via open-ats-feed and ats-scrapers, both MIT) and is re-probed daily; a company missing from it can still be reached by website or ATS URL.
  • Staff-only boards (internal careers, redeployment) are left out of search results, and career-site labels are stripped from employer names (Cba (CommBank Careers) → CBA).
  • Only public career boards are read. Companies using other ATS (iCIMS, Taleo, SuccessFactors…) are reported as not_detected and cost nothing.
  • Workday reports at most what its public career site shows; descriptions cost one extra request per job.
  • Descriptions are fetched for every vendor (Workable includes them inline, SmartRecruiters and Breezy cost one extra request per job). Set includeDescriptions: false to skip them for speed.
  • Salary is only reported where the ATS publishes it — Breezy HR does; Greenhouse, Lever, Ashby, Workday, Workable and SmartRecruiters do not in their public feeds, so salary* is null there.
  • Monitoring memory lives in a named key-value store ats-jobs-state-<stateKey> in your own account; delete it to reset.

Exact sources (advanced)

VendorInputPublic endpointReports a total?
Greenhouse{vendor:"greenhouse", handle}boards-api.greenhouse.io/v1/boards/{handle}/jobsyes
Lever{vendor:"lever", handle}api.lever.co/v0/postings/{handle}no
Ashby{vendor:"ashby", handle}api.ashbyhq.com/posting-api/job-board/{handle}no
Workday{vendor:"workday", handle, host, site}POST {host}/wday/cxs/{tenant}/{site}/jobsyes
Personio{vendor:"personio", handle}{handle}.jobs.personio.de/xmlyes
Recruitee{vendor:"recruitee", handle}{handle}.recruitee.com/api/offers/no
Teamtailor{vendor:"teamtailor", handle}{handle}.teamtailor.com/jobs.jsonno
Workable{vendor:"workable", handle}POST apply.workable.com/api/v3/accounts/{handle}/jobsyes
SmartRecruiters{vendor:"smartrecruiters", handle}api.smartrecruiters.com/v1/companies/{handle}/postingsyes
Breezy HR{vendor:"breezy", handle}{handle}.breezy.hr/jsonno

Feedback

Missing an ATS or a field you need? Open an issue on the Actor page.

Run tests

$.venv/bin/python -m pytest tests/ -q