agenttoolworks

Reference

Four job board APIs, four different answers.

Greenhouse, Lever, Ashby and Workable each publish a keyless public API listing a company's open roles. They are genuinely easy to call. They also disagree about almost every field you actually want, in ways that only show up once you have parsed a few thousand postings. This is what they return, measured against the live APIs on 2026-08-17.

What they have in common

One GET, one company, no key, no login, no pagination. You pass a board slug and you get every currently published role in a single response. That is the whole interface, and it is the reason a job search agent can be built on top of these four without touching a scraper or a login.

The slug is the part that comes off the careers page URL: jobs.lever.co/spotify means the Lever slug is spotify. It is not always the company name and it changes when a company renames or migrates.

Where they disagree

GreenhouseLeverAshbyWorkable
Endpointboards-api.greenhouse.io/v1/boards/{slug}/jobsapi.lever.co/v0/postings/{slug}?mode=jsonapi.ashbyhq.com/posting-api/job-board/{slug}apply.workable.com/api/v1/widget/accounts/{slug}
Top level shape{ jobs, meta.total }a bare array{ jobs, apiVersion }{ name, description, jobs }
Posting idid, a numberid, a uuidid, a uuidshortcode, no id field
Title fieldtitletexttitletitle
Locationlocation.name, an objectcategories.location, plus countrylocation, plus secondaryLocationscity and country, plus locations
Remote signalnoneworkplaceTypeisRemote, a booleantelecommuting, a boolean
Publication datefirst_published, ISO 8601createdAt, epoch millisecondspublishedAt, ISO 8601published_on, a date with no time
Apply linkabsolute_url, host varieshostedUrl and applyUrljobUrl and applyUrlurl and application_url
Unknown slug404, JSON body404, JSON body404, plain text404, plain text

Field names read off live responses on 2026-08-17 from the boards named in the measurements below.

The five traps

1. Lever returns a bare array

The other three wrap their postings in an object. Lever hands you the array itself, so the first thing most clients do, reach for data.jobs, gets undefinedrather than an error. Worse, it fails the same way an empty board would, so the bug reads as "this company has no openings".

2. Lever's date is epoch milliseconds

createdAt is a number like 1784569799619, not a string. The other three send strings, and Workable's is a bare calendar date with no time and no zone. Any code that sorts postings by date across boards has to normalise all four before comparing, and a naive string sort puts a Lever posting either first or last forever depending on the direction.

3. Greenhouse has no remote field

Ashby has isRemote, Workable has telecommuting, Lever has workplaceType with values like remote and hybrid. Greenhouse has nothing: the only signal is the free text the employer typed into the location, which in one real Stripe posting is SF, NYC, SEA, CHI. If you want a remote filter that works across all four, three quarters of it is a real field and the rest is a heuristic over a string. Say so in your output rather than pretending the confidence is uniform.

4. The apply link is not necessarily on the ATS

Greenhouse's absolute_url is whatever the employer configured. Across eight boards sampled on 2026-08-17 it pointed at four different hosts: job-boards.greenhouse.io, boards.greenhouse.io (both are live, because Greenhouse migrated and did not retire the old one), plus stripe.com and careers.datadoghq.com. So you cannot recognise a Greenhouse posting by its apply URL, and you cannot assume the link stays inside the ATS. Send the employer's own URL through untouched.

5. Empty and abandoned look identical

A wrong slug is a clean 404 on all four. But an account that exists and publishes nothing returns 200 with an empty list, and so does a company that migrated away months ago and left the account behind.

This is not a rare edge. Of eighteen real Workable accounts we resolved on 2026-08-17, every single one answered 200 with a name, a description and zero jobs, several with company descriptions years out of date. It looked so much like a dead endpoint that we nearly published that conclusion, until walter-careers returned 58 postings and settled it: the API is fine, the accounts are empty. If you are building a directory, an entry that has returned zero for weeks is worth flagging, because it is much more likely to be stale than quiet.

Size and latency

Worth knowing before you fan out across fifty companies, because the slowest board sets your response time and one of these is thirty times slower than another.

ProviderBoardPostingsResponseMedian time
Ashbylinear33465 KB0.22 s
Workablewalter-careers58211 KB0.69 s
Greenhousestripe578362 KB1.24 s
Leverspotify103994 KB6.97 s

Median of three sequential runs from one machine in Europe on 2026-08-17. This is a measurement, not a benchmark: it mixes distance, board size and whatever those services were doing that afternoon, and Lever ranged from 6.3 to 8.8 seconds across the three runs while Ashby stayed inside a tenth of a second. Ashby is also the only one of the four that sent a public Cache-Control, at sixty seconds.

We did not probe rate limits, deliberately. Finding one means hammering somebody else's public API until it pushes back, which is a rude way to write an article and exactly the behaviour that gets these endpoints locked down for everyone. Cache your responses, spread your requests, and treat an HTTP 429 as the answer to that question.

If you are pointing an agent at this

The four APIs are the easy part. The work is everything downstream: knowing which ATS a company uses before you can call anything, keeping slugs alive as companies migrate, normalising four location shapes and three date formats into one, and collapsing the same role posted twice. An agent asking for "remote data engineering roles in Europe" should not have to know any of that, and should not spend its context window discovering that Lever answers with an array.

That is what JobsRadar does: one call, one normalised list, deduplicated, with the boards that failed named in the response so a broken board never silently shrinks your results. It costs one credit, and a free key comes with a hundred.

If you would rather build it yourself, everything above is what you need to know before you start, which is the point of writing it down. The endpoints belong to Greenhouse, Lever, Ashby and Workable; we are not affiliated with any of them, and the boards named here are simply the ones we measured.

Questions

Do these APIs need an API key?

No. All four are public, keyless endpoints that the applicant tracking system publishes so employers can distribute their openings. That is what they are for. Nothing here involves a login, and nothing here is scraping.

How do I find a company's board slug?

It is in the URL of their careers page: jobs.lever.co/SLUG, boards.greenhouse.io/SLUG, jobs.ashbyhq.com/SLUG, apply.workable.com/SLUG. It is not always the company name, and it changes when a company renames itself or migrates.

What happens if a company moves from one ATS to another?

The old board usually starts answering 404, and any directory that hard-codes the old slug silently loses that company. We hit exactly this: Hugging Face had left Lever, and our live test caught the stale entry rather than a customer.

Can I tell whether a role is remote?

It depends on the board, which is the single most annoying thing about this set. Ashby has isRemote, Workable has telecommuting, Lever has workplaceType. Greenhouse has no remote field at all, so the only signal is whatever the employer typed into the location.

Is an empty response the same as a company with no openings?

Not necessarily, and this is the trap that costs the most time. A wrong slug gives you a 404 on all four. But an account that exists and publishes nothing gives you a 200 with an empty list, and so does a company that left the ATS months ago and never deleted the account.