DEV Community

Nikolas
Nikolas

Posted on

Greenhouse vs Ashby vs Lever vs SmartRecruiters: which job API should you scrape?

If you scrape job boards, these four cover a huge share of tech employers. They are also four different APIs with four different conventions. Here is the honest comparison I wish I had before writing my fourth parser.

Greenhouse

Endpoint: boards-api.greenhouse.io/v1/boards/{token}/jobs

  • One request returns every job. Add ?content=true for descriptions.
  • Trap: content=true roughly 12×'s the payload (5.4 MB vs 436 KB for a 715-job board). If you only need N jobs, fetch the list first and then per-job /jobs/{id} (8 KB each).
  • content is double-encoded HTML — entities then tags.
  • Salary is sometimes in metadata, a label/value array ("Salary Range" → "$180k–$230k"), and often nowhere.

Ashby

Endpoint: api.ashbyhq.com/posting-api/job-board/{token}?includeCompensation=true

  • The only one of the four with structured compensation: numbers live on compensation.summaryComponents[] where compensationType === "Salary", not on the tier object most code reads first.
  • Trap: isRemote: true is set for Hybrid roles. Trust the workplaceType string ("Remote" / "Hybrid" / "OnSite"), or your "remote only" filter silently returns hybrid jobs.
  • Board tokens are case-sensitive, and no server-side limit exists — a board like OpenAI is ~14 MB.

Lever

Endpoint: api.lever.co/v0/postings/{token}?mode=json

  • Supports &limit=N, which is genuinely useful for large boards (19 KB for 2 postings vs 775 KB for all).
  • Trap: the public endpoint has no salaryRange field at all. Any tool that reports ats-field salary evidence for Lever is inventing it.
  • Locations hide in categories.allLocations; workplace type is a hyphenated string.

SmartRecruiters

Endpoint: api.smartrecruiters.com/v1/companies/{token}/postings?limit=100&offset=N

  • Clean pagination with limit/offset and a totalFound count.
  • Trap: the public API is off by default. Of twenty well-known companies I tested, only a handful returned anything. Document that or users will read empty boards as your bug.
  • Descriptions require a second request per posting (/postings/{id}) and live inside jobAd.sections.

The summary table

Greenhouse Ashby Lever SmartRecruiters
Pagination / limit no no limit limit+offset
Structured salary sometimes (metadata) yes no no
Description in list with content=true yes yes per-posting
Workplace type no workplaceType string remote + hybrid
Company name in payload via board metadata no no from first posting

What to build instead of four parsers

I put the four behind one schema — one row per job with title, locations, workplace type, salary with evidence provenance, apply URL, and stable jobUid/recordHash for change detection. It is an Apify Actor, so there is nothing to host:

→ ATS Job Scraper: Greenhouse, Ashby & Lever

{ "boards": ["greenhouse:airtable", "ashby:Linear", "lever:spotify"], "maxJobsPerBoard": 100 }
Enter fullscreen mode Exit fullscreen mode

No API key, and a 5-job test costs a fraction of a cent.

Top comments (0)