DEV Community

Dodo Data
Dodo Data

Posted on

Half the ATS job boards you find by guessing belong to a different company

If you are building anything on job data, sooner or later you want a map from company name to job board. Greenhouse boards live at boards-api.greenhouse.io/v1/boards/<slug>/jobs, Lever at api.lever.co/v0/postings/<slug>, Ashby at api.ashbyhq.com/posting-api/job-board/<slug>. All three are public, documented, and need no key.

The obvious move is to guess the slug from the company name. Lowercase it, strip the punctuation, try it.

It works about a quarter of the time. And half of what it finds belongs to a different company.

I measured this while building an index across 2,127 companies, and the verification step turned out to matter more than the discovery step.

The naive version, and what it costs you

Take "Airspace Link". Strip it down and you get three candidates: airspacelink, airspace-link, airspace. The third one hits: boards-api.greenhouse.io/v1/boards/airspace/jobs returns 15 open jobs, HTTP 200, real data.

It is not Airspace Link's board.

Here is the same failure four times over, all from one 40-company sample:

Company slug that returned jobs actually theirs?
Airspace Link greenhouse/airspace no
ICON plc ashby/icon no
Sanctuary Computer ashby/sanctuary no
Dispatch greenhouse/dispatch no

Every one is a generic first token that some other company owns. A job index built this way does not merely have gaps. It confidently attributes one employer's open roles to another, and nothing in the response tells you.

Across the first 654: 26% of companies had a findable board, and only 47% of those survived verification.

Verification is harder than it looks, because the three APIs are not equal

The obvious check is to compare the company name against the board's company name. That works on exactly one of the three.

Greenhouse publishes it. GET boards-api.greenhouse.io/v1/boards/<slug> returns:

{"name": "Dispatch Technologies, Inc", "content": "..."}
Enter fullscreen mode Exit fullscreen mode

Ashby does not. The entire payload is {"jobs": [...], "apiVersion": "..."} and a job looks like this:

address, applyUrl, department, descriptionHtml, descriptionPlain,
employmentType, id, isListed, isRemote, jobUrl, location,
publishedAt, secondaryLocations, team, title, workplaceType
Enter fullscreen mode Exit fullscreen mode

No organisation name. No company field. The closest thing is team, which on the board I was checking said "Construction".

Lever does not either. Its fields are text, categories, hostedUrl, applyUrl, description and friends. categories gives you commitment, department, location and team. No company.

So for two of the three systems, the board itself will never tell you whose it is.

The check I wrote first was circular, and passed everything

My first verification compared the company name against the hostedUrl in the response. For jobs.lever.co/jumpcloud/... and company "JumpCloud", that matches.

Of course it matches. The URL contains the slug I just guessed. The test was asking whether the slug I guessed was the slug I guessed.

It reported 8 of 8 confirmed. Every one of the four false positives above sailed through it.

If you are writing this kind of check, the question to ask is: could this test ever fail? If the evidence is derived from the thing you are testing, it cannot.

What actually works: match a job you have already seen

The signal that is available on all three systems is the job list itself. If you already know one real job at that company, from any other source, check whether it is on the board.

def norm(s):
    return re.sub(r"[^a-z0-9]", "", (s or "").lower())

matched = known_titles & {norm(t) for t in board_titles}
if matched:
    ...  # this is their board
Enter fullscreen mode Exit fullscreen mode

That is what caught all four. greenhouse/airspace has 15 jobs and not one of them is the backend role we had seen at Airspace Link.

Two things to know about it:

It gives false negatives, and you must not treat those as proof of anything. greenhouse/shopmy failed the job match, and its board is literally called "ShopMy". The remote board we sourced titles from was listing a role the ATS board no longer carried. So use the name when the ATS gives you one, and fall back to the job match when it does not.

Neither signal firing means you do not know. That is a third state, not a no. Those go in a candidates file, not the index, and a later pass with more known titles for that company can promote one.

Two smaller things that cost me time

api.lever.co publishes Crawl-delay: 1. My first throttle was a single global 0.15s pause, which sent Lever roughly four requests a second against the one second it asks for. Worth checking the exact host you are hitting, not the one that looks similar: jobs.ashbyhq.com/robots.txt disallows /api/, but api.ashbyhq.com is a different host and serves no robots.txt at all.

Filter the company list before you probe it. I measured a 67% hit rate on a sample, then watched it collapse to about 10% on the full run. The sample had been filtered to engineering roles, which is exactly the population that runs an ATS. Most employers on a remote job board are staffing and VA agencies that never had a Greenhouse board to find. Filtering first removed roughly 6,700 requests that were only ever going to 404 on somebody else's API, which is both faster and more polite.

The one that got past all of it

Everything above was in place, and a board named NATIONAL still ended up in the index three times over: as National Church Residences, as National Laboratory of the Rockies, and as National Louis University. Same for a board called Paradigm and one called Public.

The name check was doing containment in both directions. Any board whose name is a common word swallows every company that starts with it. Worse, it re-admitted greenhouse/airspace for Airspace Link, which the job match had already disproved, because "Airspace" is inside "Airspace Link".

Containment is the wrong test. One name has to lead with the other. That single change is the difference between these:

Company Board name Same company? Containment says Prefix says
Dispatch Dispatch Technologies, Inc yes match match
Domino Data Lab Domino yes match match
Beam Bridge to Enter Advanced Mathematics (BEAM) no match no match
Remote General Assembly Remote Jobs no match no match

Two more things came out of that round:

The slug is evidence on its own. greenhouse/dominodatalab is not a guess that happened to land, it is the company name spelled out. Trusting an exact slug recovered nine boards a stricter pass had wrongly thrown away.

Add a consistency check that does not care how a row was confirmed. A board belongs to one company. If two companies both claim it, at most one is right and you cannot tell which, so demote all of them. That backstop is what caught NATIONAL, and it would have caught it no matter which rule let it in.

The result

264 verified boards, 20,929 open jobs, from 2,127 companies. Every row carries which evidence confirmed it:

{
  "company": "Help Scout",
  "ats": "ashby",
  "slug": "helpscout",
  "open_jobs": 10,
  "evidence": "job_match",
  "confirmed_by": "directorofproductdesign"
}
Enter fullscreen mode Exit fullscreen mode

148 confirmed on the board's own name, 94 on a job match, 13 on both, 9 on an exact slug. 104 companies sit in the candidates file. Final audit: zero boards claimed by two companies, zero short-slug matches outside the company name, zero rows without evidence.

Twelve percent of the companies I started with. The other 88% either have no board on these three systems or could not be proven, and saying so is the entire point.

If you want the job data without building this

The same three endpoints, with the row shape and the ATS handling already done, are on Apify Store:
Greenhouse, Lever, Ashby, Workday, or the multi-system one that auto-detects which system a career page runs on. Public endpoints only, no personal data, $1 per 1,000 jobs.

If you build your own instead, the one thing I would take from this post is the third state. A board you found but cannot prove is not a board you found.

Top comments (0)