{"slug": "pulling-open-jobs-from-greenhouse-lever-and-ashby-without-scraping-html", "title": "Pulling open jobs from Greenhouse, Lever and Ashby without scraping HTML", "summary": "A developer built an Apify actor that pulls open job listings directly from the public JSON feeds of applicant tracking systems including Greenhouse, Lever, Ashby, Workable, SmartRecruiters and Recruitee, avoiding HTML scraping of JavaScript-heavy career pages. The actor accepts company board slugs or careers URLs and supports filtering by keywords, location, department, employment type, remote status, posting date, salary and skills, plus a monitor mode that returns only new or changed jobs. The developer notes that field sets differ across vendors — Greenhouse's board feed omits remote, workplace type and employment type, Lever lacks an updatedAt field, and most boards publish no salary — so the seniority field is only a title-based heuristic.", "body_md": "I built an Apify actor that reads job boards from several applicant tracking systems (ATS), so keep that bias in mind. An AI assistant helped me draft this article, and I reviewed and edited it. The free route comes first, because for a handful of companies a short script is enough.\n\nRecruiters, job-board builders and sales teams that watch hiring signals all want the same thing: a clean list of open roles at specific companies. Career pages are often JavaScript-heavy, but most companies run their hiring on an ATS that exposes a public JSON feed so the career page can render. You do not need to scrape HTML. You need the company's board slug, which is the last part of the careers URL (for example `boards.greenhouse.io/airbnb`, `jobs.lever.co/spotify`, `jobs.ashbyhq.com/ramp`).\n\n``` python\nimport requests\n\ndef greenhouse(slug):\n    r = requests.get(f\"https://boards-api.greenhouse.io/v1/boards/{slug}/jobs\", timeout=30)\n    r.raise_for_status()\n    return [(j[\"title\"], j[\"location\"][\"name\"], j[\"absolute_url\"]) for j in r.json()[\"jobs\"]]\n\ndef lever(slug):\n    r = requests.get(f\"https://api.lever.co/v0/postings/{slug}\", params={\"mode\": \"json\"}, timeout=30)\n    r.raise_for_status()\n    return [(j[\"text\"], j[\"categories\"].get(\"location\"), j[\"hostedUrl\"]) for j in r.json()]\n\ndef ashby(slug):\n    r = requests.get(f\"https://api.ashbyhq.com/posting-api/job-board/{slug}\", timeout=30)\n    r.raise_for_status()\n    return [(j[\"title\"], j[\"location\"], j[\"jobUrl\"]) for j in r.json()[\"jobs\"]]\n\nfor name, fn, slug in [(\"greenhouse\", greenhouse, \"airbnb\"),\n                       (\"lever\", lever, \"spotify\"),\n                       (\"ashby\", ashby, \"ramp\")]:\n    jobs = fn(slug)\n    print(name, slug, len(jobs), jobs[0])\n```\n\nI checked these against the live endpoints. Greenhouse returns `jobs` with `title`, `location.name`, `absolute_url`, `updated_at` and `first_published`. Lever returns a list with `text`, `categories` (including `location`, `department`, `commitment`), `hostedUrl`, `applyUrl` and `createdAt`. Ashby returns `jobs` with `title`, `department`, `location`, `isRemote`, `workplaceType`, `publishedAt` and `jobUrl`. Field sets differ between vendors and can change, so check each response's keys before depending on one. For descriptions, Greenhouse needs `?content=true` on the request.\n\nThe actor covers Greenhouse, Lever, Ashby, Workable, SmartRecruiters and Recruitee, plus a schema.org JobPosting fallback for single career pages. You can paste company slugs or careers URLs. The input below is the README's real test input:\n\n```\n{\n  \"companies\": [\n    \"airbnb\",\n    \"https://jobs.lever.co/spotify\",\n    \"https://jobs.ashbyhq.com/ramp\"\n  ],\n  \"onlyNewSinceLastRun\": false,\n  \"maxJobsPerCompany\": 3,\n  \"maxJobsTotal\": 3,\n  \"includeDescription\": false\n}\n```\n\nOther real fields include `keywords`, `excludeKeywords`, `locations`, `departments`, `employmentTypes`, `remoteOnly`, `workModels`, `postedAfter`, `minSalary`, `skillsInclude` and `monitorMode` (return only new or changed jobs, optionally also removed ones).\n\nExcerpt of real sample output (description removed, a few fields kept):\n\n```\n{\n  \"company\": \"Spotify\",\n  \"ats\": \"lever\",\n  \"title\": \"Senior Product Manager, Consumer - Audiobooks\",\n  \"department\": \"Product\",\n  \"location\": \"London\",\n  \"remote\": false,\n  \"workplaceType\": \"hybrid\",\n  \"employmentType\": \"FULL_TIME\",\n  \"postedAt\": \"2026-10-12T13:42:44.230Z\",\n  \"jobUrl\": \"https://jobs.lever.co/spotify/ddc10181-6ecc-4dfc-9d02-2c5ce820cfdd\",\n  \"salaryMin\": null,\n  \"seniority\": \"senior\"\n}\n```\n\nThe Greenhouse row in the same file shows the vendor differences: Airbnb's \"Senior Software Engineer, Payments Transaction Risk\" has `remote`, `workplaceType` and `employmentType` all `null`, because Greenhouse's board feed does not carry them.\n\n`remote: true` only when the location says remote), Lever has no `updatedAt`, and most boards publish no salary.` seniority` is a heuristic from the title only. Treat it as a best guess.\nFor a few boards, run the script. For a watchlist with filters and a daily new-jobs feed, the actor is here: [https://apify.com/transparent_meteorite/ats-open-roles](https://apify.com/transparent_meteorite/ats-open-roles)\n\n*Disclosure again: I built the actor, and an AI assistant helped draft this article. I reviewed it and checked the three endpoints before writing the snippet.*", "url": "https://wpnews.pro/news/pulling-open-jobs-from-greenhouse-lever-and-ashby-without-scraping-html", "canonical_source": "https://dev.to/tmeteordata/pulling-open-jobs-from-greenhouse-lever-and-ashby-without-scraping-html-4a1o", "published_at": "2026-10-24 01:32:36+00:00", "updated_at": "2026-10-24 01:41:55.167228+00:00", "lang": "en", "topics": ["developer-tools", "ai-tools"], "entities": ["Apify", "Greenhouse", "Lever", "Ashby", "Workable", "SmartRecruiters", "Recruitee", "Airbnb"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/pulling-open-jobs-from-greenhouse-lever-and-ashby-without-scraping-html", "markdown": "https://wpnews.pro/news/pulling-open-jobs-from-greenhouse-lever-and-ashby-without-scraping-html.md", "text": "https://wpnews.pro/news/pulling-open-jobs-from-greenhouse-lever-and-ashby-without-scraping-html.txt", "jsonld": "https://wpnews.pro/news/pulling-open-jobs-from-greenhouse-lever-and-ashby-without-scraping-html.jsonld"}}