Which ATS does a company use? How to find its job board (Greenhouse, Lever, Ashby, Workday)
Last updated: 4 November 2026 · By Quicksloth
Before you can download a company's open jobs, you need two things: the system it posts them on (its ATS, applicant tracking system) and the name of its job board in that system, such as figma on Greenhouse or ramp on Ashby. This guide shows where those two things show up, how to confirm them with one request to the system's public API, and a short free script that does both. We ran it today on 14 well-known companies: it recognised 12.
Short answer: open any job on the company's careers page and read the address. greenhouse.io, lever.co, ashbyhq.com or myworkdayjobs.com tell you the system, and the part right after the domain is the board name. If the careers page stays on the company's own domain, search its page source for those domains, or try the company name on the three public APIs: HTTP 200 means the board exists, 404 means it does not. Workday addresses cannot be guessed; you have to find one.
The fastest check: open a job and read the address
Click any job on the careers page, or its Apply button, and look at the address bar. These are the addresses we saw on 4 November 2026:
| System | Job page address looks like | What you need |
|---|---|---|
| Greenhouse | boards.greenhouse.io/figma/jobs/… or job-boards.greenhouse.io/gitlab/jobs/… | board name (Greenhouse calls it the "Job Board URL token"): figma |
| Lever | jobs.lever.co/spotify/… (EU instance: jobs.eu.lever.co/…) | board name: spotify |
| Ashby | jobs.ashbyhq.com/ramp/… | board name: ramp |
| Workday | redhat.wd5.myworkdayjobs.com/jobs/… | three parts: company (redhat), the wd5 number and the site (jobs) |
Addresses on smartrecruiters.com, workable.com, icims.com, jobvite.com and similar domains mean another system. This guide, its script and our Actors do not cover those.
When the careers page hides it: look in the page source
Many careers pages list the jobs on the company's own domain. The system's address is then often still in the page: right-click and choose View page source (in most browsers), then search for greenhouse, lever.co, ashbyhq or myworkday. What we found in the HTML of real careers pages on 4 November 2026, downloaded once each without running JavaScript:
| Careers page | What the source contains | System and board |
|---|---|---|
figma.com/careers/ | 290 links to boards.greenhouse.io/figma/jobs/… | Greenhouse, figma |
notion.com/careers | 292 links to jobs.ashbyhq.com/notion/… | Ashby, notion |
ramp.com/careers | 336 links to jobs.ashbyhq.com/ramp/… | Ashby, ramp |
linear.app/careers | 62 links to jobs.ashbyhq.com/Linear/… | Ashby, Linear |
careers.duolingo.com | a call to Greenhouse's API: boards-api.greenhouse.io/v1/boards/duolingo/departments | Greenhouse, duolingo |
| Workday's own careers page | links to workday.wd5.myworkdayjobs.com/Workday/ | Workday, workday / wd5 / Workday |
redhat.com/en/jobs | links to redhat.wd5.myworkdayjobs.com/jobs/ | Workday, redhat / wd5 / jobs |
That is 7 of the 14 pages we tried. The other seven had no system address in their HTML: GitLab, Stripe, Cloudflare, Spotify, Salesforce and NVIDIA build or link their job lists in other ways, and OpenAI's careers page answered our request with HTTP 403. Greenhouse boards can also be embedded with an address like boards.greenhouse.io/embed/job_board?for=<board>, where the board name follows for=; none of our 14 pages used it, but the script below looks for it too.
When the job list is built by JavaScript, the board address may appear only in the requests the page makes while it loads. In the browser's developer tools (F12), open the Network tab, reload the page and type greenhouse, lever, ashby or myworkday in the filter box.
Guess the board name and ask the API
Greenhouse, Lever and Ashby all have a public job board API that needs no key, so you can test a guess with one request. Greenhouse's documentation says it plainly: "Job Board data is publicly available, so authentication is not required for any GET endpoints" (Greenhouse Job Board API, read on 4 November 2026). Lever documents its Postings API and Ashby its Job Postings API.
https://boards-api.greenhouse.io/v1/boards/<board>/jobs
https://api.lever.co/v0/postings/<board>?mode=json
https://api.ashbyhq.com/posting-api/job-board/<board>
HTTP 200 means the board exists; 404 means it does not. For the seven companies whose page source showed nothing, we tried the company name in lower case on all three on 4 November 2026:
| Name tried | Greenhouse | Lever | Ashby |
|---|---|---|---|
gitlab | 200, 239 jobs | 404 | 404 |
stripe | 200, 754 jobs | 404 | 404 |
cloudflare | 200, 468 jobs | 404 | 404 |
spotify | 404 | 200, 84 jobs | 404 |
openai | 404 | 404 | 200, 812 jobs |
salesforce | 404 | 404 | 404 |
nvidia | 404 | 404 | 404 |
Three things we learned from the answers:
- Check that it is the right company. A 200 only says that a board with that name exists. Greenhouse returns a
company_namefor each job (for the three boards above: "GitLab", "Stripe" and "Cloudflare"); Lever and Ashby do not, so open one of the job links and look at it. - Upper and lower case: on 4 November 2026 Greenhouse and Ashby answered 200 whatever the case (
FIGMA,Linear,linear,LINEAR), but Lever answered 404 toSpotifyand 200 tospotify. Copy Lever board names exactly. - 404 everywhere does not mean the company has no jobs: Salesforce and NVIDIA hire on Workday, which you cannot test this way.
Workday is different: company, wdN and site
A Workday career site has an address like https://<company>.wd<N>.myworkdayjobs.com/<site>. The number after wd differs from company to company, and the site name is chosen by the company, so there is no single request that answers "does this company use Workday?". You have to find one address, by clicking a job or from the page source. The four Workday sites of our test, checked on 4 November 2026 with the JSON request the career page itself makes (explained in our Workday guide):
| Company | Career site | Where we found it | Jobs |
|---|---|---|---|
| Workday | workday.wd5.myworkdayjobs.com/Workday | source of its careers page | 362 |
| Red Hat | redhat.wd5.myworkdayjobs.com/jobs | source of its careers page | 138 |
| NVIDIA | nvidia.wd5.myworkdayjobs.com/NVIDIAExternalCareerSite | not on its careers page, which links to a job search at jobs.nvidia.com; that page's Sign In link points to the Workday site | 2,000 (Workday's count stops there) |
| Salesforce | salesforce.wd12.myworkdayjobs.com/External_Career_Site | not in the HTML of its jobs page, which has no job links until JavaScript runs; we used the address from our Workday guide | 1,554 |
Once you have the host (for example workday.wd5.myworkdayjobs.com), its robots.txt is worth a look: on all four hosts above it listed the company's career sites, one Sitemap: line each (four sites for Workday, nine for Salesforce, including Slack and Tableau). It also says which sites may be read: the script below checks it before each Workday request.
A free Python script: careers page in, system and board out
The script does the two steps above for a list of companies. It reads each careers page once (if the site's robots.txt allows it), looks for Greenhouse, Lever, Ashby and Workday addresses in the source, and checks what it found on the system's API. If the page shows nothing, it tries the company name on the Greenhouse, Lever and Ashby APIs. It pauses one second between requests and uses only Python's standard library (Python 3.8 or later).
"""Find which ATS a company uses (Greenhouse, Lever, Ashby, Workday) and the name of its job board.
Step 1: read the careers page once (if its robots.txt allows it) and look for job-board addresses in the source.
Step 2: check what step 1 found on the system's public API, or, if it found nothing, try the company name
on the Greenhouse, Lever and Ashby APIs (HTTP 200 = the board exists, 404 = it does not).
"""
import json
import re
import sys
import time
import urllib.error
import urllib.request
import urllib.robotparser
from collections import Counter
from urllib.parse import urlsplit
UA = "which-ats-script"
COMPANIES = [arg.split("=", 1) for arg in sys.argv[1:]] or [["Figma", "https://www.figma.com/careers/"]]
PATTERNS = {
"greenhouse": r"(?:job-)?boards(?:-api)?\.greenhouse\.io/(?:embed/job_(?:board|app)(?:/js)?\?(?:[^\"'\s<>]*?&(?:amp;)?)?for=|v1/boards/)?([A-Za-z0-9_-]+)",
"lever": r"(?:jobs|api)\.lever\.co/(?:v0/postings/)?([A-Za-z0-9_.-]+)",
"ashby": r"(?:jobs\.ashbyhq\.com|api\.ashbyhq\.com/posting-api/job-board)/([A-Za-z0-9_.%-]+)",
"workday": r"([a-z0-9-]+\.wd\d+\.myworkdayjobs\.com/(?:[a-z]{2}-[A-Z]{2}/)?[A-Za-z0-9_-]+)",
}
OTHERS = r"smartrecruiters\.com|workable\.com|icims\.com|jobvite\.com|recruitee\.com|bamboohr\.com|teamtailor\.com|personio\.(?:de|com)"
NOT_BOARDS = {"embed", "api", "js", "css", "static", "assets", "v0", "v1", "favicon.ico"}
def request(url, body=None):
"""Return (HTTP status, text). Errors such as 404 or 403 come back as a status, not an exception."""
data = json.dumps(body).encode() if body else None
headers = {"User-Agent": UA, "Content-Type": "application/json"} if body else {"User-Agent": UA}
try:
with urllib.request.urlopen(urllib.request.Request(url, data, headers), timeout=30) as response:
return response.status, response.read().decode("utf-8", "replace")
except urllib.error.HTTPError as error:
return error.code, ""
except (urllib.error.URLError, TimeoutError):
return 0, "" # no answer at all
def allowed(url):
"""Read robots.txt with our own User-Agent (some sites refuse Python's default one)."""
parts = urlsplit(url)
status, text = request(f"{parts.scheme}://{parts.netloc}/robots.txt")
if status in (401, 403):
return False
robots = urllib.robotparser.RobotFileParser()
robots.parse(text.splitlines() if status == 200 else [])
return robots.can_fetch(UA, url)
def from_page(url):
if not allowed(url):
return {}, "robots.txt does not allow it"
status, html = request(url)
if status != 200:
return {}, f"HTTP {status}"
found = {}
for ats, pattern in PATTERNS.items():
names = Counter(m.rstrip(".%") for m in re.findall(pattern, html) if m.lower() not in NOT_BOARDS)
if names:
found[ats] = names.most_common(1)[0][0] # the board the page links to most often
others = sorted(set(re.findall(OTHERS, html)))
return found, "other systems: " + ", ".join(others) if others else ""
def check(ats, board):
"""Ask the system's API for the board: returns (HTTP status, number of jobs as text)."""
if ats == "greenhouse":
status, text = request(f"https://boards-api.greenhouse.io/v1/boards/{board}/jobs")
jobs = json.loads(text)["jobs"] if status == 200 else []
company = f", company_name {jobs[0].get('company_name')!r}" if jobs else "" # check it is the right company
return status, f"{len(jobs)} jobs{company}" if status == 200 else ""
if ats == "lever":
status, text = request(f"https://api.lever.co/v0/postings/{board}?mode=json")
return status, f"{len(json.loads(text))} jobs" if status == 200 else ""
if ats == "ashby":
status, text = request(f"https://api.ashbyhq.com/posting-api/job-board/{board}")
return status, f"{sum(j.get('isListed', True) for j in json.loads(text)['jobs'])} jobs" if status == 200 else ""
host, path = board.split("/", 1) # workday: tenant.wdN.myworkdayjobs.com/[en-US/]site
tenant, site = host.split(".")[0], path.split("/")[-1]
api = f"https://{host}/wday/cxs/{tenant}/{site}/jobs"
if not allowed(api):
return "robots.txt does not allow it", ""
status, text = request(api, {"appliedFacets": {}, "limit": 1, "offset": 0, "searchText": ""})
return status, f"{json.loads(text).get('total')} jobs" if status == 200 else ""
def guesses(name):
base = re.sub(r"[^a-z0-9 ]", "", name.lower()).split()
return list(dict.fromkeys(["".join(base), "-".join(base)]))
hits = 0
for name, url in COMPANIES:
found, note = from_page(url)
how, results = "page source", {}
for ats, board in found.items():
results[ats] = check(ats, board)
time.sleep(1)
if not found: # nothing in the page: try the company name on the three public APIs
how = "name guess"
for guess in guesses(name):
for ats in ("greenhouse", "lever", "ashby"):
if ats not in found:
status, jobs = check(ats, guess)
if status == 200:
found[ats], results[ats] = guess, (status, jobs)
time.sleep(1)
hits += any(status == 200 for status, _ in results.values())
answer = "; ".join(f"{ats}:{found[ats]} -> HTTP {s}, {n}" for ats, (s, n) in results.items()) or "not found"
print(f"{name:<11} [{how}] {answer}" + (f" ({note})" if note else ""))
time.sleep(1)
print(f"Recognised {hits} of {len(COMPANIES)} companies")
Give each company as Name=careers page URL. The run on 4 November 2026:
$ python3 which_ats.py "Notion=https://www.notion.com/careers" "Ramp=https://ramp.com/careers" \
"Linear=https://linear.app/careers" "Figma=https://www.figma.com/careers/" \
"Duolingo=https://careers.duolingo.com/" "Workday=https://www.workday.com/en-us/company/careers/overview.html" \
"Red Hat=https://www.redhat.com/en/jobs" "GitLab=https://about.gitlab.com/jobs/" "Stripe=https://stripe.com/jobs" \
"Cloudflare=https://www.cloudflare.com/careers/jobs/" "Spotify=https://www.lifeatspotify.com/jobs" \
"OpenAI=https://openai.com/careers/" "Salesforce=https://careers.salesforce.com/en/jobs/" \
"NVIDIA=https://www.nvidia.com/en-us/about-nvidia/careers/"
Notion [page source] ashby:notion -> HTTP 200, 146 jobs
Ramp [page source] ashby:ramp -> HTTP 200, 168 jobs
Linear [page source] ashby:Linear -> HTTP 200, 31 jobs
Figma [page source] greenhouse:figma -> HTTP 200, 145 jobs, company_name 'Figma'
Duolingo [page source] greenhouse:duolingo -> HTTP 200, 54 jobs, company_name 'Duolingo'
Workday [page source] workday:workday.wd5.myworkdayjobs.com/Workday -> HTTP 200, 362 jobs
Red Hat [page source] workday:redhat.wd5.myworkdayjobs.com/jobs -> HTTP 200, 138 jobs
GitLab [name guess] greenhouse:gitlab -> HTTP 200, 239 jobs, company_name 'GitLab'
Stripe [name guess] greenhouse:stripe -> HTTP 200, 754 jobs, company_name 'Stripe'
Cloudflare [name guess] greenhouse:cloudflare -> HTTP 200, 468 jobs, company_name 'Cloudflare'
Spotify [name guess] lever:spotify -> HTTP 200, 84 jobs
OpenAI [name guess] ashby:openai -> HTTP 200, 812 jobs (HTTP 403)
Salesforce [name guess] not found
NVIDIA [name guess] not found
Recognised 12 of 14 companies
What we got: 7 of 14 companies recognised from the page source and 5 more from the name guess, so 12 of 14. The two misses are the Workday companies whose careers pages do not show their Workday address. Given NVIDIA's job search page instead, python3 which_ats.py "NVIDIA=https://jobs.nvidia.com/careers" printed workday:nvidia.wd5.myworkdayjobs.com/NVIDIAExternalCareerSite -> HTTP 200, 2000 jobs. The whole run took 87 seconds, mostly the one-second pauses.
What the script does not do, to stay short: it does not run JavaScript; it tries name guesses only when the page shows nothing, and only in two forms (red hat becomes redhat and red-hat); it does not check Lever's EU instance or Greenhouse's EU boards; it does not look for myworkdaysite.com addresses; and it only names other systems (SmartRecruiters, Workable, iCIMS, Jobvite and a few more) if it sees them in the page, without checking them.
Limits and common mistakes
- The careers page is built with JavaScript.
- Then the HTML has no job links, as on 7 of our 14 pages. Click a job in a browser, use the developer tools' Network tab, or try the name guess.
- The site blocks scripts.
- OpenAI's careers page answered our script with HTTP 403; its Ashby board answered normally. Do not try to get around a block: use the name guess or the address of a job you opened in your browser.
- The careers page runs on one system and the applications on another.
- NVIDIA's job search page is on its own domain, but its Sign In link goes to Workday. The system that holds the jobs is the one in the job and Apply addresses; when two systems show up, open a job to see which one it is.
- The name guess finds a board, but it is another company.
- Two companies can have similar names. Check Greenhouse's
company_name, or open a job link from the response. - A board answers 200 with no jobs.
- The board exists but has nothing posted right now, or the company has moved to another system. Look at the careers page again.
- The company is on SmartRecruiters, Workable, iCIMS or another system.
- This guide, the script and our Actors cover Greenhouse, Lever, Ashby and Workday only.
Next: export the jobs
With the system and the board name, you can read the jobs:
- One-off export with a free script: our guides to the Greenhouse Job Board API, the Lever Postings API, the Ashby job board API and Workday career sites each have a short script that saves a board to CSV.
- Only the new jobs, on a schedule: see how to monitor new jobs at a list of companies. Write your list as
system:board, for examplegreenhouse:figma,lever:spotify,ashby:notion.
If you would rather not run scripts, we make two Actors on the Apify Store that read these boards in the cloud:
- ATS Jobs Scraper reads Greenhouse, Lever and Ashby boards into one schema. In Companies you can paste board URLs,
ats:boardnames, careers-page URLs or plain company names, up to 1,000 per run. For a careers-page URL it loads that one page and looks for a linked or embedded board; we ran its detection code on the pages we saved for this test, and it found the same five Greenhouse and Ashby boards as the script. For a plain company name it tries the most likely board names on Greenhouse, then Lever, then Ashby. A page with no board is reported in the run and the other companies continue; then paste the company name or the board URL instead. - Workday Jobs Scraper reads Workday career sites: paste the
myworkdayjobs.comaddress up to the site name, one per line (a language part such as/en-US/or a job path after the site name is accepted). A careers page on the company's own domain is not an address it can read.
Both charge per job a run saves, plus Apify's small start charge for each run; the current prices are in the Pricing tab of each Actor's page.
Questions and answers
- Is it allowed to look up a company's job board this way?
- The script reads one public careers page per company, after checking its robots.txt, and then the public job board APIs that Greenhouse, Lever and Ashby document for publishing job postings, which need no login or key. Their documentation describes these APIs for companies that build their own careers pages and does not say anything about other uses. Workday does not offer the JSON address as a public API, and its robots.txt differs from company to company (the script checks it). Keep a gentle pace, and check that your use of the job texts is allowed where you are.
- Is a Greenhouse board token secret?
- No. It is the board name in the public job board address (
figmainboards.greenhouse.io/figma). Greenhouse's documentation calls it the "Job Board URL token" and says that only the endpoint for submitting applications needs authentication, with the company's Job Board API key. - Can I list every company that uses Greenhouse, Lever, Ashby or Workday?
- Not with these APIs: they work one board at a time, and neither the script nor our Actors has a directory of companies. You start from your own list of companies.
- Does it tell me which ATS a company uses to screen my resume?
- It tells you where the company publishes its open jobs. That is usually where applications go too, but this guide is about reading job postings, not about resumes.
- Why did the script find nothing for a company I know is hiring?
- Most often the careers page loads its jobs with JavaScript, or the company uses Workday or a system this guide does not cover. Open a job in your browser and read the address: that settles it.
Script, page sources and APIs checked by Quicksloth on 4 November 2026 on the public careers pages and job boards of the 14 companies named; job counts change every day. Written with AI tools and checked by Quicksloth. Quicksloth is an independent developer and is not affiliated with Greenhouse, Lever, Ashby, Workday, Apify or the companies named. Greenhouse, Lever, Ashby, Workday and Apify are trademarks of their respective owners.