Company Job Boards API & Scraper
The ATS Job Boards API returns company-direct job postings pulled straight from applicant-tracking systems (Greenhouse, Lever and more) as clean JSON.
🤖 Using an AI assistant? Copy this link into ChatGPT / Claude / Cursor — it reads every endpoint and parameter instantly and tells you if this API fits your use case.
The primary company_jobs endpoint returns a company's open roles with title, URL, location(s), department and the ATS source — the authoritative postings, before they are syndicated to job boards. You can also fetch a single job_detail, search across companies and list the supported providers. It is built for recruiting tools, labor-market analysts and sales-intelligence teams that want first-source hiring data without scraping dozens of separate career pages. One ReefAPI key, one shared credit pool, the standard envelope.
The four providers, what to pass as company, and what each id looks like
The providers action returns exactly four keys and they do not behave the same way. Three of them serve one company's board; the fourth is a cross-company index and is the only one the search action accepts. Job ids are shaped differently per provider and mean nothing outside their own board. The table was measured on greenhouse/gitlab, lever/spotify, ashby/ramp and a workable search for 'python engineer'.
| ats | What to pass as company | jobs[].id shape | What we measured |
|---|---|---|---|
| greenhouse | The board token from the job-board URL, e.g. 'gitlab' | 10-digit numeric string: 8503792002, 8556658002 | company_jobs returned total 219. requisition_id is filled ('6263'); compensation was null. |
| lever | The org slug, e.g. 'spotify' | Lowercase UUID: a0fa7da3-4c3c-4fa2-97bd-7d6eb01eb9e5 | total 90. employment_type 'full_time' and team ('Advertising R&D') filled; compensation null. |
| ashby | The org slug, e.g. 'ramp' | Lowercase UUID: 34413f8d-26bf-4bbc-8ade-eb309a0e2245 | total 134, and the only provider that returned pay: compensation{} plus salary{min:211400, max:290600, currency:'USD', source:'structured'}. |
| workable | Use the search action rather than company_jobs | 22-character base62 code: oUJx9skkD6SnA9zEFabpod | search 'python engineer' reported total 2226. company_jobs against Workable account slugs returned ok:true with total 0 in every test. |
| posted_date | (all providers) | ISO-8601, but the flavor differs | Greenhouse carries a US-Eastern offset ('2026-04-17T05:58:03-04:00'), Lever and Ashby are UTC with sub-second precision, Workable ends in 'Z'. Normalize before sorting across boards. |
| count vs total | (all providers) | Two different numbers | count is the rows in this response, total is the size of the whole board or result set. limit=3 on gitlab gave count 3 and total 219. |
| company echo | (all providers) | Not always what you passed | Greenhouse echoes the board's display name ('GitLab'); Lever, Ashby and Workable echo the lowercase slug. company_slug always holds the slug. |
| bad values | ats / company | Rejected differently | An ats outside the four is INVALID_PARAM with the allowed list attached. An unknown board token is NOT_FOUND. Passing 'greenhouse' to search is INVALID_PARAM, because only workable aggregates across companies. |
Job ids do not collide across providers, and they are not portable either. A Lever UUID sent with ats=greenhouse returned NOT_FOUND, and a Greenhouse numeric id sent with ats=lever did the same. Keep the (ats, company, id) triple together as your key.
Real request and response JSON
Captured from the indexed primary action, search, on .
{
"method": "POST",
"url": "https://api.reefapi.com/ats-jobs/v1/search",
"headers": {
"x-api-key": "$REEF_KEY",
"content-type": "application/json"
},
"body": {
"query": "python engineer",
"ats": "workable"
}
}{
"ok": true,
"meta": {
"api": "ats-jobs",
"endpoint": "search",
"mode": "live",
"latency_ms": 1931.6,
"record_count": 50,
"bytes": 421776,
"cache_hit": false,
"completeness_pct": 93.7,
"ats": "workable",
"total": 2201,
"requests": 3
},
"data": {
"jobs": [
{
"id": "3xbmgvn389SGfSwERYGABM",
"title": "Junior Python Engineer (f/m/d)",
"url": "https://jobs.workable.com/view/3xbmgvn389SGfSwERYGABM/hybrid-junior-python-engineer-(f%2Fm%2Fd)-in-vienna-at-adverity",
"ats_source": "workable",
"company": "Adverity",
"location": "Vienna, Vienna, Austria",
"locations": [
"Vienna, Vienna, Austria"
],
"department": "Engineering",
"team": null,
"employment_type": "full_time",
"remote": false,
"workplace_type": "hybrid",
"posted_date": "[redacted-phone]T14:52:10.751Z",
"updated_date": "[redacted-phone]T14:52:10.751Z",
"requisition_id": null,
"apply_url": null,
"compensation": null,
"description_html": "<p>We are looking for a curious and driven<strong> Junior Python Engineer</strong> to join our product organisation. You'll help build features that genuinely solve problems for our users, working alongside experienced colleagues and mentors from day one.</p><p>This is a full-time role based in Vienna. We ask you to be in the office three times per week, as we value in-person collaboration and see real benefit in it.</p><h3><strong>Some of the things you'll work on:</strong></h3><ul><li>Join a team and work closely with colleagues, mentors, and team leads.</li><li>Get up to speed on our engine",
"description_text": "We are looking for a curious and driven Junior Python Engineer to join our product organisation. You'll help build features that genuinely solve problems for our users, working alongside experienced colleagues and mentors from day one.\nThis is a full-time role based in Vienna. We ask you to be in the office three times per week, as we value in-person collaboration and see real benefit in it.\nSome of the things you'll work on:\nJoin a team and work closely with colleagues, mentors, and team leads.\nGet up to speed on our engineering practices and product landscape through your first projects.\nBui",
"company_slug": "adverity",
"location_parsed": {
"remote": false,
"city": "Vienna",
"region": "Austria"
},
"is_new": true,
"is_active": true
},
{
"id": "ej9PWCnmQ92jUWWvcendDF",
"title": "Senior Python Engineer - Data & ML Systems",
"url": "https://jobs.workable.com/view/ej9PWCnmQ92jUWWvcendDF/remote-senior-python-engineer---data-%26-ml-systems-in-ireland-at-finclude",
"ats_source": "workable",
"company": "Finclude",
"location": "Ireland",
"locations": [
"TELECOMMUTE",
"Ireland"
],
"department": "Technology",
"team": null,
"employment_type": "full_time",
"remote": true,
"workplace_type": "remote",
"posted_date": "[redacted-phone]T13:32:37.924Z",
"updated_date": "[redacted-phone]T13:32:37.924Z",
"requisition_id": null,
"apply_url": null,
"compensation": null,
"description_html": "<h3>About us</h3><p>Finclude is a Dublin-based fintech pioneer revolutionizing how creditworthiness is assessed across Europe. We are bridging the gap for over 200 million people who lack traditional credit access by replacing outdated history-based models with real-time, AI-driven transactional intelligence. </p><p></p><h3>What you’ll be working on</h3><p>You will work on a Python-based system responsible for:</p><ul><li>Processing and transforming transactional data from multiple banking sources</li><li>Designing data pipelines tailored to real-world data inconsistencies and edge cases</li><",
"description_text": "About us\nFinclude is a Dublin-based fintech pioneer revolutionizing how creditworthiness is assessed across Europe. We are bridging the gap for over 200 million people who lack traditional credit access by replacing outdated history-based models with real-time, AI-driven transactional intelligence. \n\nWhat you’ll be working on\nYou will work on a Python-based system responsible for:\nProcessing and transforming transactional data from multiple banking sources\nDesigning data pipelines tailored to real-world data inconsistencies and edge cases\nBuilding systems for data normalization, categorization",
"company_slug": "finclude",
"location_parsed": {
"remote": false,
"city": "Ireland",
"country": "Ireland"
},
"is_new": false,
"is_active": true
},
{
"id": "tHHAVj4LfDiQ274AduhhMQ",
"title": "Senior Python Engineer - Data & ML Systems",
"url": "https://jobs.workable.com/view/tHHAVj4LfDiQ274AduhhMQ/remote-senior-python-engineer---data-%26-ml-systems-in-greece-at-finclude",
"ats_source": "workable",
"company": "Finclude",
"location": "Greece",
"locations": [
"TELECOMMUTE",
"Greece"
],
"department": "Technology",
"team": null,
"employment_type": "full_time",
"remote": true,
"workplace_type": "remote",
"posted_date": "[redacted-phone]T13:32:37.712Z",
"updated_date": "[redacted-phone]T13:32:37.712Z",
"requisition_id": null,
"apply_url": null,
"compensation": null,
"description_html": "<h3>About us</h3><p>Finclude is a Dublin-based fintech pioneer revolutionizing how creditworthiness is assessed across Europe. We are bridging the gap for over 200 million people who lack traditional credit access by replacing outdated history-based models with real-time, AI-driven transactional intelligence. </p><p></p><h3>What you’ll be working on</h3><p>You will work on a Python-based system responsible for:</p><ul><li>Processing and transforming transactional data from multiple banking sources</li><li>Designing data pipelines tailored to real-world data inconsistencies and edge cases</li><",
"description_text": "About us\nFinclude is a Dublin-based fintech pioneer revolutionizing how creditworthiness is assessed across Europe. We are bridging the gap for over 200 million people who lack traditional credit access by replacing outdated history-based models with real-time, AI-driven transactional intelligence. \n\nWhat you’ll be working on\nYou will work on a Python-based system responsible for:\nProcessing and transforming transactional data from multiple banking sources\nDesigning data pipelines tailored to real-world data inconsistencies and edge cases\nBuilding systems for data normalization, categorization",
"company_slug": "finclude",
"location_parsed": {
"remote": false,
"city": "Greece"
},
"is_new": false,
"is_active": true
}
],
"count": 50,
"total": 2201,
"query": "python engineer",
"ats_source": "workable"
}
}What the Company Job Boards API does
| Action | Description | Concrete use case | Key params |
|---|---|---|---|
| company_jobs | all open jobs for a board → {jobs[]{id,title,url,company,location,locations,...}, count, total, company, ats_source}. `ats`=greenhouse|lever|ashby|workable + `company` (board token/subdomain). | Recruiting teams call company_jobs to get all open jobs for a board → {jobs[]{id,title,url,company,location,locations,...}, count, tota…. | ats, company, limit, cursor, page |
| job_detail | one fully-populated job (description + requirements/benefits) by `ats` + `company` + `job_id`. | Labor-market analysts call job_detail to get one fully-populated job (description + requirements/benefits) by `ats` + `company` + `job_id`.. | ats, company, job_id |
| search | cross-company job search over the Workable aggregator (170K jobs) → jobs[]. `query` + (default `ats`=workable). paginated. | Job boards call search to get cross-company job search over the Workable aggregator (170K jobs) → jobs[]. | query, ats, limit, cursor, page |
| providers | the supported ATS providers + their capabilities | Sales intelligence teams call providers to get the supported ATS providers + their capabilities. | none |
Call search from your stack
curl -X POST https://api.reefapi.com/ats-jobs/v1/search \
-H "x-api-key: $REEF_KEY" \
-H "content-type: application/json" \
-d '{"query":"python engineer","ats":"workable"}'import requests
r = requests.post(
"https://api.reefapi.com/ats-jobs/v1/search",
headers={"x-api-key": REEF_KEY},
json={
"query": "python engineer",
"ats": "workable"
},
)
print(r.json()["data"])const res = await fetch("https://api.reefapi.com/ats-jobs/v1/search", {
method: "POST",
headers: {
"x-api-key": process.env.REEF_KEY,
"content-type": "application/json",
},
body: JSON.stringify({
"query": "python engineer",
"ats": "workable"
}),
});
const { ok, data, meta, error } = await res.json();Ask your MCP-connected assistant: call reefapi.ats-jobs.search with {"query":"python engineer","ats":"workable"}.Who uses this API and why
- Sales-intelligence teams call company_jobs to detect hiring signals (a company opening 10 sales roles) from the source ATS.
- Job boards run company_jobs across a company list to aggregate fresh, first-source postings.
- Labor-market analysts use search to measure open-role volume by department and location over time.
Questions developers ask before integrating
Which ATS providers are actually supported?
The providers action returns four keys, alphabetically: ashby, greenhouse, lever, workable. Anything else is rejected at the parameter level with INVALID_PARAM and the allowed list in error.detail.allowed, so a typo in the provider name never turns into a silently empty result.
Is a job id unique across providers?
No, and the shapes are different enough that you can usually tell them apart: Greenhouse ids are 10-digit numeric strings, Lever and Ashby both use lowercase UUIDs, Workable uses a 22-character base62 code. An id is only meaningful inside one board, which we confirmed both ways: a Lever UUID with ats=greenhouse and a Greenhouse id with ats=lever both returned NOT_FOUND. Store the (ats, company, id) triple as the key.
Which provider gives me salary data?
Ashby, in our measurements. A Ramp job came back with a compensation object holding tiers and components, plus a normalized salary{min:211400, max:290600, currency:'USD', source:'structured'}. compensation was null on every Greenhouse, Lever and Workable job we read, so if pay is the point of your query, start with Ashby boards.
Why does search reject ats=greenhouse?
Only Workable runs a public cross-company index, so search is a Workable-only action and ats defaults to 'workable'. Passing greenhouse, lever or ashby returns INVALID_PARAM with a message pointing you at company_jobs. To search across Greenhouse boards you have to enumerate the board tokens yourself and call company_jobs per company.
What is the difference between count and total?
count is how many rows are in this response, total is the size of the underlying set. Asking gitlab for 3 jobs returned count 3 and total 219; spotify reported total 90 and ramp 134. On search, total is the size of the query's result set, 2226 for 'python engineer'. Use total to decide whether to page, not to size your array.
A job says remote: true but location_parsed.remote is false. Which is right?
Both, because they answer different questions. The top-level remote flag reflects whether the posting offers a remote option at all, which is why a Ramp job listed in New York with 'Remote (US)' among its locations reads remote:true. location_parsed.remote reflects only the primary location string, so it stays false unless that string itself says remote. workplace_type ('remote', 'hybrid', 'onsite') is usually the field you actually want.
Can I list one company's jobs on Workable?
Not reliably today. company_jobs with ats=workable and account slugs taken straight from search results returned ok:true with total 0 and an empty jobs array, and job_detail for those same ids returned NOT_FOUND. Treat Workable as a search-only surface, and use company_jobs for greenhouse, lever and ashby, where we verified live boards of 219, 90 and 134 jobs.
What do is_new and requisition_id mean?
is_new was true on the Workable rows posted within the previous week and false on the older Greenhouse, Lever and Ashby postings we read, so it is a freshness hint rather than a state you can rely on. requisition_id is the employer's own internal number, and only Greenhouse supplied it ('6263'); it is null on the other three providers.
What is the Company Job Boards API?
Company Job Boards API is a ReefAPI endpoint group for jobs straight from company career pages. It returns live JSON through POST requests under /ats-jobs/v1.
Is the Company Job Boards API free to try?
Yes. ReefAPI starts with 1,000 free credits, no card required. Company Job Boards calls use the same shared credit balance as every other ReefAPI engine.
Do I need a Company Job Boards login or account?
No login to Company Job Boards is needed for the API response. You call ReefAPI with your x-api-key header, and the playground can run live examples before you create a production key.
How fresh is the Company Job Boards data?
The page example is captured from a live company_jobs call, and production requests fetch live data through ReefAPI rather than a static sample.
How many credits does the Company Job Boards API use?
Company Job Boards actions currently cost 1 credit per successful call. Failed or blocked calls are free, and all APIs draw from one credit pool.
Can I call Company Job Boards from an AI assistant or MCP client?
Yes. Connect ReefAPI once through MCP and your assistant can call ats-jobs actions with the same key, credit pool and JSON envelope used by normal REST requests.