ATS Job Board API - Greenhouse, Lever, Ashby & Personio
Pricing
from $2.00 / 1,000 results
ATS Job Board API - Greenhouse, Lever, Ashby & Personio
Give it your own list of companies, get every open role across four applicant-tracking systems in one normalised schema. Uses each vendor's official public job-board API - no login, no proxy, no scraping.
Pricing
from $2.00 / 1,000 results
Rating
0.0
(0)
Developer
Project Desk
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
3 days ago
Last modified
Categories
Share
ATS Job Board API — Greenhouse, Lever, Ashby & Personio
Give it your own list of companies. Get back every open role across four applicant-tracking systems in one schema, deduplicated where a company posted the same role to two of them.
No login. No proxy. No scraping. Each of these vendors publishes a public job-board API so third parties can embed a customer's careers page — this Actor consumes exactly those documented endpoints.
Why this exists
Most job Actors sell you a hosted feed of somebody else's target list. If you want your forty companies polled every morning, that isn't what you're buying.
This is the other shape: you supply the companies, it returns their roles. Useful for recruiters tracking a client's competitors, sales teams reading hiring as a buying signal, job seekers watching a shortlist, and anyone building a niche job board.
Input
One entry per company, as a careers URL or the short ats:token form. Both work, mixed
freely:
https://boards.greenhouse.io/gitlabhttps://jobs.lever.co/leverdemohttps://jobs.ashbyhq.com/notionacme.jobs.personio.degreenhouse:stripelever:spotify
Where the token comes from. It is the company's name in its board URL — the gitlab in
boards.greenhouse.io/gitlab. If a company's careers page is a JavaScript app that doesn't
show its board URL, open a single job posting; the address bar will show the real board.
| Option | Default | What it does |
|---|---|---|
boards | — | The companies. Required. |
includeDescription | true | Full job text. Off gives a fast list of titles and links. |
deduplicate | true | Merge a role cross-posted to two boards, keeping the fuller copy. |
remoteOnly | false | Keep only remote roles. |
titleFilter | — | Case-insensitive substring on the title. |
maxJobs | 0 | Cap the rows. 0 is no cap. If the cap bites, the log says by how much. |
maxConcurrency | 8 | Boards fetched at once. |
Output
One row per role, the same seventeen fields whatever the source:
| Field | Notes |
|---|---|
ats | greenhouse, lever, ashby, personio |
company, board_token, job_id | job_id is the vendor's own and is stable across runs — the right key for diffing new and closed roles |
title, location, all_locations | Location exactly as the company wrote it, never re-mapped |
remote | One boolean, derived — the four vendors each say it differently |
department, team | |
employment_type, workplace_type | Vendor wording preserved rather than normalised into a guess |
apply_url | The public posting on the company's own board |
posted_at, updated_at | ISO 8601 where the board provides it, empty where it doesn't. Never invented |
description, description_chars | Plain text, HTML stripped. The char count is a cheap way to spot stub postings |
Two things worth knowing before you buy
Deduplication is deliberately conservative. It merges a role only when it appears on two different boards. Two roles on the same board that share a title and location are kept as two rows — a company hiring three Support Engineers in London is not a bug to be cleaned up, and an earlier, cleverer version of this merged "AI Engineer, Bangalore" into "Senior AI Engineer, US". Under-merging shows you one extra row; over-merging silently destroys a real vacancy. This errs toward the harmless mistake.
SmartRecruiters and Workable are not supported, on purpose. Both publish a public postings endpoint, and both currently return HTTP 200 with a valid but permanently empty response for every company tested (SmartRecruiters: eight companies, all zero results; Workable: nine accounts, all empty). Rather than ship adapters that always return nothing, they are left out until one demonstrably returns a job.
Coverage
Verified live on 27 August 2026, plain unauthenticated requests:
| ATS | Endpoint | Test board | Result |
|---|---|---|---|
| Greenhouse | boards-api.greenhouse.io/v1/boards/{token}/jobs?content=true | gitlab | 218 jobs |
| Lever | api.lever.co/v0/postings/{token}?mode=json | leverdemo | 384 jobs |
| Ashby | api.ashbyhq.com/posting-api/job-board/{token} | notion | 135 jobs |
| Personio | {token}.jobs.personio.de/xml | demo | XML, live |
741 rows in one run across the four, 712 of them carrying a real description.
Ashby roles marked isListed: false are skipped — the company has taken those off its
public board, and republishing them would be wrong.
Run it locally
No Apify account, no Docker:
python tools/try_local.py greenhouse:gitlab lever:leverdemo https://jobs.ashbyhq.com/notion
Writes tools/out/local-run.json and prints a summary. Same code path as the Actor — only
the Apify wiring is replaced.
Tests, stdlib only:
python tests/test_ats.py
79 assertions, including a regression block guarding the dedupe behaviour described above, and fixtures trimmed from real vendor responses so a shape change surfaces here rather than in production.
Pricing
Pay per result. You are charged per job row pushed, and nothing else — no platform fee, no
charge for a board that returns empty. Turning includeDescription off does not reduce the
row count and so does not reduce the price; it just makes runs faster and the output smaller.