Job Search API - Greenhouse, Lever, Ashby & Workday Jobs Index avatar

Job Search API - Greenhouse, Lever, Ashby & Workday Jobs Index

Pricing

Pay per event

Go to Apify Store
Job Search API - Greenhouse, Lever, Ashby & Workday Jobs Index

Job Search API - Greenhouse, Lever, Ashby & Workday Jobs Index

Search 1.4M+ live jobs from 54,000+ company career boards (Greenhouse, Lever, Ashby, Workday, Workable, Personio and 10 more ATSs) in one query, refreshed daily. Posted vs first-seen dates, reposts, duplicates, hiring companies. $1.50/1,000 jobs.

Pricing

Pay per event

Rating

0.0

(0)

Developer

Yukai Lin

Yukai Lin

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

4 days ago

Last modified

Share

What does Job Search API do?

It searches one index of live job postings from company career boards — Greenhouse, Lever, Ashby, Workday, Workable, Recruitee, Personio, Teamtailor, BambooHR and 7 more applicant tracking systems — and returns the matching jobs in seconds. No company list needed: ask for "data engineer, remote, posted in the last 7 days" and get every matching job across all indexed employers.

Input: job title keywords plus any filters (location, country, remote, company, ATS, posted date, experience level, minimum salary). Output: one row per matching job (title, company, location, salary where published, dates, job and apply URL), or one row per hiring company. Price: $1.50 per 1,000 jobs ($0.0015 each, $1.35 on Silver, $1.20 on Gold), $3.00 per 1,000 hiring companies, plus a $0.0005 start fee per run; a search with no match, or a mistyped input, costs only the start fee.

