ATS Job API Reference: nine applicant tracking systems, verified live

Wevegotscrapys1 pts0 comments

ATS Job API Reference — Greenhouse, Lever, Ashby, Workday, SmartRecruiters, Workable, Recruitee, Personio, BambooHR

Why this exists

Nine applicant tracking systems — Greenhouse, Lever, Ashby, Workday, SmartRecruiters, Workable, Recruitee, Personio, and BambooHR — each expose a public, unauthenticated JSON (or, in Personio's case, XML) endpoint that powers their own embeddable careers widget. These endpoints are not secret, but accurate documentation of their shapes and quirks is scattered across forum posts, GitHub issues, and half-stale blog posts. This page consolidates what we verified by actually calling each one, against a real company, on the date at the top of this page.

Nothing here is documented from memory or from someone else's writeup. Every endpoint URL, every trimmed JSON/XML sample, and every quirk below was observed directly.

Greenhouse

Verified live — stripe, 200 OK, 550 jobs

Endpoint

GET https://boards-api.greenhouse.io/v1/boards/{board_token}/jobs

No authentication. Add ?content=true to include each job's full HTML description in the same response (omit it, or use content=false, for a lighter list-only call).

Finding the board token

The token is the slug in a company's public Greenhouse board URL, e.g. boards.greenhouse.io/stripe → token stripe. It's usually — not always — the company's lowercase name with no spaces.

Example request

curl "https://boards-api.greenhouse.io/v1/boards/stripe/jobs?content=true"

Trimmed real response

"jobs": [<br>"id": 8023928,<br>"title": "Account Executive, Bridge",<br>"location": { "name": "London" },<br>"absolute_url": "https://stripe.com/jobs/search?gh_jid=8023928",<br>"company_name": "Stripe",<br>"first_published": "2026-07-30T06:59:38-04:00",<br>"updated_at": "2026-08-04T07:02:31-04:00",<br>"requisition_id": "See Opening ID",<br>"content": "&lt;h2&gt;&lt;strong&gt;Who we are&lt;/strong&gt;&lt;/h2&gt;\n..."

Quirk: content is double entity-encoded HTML<br>We watched this directly on the live response above. The field isn't plain HTML, and it isn't HTML with normal entities either — it's HTML where the entities have themselves been HTML-entity-encoded a second time. arrives as the literal text &lt;h2&gt;. You must run an HTML-entity-decode pass twice before you have real markup to strip or render. Decoding only once leaves you with visible &lt; and &gt; junk in the text.

Pagination

None. One call returns the full list of open jobs for the board — Stripe's request above returned all 550 open jobs in a single response, no offset/limit parameters exist.

Rate limits

Undocumented publicly; no Retry-After or rate-limit headers were present on our test responses. Be polite — space out requests and don't hammer a board in a tight loop, especially across many companies.

Other notes

company_name is reliably present — Greenhouse is one of only two platforms in this list (with Workable) that reliably gives you a human-readable company name back.

departments and offices are available as separate sibling endpoints (/departments, /offices) if you want the taxonomy independent of jobs.

Lever

Verified live — palantir, 200 OK, 301 jobs

Endpoint

GET https://api.lever.co/v0/postings/{site}?mode=json

No authentication. The mode=json parameter matters — without it Lever's default content negotiation can behave inconsistently; always pass it explicitly.

Finding the site slug

From a company's public Lever board, e.g. jobs.lever.co/palantir → slug palantir. Note Netflix, a commonly cited Lever example in older writeups, returned a 404 Document not found when we tested it — they're no longer on Lever, which is exactly the kind of staleness this page is trying to avoid perpetuating.

Example request

curl "https://api.lever.co/v0/postings/palantir?mode=json"

Trimmed real response

"id": "a1b2c3d4-...",<br>"text": "Software Engineer, Backend",<br>"categories": {<br>"department": "Engineering",<br>"location": "New York",<br>"allLocations": ["New York", "Remote"],<br>"commitment": "Full-time"<br>},<br>"workplaceType": "hybrid",<br>"createdAt": 1753564800000,<br>"hostedUrl": "https://jobs.lever.co/palantir/a1b2c3d4-...",<br>"applyUrl": "https://jobs.lever.co/palantir/a1b2c3d4-.../apply"

Quirk: response is a bare array, not an object<br>Unlike every other platform here, the top-level response is a JSON array, not an object with a jobs/content/result key. Code that assumes an object wrapper and does data.jobs will silently get undefined here.

Pagination

None on the public postings endpoint — one call returns every open posting.

Rate limits

Undocumented; no rate-limit headers observed. Space out calls, especially across a large company list.

Other notes

createdAt is a Unix millisecond timestamp, not ISO 8601 — convert it.

An empty array ([]) is genuinely ambiguous: it's returned both for a company that isn't on Lever at all in some edge cases and for a company that is on Lever but currently has zero open roles. In practice a company not on Lever usually 404s rather than returning an empty array, but don't rely on...

lever jobs company greenhouse stripe https

Related Articles