Our crawler reads every indexed board once a day through each ATS's official public job-board endpoint (the feeds these vendors publish so companies can show jobs on their own sites). The Actor itself only queries the index, so runs are fast and cheap.

  • 🔎 Search 1,400,000+ live jobs from 54,000+ career boards of 16 applicant tracking systems in one query
  • 🗓️ Refreshed daily, with two separate clocks: sourcePostedAt (the date the employer's ATS gives) and firstSeenAt (when our crawl first saw the job), plus lastSeenAt and removedAt (when the job was taken down) — good for new-job alerts and hiring-trend research
  • ♻️ Reposts and duplicates flagged: a job re-posted under a new URL, a job that came back, a posting date moved forward (repostKind), and the same opening listed under several URLs (duplicateKind; the extra copies are left out by default, see skipDuplicates)
  • 🏢 Hiring companies output: the same filters, one row per company (deduplicated by company website) with the website, matching jobs, sample titles and locations, optionally with public contacts
  • 🎯 Filters: title keywords (any of several phrases, word* for word starts), excluded words, companies, location text, ISO countries, remote only, ATS, employment type, posted within N days, new in the index within N days, experience level (internship, entry, mid, senior, lead, executive) and minimum salary (filtered-out jobs are free)
  • 🧹 One clean format: title, company, location, country, remote, workplace type, employment type, experience level (seniority), department, salary range (when the ATS publishes one, also as a yearly amount), posting date, job URL and apply URL
  • 🔗 Links, not copies: each row links to the original posting; full descriptions stay on the employer's site (a short snippet is included where the ATS provides plain text)
  • 💵 $1.50 per 1,000 jobs returned, plus a $0.0005 start fee per run (default 256 MB memory = one start event)

Need live jobs of specific companies (17 ATSs, auto-detected from a website)? Use our Career Page Jobs Scraper instead: it reads the boards at the moment of the run.

How to use it

  1. Type the job titles you want into Job title keywords (data engineer, machine learning), or leave it empty and filter by company, location or country only.
  2. Narrow it down: Remote jobs only, Countries (ISO codes), Posted within (days), Job boards (ATS), Employment types, Experience level, Minimum salary.
  3. Choose Output: one row per job (default) or one row per hiring company.
  4. Set Max results to cap the cost, click Start, then export the table as CSV, Excel or JSON, or call the Actor from the API.

The form is prefilled with two title keywords and 100 results (about $0.15, done in seconds).

Input

FieldWhat it does
keywordsJobs whose title contains any of these phrases (all words, in order). engineer* matches Engineer and Engineering. Only letters and digits count: C# and C++ are searched as the word "c", so write C# developer (the run adds a keywordNotes warning to SUMMARY for a one-letter keyword)
excludeKeywordsSkip titles containing any of these phrases
companiesCompany name contains any of these phrases
locationsLocation text contains any of these phrases (city, state, country, "Remote"). It is plain text matching: "Berlin" also finds Berlin, NJ and Berlin, VT; add countries (DE) to stay in one country
countriesTwo-letter ISO codes (US, GB, DE), read from the location
remoteOnlyOnly remote jobs
postedWithinDaysPosted in the last N days (jobs without a posting date are left out)
newWithinDaysFirst seen by our daily crawl in the last N days (for alerts: run daily with 1)
companyDomainsOnly companies with these websites (stripe.com)
atsgreenhouse, lever, ashby, workday, workable, recruitee, personio, breezy, teamtailor, bamboohr, pinpoint, jazzhr, homerun, rippling, gem, freshteam
employmentTypesfull-time, part-time, contract, temporary, internship, apprenticeship, freelance, volunteer
seniorityExperience levels to keep: internship, entry, mid, senior, lead, executive (see "Experience level and salary")
includeUnknownSeniorityWith seniority: also keep jobs whose title names no level
minSalary, minSalaryCurrency, minSalaryPeriodKeep jobs whose stated salary reaches this amount (default currency USD, period year)
salaryMatchoverlap (default: top of the range reaches the minimum) or guaranteed (bottom of the range does)
convertSalaryCurrencyCompare salaries in other currencies with approximate fixed rates (default on)
keepJobsWithoutSalaryWith minSalary: keep jobs that state no salary (default on)
includeRemovedAlso return jobs that were taken down
sortposted (newest posting first) or firstSeen
skipDuplicatesLeave out extra copies of one opening posted under another URL (default on; set false to keep them, flagged isDuplicate). A posting URL is never returned twice in one run
repostsOnlyOnly jobs we saw reposted
outputjobs (default) or companies (one row per hiring company)
minJobsPerCompany, companySort, onlyWithWebsiteHiring companies: at least N matching jobs, order, only companies with a known website
enrichCompaniesHiring companies: contacts (our Website Contact Extractor) or company (Company Website Enrichment), run once per company website
maxResultsStop after this many jobs or companies (default 100, up to 100,000)
{
"keywords": ["data engineer", "analytics engineer"],
"remoteOnly": true,
"postedWithinDays": 7,
"maxResults": 500
}

Output

One row per job (real example from the index, 2 October 2026):

{
"title": "Data Engineer, Data Quality & Provenance",
"company": "Wayve",
"companyDomain": "wayve.ai",
"companyWebsite": "https://wayve.ai/",
"location": "Leonberg, Germany",
"country": "DE",
"remote": false,
"workplaceType": null,
"employmentType": "full-time",
"seniority": null,
"senioritySource": null,
"department": "Simulation, Evaluation, Validation / Simulation, Evaluation, Validation",
"salaryMin": null,
"salaryMax": null,
"salaryCurrency": null,
"salaryPeriod": null,
"salaryAnnualMin": null,
"salaryAnnualMax": null,
"url": "https://jobs.ashbyhq.com/wayve/102a31c7-44a5-48bc-8c6e-1f4434e83974",
"applyUrl": "https://jobs.ashbyhq.com/wayve/102a31c7-44a5-48bc-8c6e-1f4434e83974/application",
"sourcePostedAt": "2026-10-02T15:25:57.680Z",
"sourcePostedAtApproximate": false,
"originalPostedAt": "2026-10-02T15:25:57.680Z",
"firstSeenAt": "2026-10-02T16:21:23.777Z",
"firstSeenIsBaseline": false,
"indexLagHours": 0.9,
"lastSeenAt": "2026-10-02T16:21:23.777Z",
"removedAt": null,
"postedAt": "2026-10-02T15:25:57.680Z",
"postedAtApproximate": false,
"updatedAt": null,
"repostOf": null,
"repostKind": null,
"repostCount": 0,
"reopenedCount": 0,
"duplicateGroup": null,
"duplicateKind": null,
"isDuplicate": false,
"companyDomainSource": "ats_profile",
"companyDomainConfidence": "high",
"snippet": "Before the detail, here's the challenge you'd help us solve. We build the embodied intelligence that moves real vehicles safely, and the ecosystem a billion machines will run on in the future. Very few people in AI can say this. Every role here, whatever the team, plugs into…",
"ats": "ashby",
"board": "ashby:wayve",
"jobKey": "ashby:wayve:102a31c7-44a5-48bc-8c6e-1f4434e83974"
}

The run's key-value store has a SUMMARY record: status (SUCCESS, NO_RESULTS, LIMIT_REACHED, FAILED), jobs (or companies) returned, matching (total matches, capped at 100,000: then matchingIsCapped is true and messages say "100,000+"), the query sent and the amount charged.

Hiring companies output

Set output to companies to turn any job search into a list of companies that are hiring for it — for recruiters, staffing agencies and B2B sales teams. Each company appears once, however many matching jobs it has (companies are grouped by their website; copies of one opening are counted once):

{
"company": "Maibornwolff Gmbh",
"companyDomain": "maibornwolff.de",
"companyWebsite": "https://www.maibornwolff.de/",
"companyDomainSource": "board_link",
"companyDomainConfidence": "high",
"matchingJobs": 5,
"matchingRemoteJobs": 0,
"totalLiveJobs": 52,
"sampleTitles": [
"Senior Data Engineer (m/w/d)"
],
"topLocations": [
"Munich",
"Frankfurt",
"Berlin",
"Hamburg",
"Leipzig"
],
"countries": [],
"topDepartments": [
"Data Engineering & BI / Data Science"
],
"newestPostedAt": "2025-12-17T09:24:30.000Z",
"oldestPostedAt": "2025-12-17T08:55:15.000Z",
"newestFirstSeenAt": "2026-10-02T18:04:17.496Z",
"careersUrl": "https://maibornwolff.jobs.personio.de/",
"boards": [
{
"ats": "personio",
"board": "personio:maibornwolff",
"careersUrl": "https://maibornwolff.jobs.personio.de/",
"liveJobs": 52
}
],
"enrichment": "contacts",
"enrichmentStatus": "ok",
"organizationName": "MaibornWolff GmbH",
"primaryEmail": "info@maibornwolff.de",
"emails": [
"info@maibornwolff.de"
],
"primaryPhone": "+4989544253000",
"socialProfiles": {
"linkedin": "https://www.linkedin.com/company/maibornwolff",
"instagram": "https://www.instagram.com/maibornwolff",
"youtube": "https://www.youtube.com/c/MaibornWolffGmbH",
"facebook": "https://www.facebook.com/MaibornWolffGmbH"
},
"address": "Theresienhöhe 13, 80339 München, DE",
"companyKey": "maibornwolff.de"
}
  • Company website: found from the ATS profile, the careers page (redirects and links), job URLs on the company's own site, or a homepage that links back to the careers board. companyDomainConfidence is high or medium; companies we could not map have companyDomain: null (use onlyWithWebsite to skip them).
  • Contacts (enrichCompanies): contacts runs our Website Contact Extractor once for all company websites and adds primaryEmail, emails, primaryPhone, socialProfiles, address, contactFormUrl; company runs Company Website Enrichment (description, industry, business model, contacts, tech stack). That Actor runs in your Apify account and charges its own price per website; enrichmentStatus says ok, failed, not_returned or no_website for each company. Only public company-level contact details are collected, not personal profiles.

Experience level and salary

  • seniority is one of internship, entry, mid, senior, lead, executive, or empty. It is derived, not guessed: an internship or apprenticeship employment type gives internship; otherwise level words in the title decide: Intern/Werkstudent → internship; Junior/Jr/Graduate/New Grad/Trainee or "Engineer I" → entry; Mid-level/Intermediate or "Engineer II" → mid; Senior/Sr or "Engineer III" → senior; Lead/Staff Engineer/Principal/Supervisor/Engineering Manager → lead; Director/Head of/VP/Chief/President → executive. Functional "manager" titles (Product, Project, Account, Marketing Manager…) do not count as people managers. senioritySource says employment_type or title. Many titles ("Software Engineer") name no level: the Experience level filter drops them unless Also keep jobs without a known experience level is on.
  • Minimum salary compares only jobs whose ATS publishes a salary range (or wrote one in the posting). Periods are compared as yearly amounts (hour × 2080, day × 260, week × 52, month × 12), shown per job as salaryAnnualMin/salaryAnnualMax in the job's own currency. Other currencies are converted with fixed approximate rates (about 45 currencies, rounded autumn-2026 rates, can be some percent off) for the comparison only. Jobs without a comparable salary are kept by default.
  • Both filters run on the results of the other filters (the index has no level or salary column to search), so a run with them reads whole pages and checks at most 200,000 jobs; SUMMARY shows scannedJobs, jobsFilteredOut and matchingBeforeLevelSalaryFilters. Dropped jobs are not charged. They work with the jobs output, not with the hiring-companies output.

Dates: posted vs. first seen

  • sourcePostedAt is the date the employer's ATS publishes (Workday gives the day only: sourcePostedAtApproximate: true). originalPostedAt keeps the first date when an employer later moves it forward.
  • firstSeenAt is when our daily crawl first saw the job. For jobs that were already online when we started reading their board, firstSeenIsBaseline is true and firstSeenAt only marks the start of our observation.
  • indexLagHours is firstSeenAt − sourcePostedAt for jobs that appeared after we started watching the board: how long a new job took to reach the index.

Reposts and duplicates

  • repostKind: new_url_same_requisition / new_url_after_removal (a job taken down and posted again under a new URL, repostOf = the earlier job), reopened (the same job came back), date_refreshed (the posting date moved forward; repostCount counts how often).
  • duplicateKind: same_requisition (two URLs of one ATS requisition), same_text (same title, location and text), cross_board (the same job on two career boards of one company), same_title_location (same title in the same place — often several real openings, so never hidden). With skipDuplicates (on by default) the extra copies (isDuplicate: true) of the first three kinds are left out, and one posting URL listed on two boards is returned once (SUMMARY.sameUrlDropped counts them).

Use with AI agents (MCP)

Connect Apify's MCP server (https://mcp.apify.com?tools=tidytools/ats-job-search) to Claude, Cursor or any MCP client, then ask e.g. "Find remote machine learning engineer jobs posted this week at companies using Greenhouse or Ashby."

{ "keywords": ["machine learning engineer"], "remoteOnly": true, "postedWithinDays": 7, "ats": ["greenhouse", "ashby"], "maxResults": 50 }

A run with no matching jobs costs only the start fee; if the index is temporarily unreachable the run fails without charging any jobs. Input mistakes (an unknown country code or ATS, a non-numeric "posted within" or "Max results" value, a list field given as a number, more than 50 values in one list, a yes/no field that is not true or false, a minimum salary with the hiring-companies output) never fail the run: it ends normally with one free invalid_input row per problem (charged: false) and SUMMARY.status NO_RESULTS.

Pricing

Pay per event:

  • Job returned: $0.0015 ($1.50 per 1,000), $1.35 per 1,000 on Silver, $1.20 per 1,000 on Gold and above
  • Run start: $0.0005 per run
  • Hiring company (output = companies): $0.003 per company row returned, $2.70 per 1,000 on Silver, $2.40 per 1,000 on Gold and above. Runs with no matching companies pay only the start fee. With enrichCompanies, the enrichment Actor charges its own price per website in your account (Website Contact Extractor: $0.002 per website where contact details were found).

Set Max results (and Apify's maximum charge per run) to cap the cost: the Actor stops exactly at your limit.

Coverage and freshness

  • Boards: 54,000+ working career boards (42,000+ with open jobs) of Greenhouse, Lever (US and EU), Ashby, Workday, Workable, Recruitee, Personio, Breezy HR, Teamtailor, BambooHR, Pinpoint, JazzHR, Homerun, Rippling, Gem and Freshteam, discovered from public web crawls (Common Crawl, 2024–2026); the list grows over time.
  • In a 100-job sample (2 October 2026), 100 jobs were still listed on the employer's board and 100 job URLs opened.
  • Each board is read once a day. A job that disappears from its board gets removedAt within about 24 hours (48 hours when the whole board goes offline).
  • Workday shows at most 2,000 jobs per career site; for the largest Workday employers the index holds those 2,000.
  • Workday posting dates are day-precise ("Posted 3 days ago"), marked postedAtApproximate: true.
  • country is read from the location text; jobs with unclear locations have country: null.

Limitations

  • Only employers that use one of the 16 supported ATSs are in the index. Jobs posted only on LinkedIn, Indeed or a custom careers page are not.
  • Up to a day old. Boards are read once a day; for live jobs of specific companies at the moment of the run, use the Career Page Jobs Scraper.
  • Keywords match the job title only, not the description.
  • No full descriptions: links and short snippets only.
  • Salary is filled only when the employer's ATS publishes it; country is guessed from the location text and can be empty.

FAQ

Does the Actor collect usage data? The Actor sends one anonymous usage counter per run (input type, item count range, outcome) to improve the tool; no URLs, content or account data.

Is this data scraped from LinkedIn or job sites? No. It comes only from the employers' own applicant tracking systems through their public job-board endpoints, with links back to the original postings.

Can I get full job descriptions? The index keeps links and short snippets only. For full descriptions of specific companies, use the Career Page Jobs Scraper with descriptions on.

How do I get only junior or internship jobs? Set Experience level to internship and entry. The level comes from the employment type or the words in the title (no AI); jobs whose title names no level are left out unless you tick Also keep jobs without a known experience level. See "Experience level and salary".

Can I set a minimum salary? Yes: Minimum salary with its currency and period (e.g. 100000 USD per year, or 50 EUR per hour). Jobs that state no salary are kept by default (most employers publish pay only where the law requires it); turn Keep jobs that state no salary off to drop them. Jobs removed by these filters are not charged.

Do I get the same job twice? No: copies of one opening are left out by default (skipDuplicates), and one posting URL is returned once per run. Several real openings with the same title in one city are kept.

What happens if I mistype an input? The run ends normally with a free invalid_input row that says what to fix; nothing is charged except the start fee. This includes a search the index itself refuses. A search with no match is not a mistake: it ends normally with no rows and costs only the start fee. An empty search (no keywords or filters) is allowed and returns the newest jobs of the whole index, so keep Max results small.

Something missing or wrong? Open an issue on the Actor's Issues tab: we read every one.