Greenhouse Jobs API - Career Page Scraper & Apply Links
Pricing
from $2.00 / 1,000 job results
Greenhouse Jobs API - Career Page Scraper & Apply Links
Greenhouse jobs API and career page scraper. Read live postings from any Greenhouse job board or careers page, or search thousands of known Greenhouse ATS boards in one run. Full descriptions, departments, locations and direct apply links as JSON.
Pricing
from $2.00 / 1,000 job results
Rating
0.0
(0)
Developer
Sulle H
Maintained by CommunityActor stats
0
Bookmarked
3
Total users
2
Monthly active users
9 days ago
Last modified
Categories
Share
Greenhouse Jobs Scraper — Every Open Role, With Descriptions & Apply Links
Give it a company and get back every job currently open on that company's Greenhouse board — full descriptions, departments, offices, locations, requisition ids and direct apply links, as structured JSON.
{ "boards": ["databricks", "stripe", "figma"] }
No login, no cookies, no proxies, no API key. Greenhouse publishes this data openly; this Actor reads it, normalizes it and hands it to you in one call.
What you get
Every row has the same shape:
| Field | Description |
|---|---|
company / companySlug | Company name and its Greenhouse board slug |
jobId | Greenhouse job id — stable, so it works as a dedupe key across runs |
title | Job title |
location | Location string as shown on the board |
offices | Structured office list, when the company defines offices |
departments | Department or team |
firstPublishedAt | When the role first went live |
updatedAt | Last modification timestamp |
applyUrl | Direct application link on the company's own board |
requisitionId | Internal requisition id, when the company exposes one |
descriptionText | Full job description as plain text |
scrapedAt | When this row was read |
Input
{"boards": ["databricks", "https://job-boards.greenhouse.io/figma"],"titleFilter": "engineer","locationFilter": "remote","includeDescription": true,"maxJobsPerBoard": 200}
| Option | What it does |
|---|---|
boards (required) | One entry per company. Bare slugs and full board URLs both work. |
titleFilter | Case-insensitive substring match on the job title. |
locationFilter | Case-insensitive substring match on the location. |
includeDescription | Set false for a faster, lighter run when you only need titles and links. |
maxJobsPerBoard | Cap the rows per company. Defaults to 100 — a board like Databricks has 800+ open roles and this Actor bills per row. |
Finding a board slug
Open the company's careers page and look at the URL:
| URL you see | Slug |
|---|---|
job-boards.greenhouse.io/databricks | databricks |
boards.greenhouse.io/stripe | stripe |
careers page with ?for=acme in the source | acme |
Embedded boards work too — many companies frame Greenhouse into their own site, and the slug is still in the page source. You can also paste the full URL straight into boards and let the Actor pull the slug out.
What people use it for
Tracking one employer. Watch a company you want to work for, or a competitor you want to understand. Run it on a schedule and diff the jobId set.
Sourcing and recruiting. Pull open roles across a list of target companies, filter by title, and work from a clean spreadsheet instead of twenty browser tabs.
Market research. Departments and office locations tell you where a company is investing before any announcement does.
Feeding a job board. Every row carries applyUrl pointing at the employer, so your users apply directly rather than through a middleman.
Scale beyond one ATS
This Actor is deliberately narrow: you name the Greenhouse boards, it reads them. If you need breadth instead — search across companies without knowing who uses what — two companion Actors share the same output schema:
- Career Site Job Feed — searches a bundled registry of 40,000+ company boards across Workday, Greenhouse, SmartRecruiters, Workable, Lever, Ashby and Breezy in one run.
- New Job Alerts — the same coverage, but each run returns only postings that appeared since the previous run.
Or stay narrow and switch platform. These work the same way this Actor does, one applicant tracking system each, and emit the same rows:
- SmartRecruiters Jobs Scraper — 4,334 known SmartRecruiters boards, searchable without naming a company.
- Workable Jobs Scraper — 1,710 known Workable boards, full descriptions included.
- Workday Jobs Scraper — 2,658 known Workday boards; paste a careers URL and it works out the three-part key.
- Breezy HR Jobs Scraper — 2,800 known Breezy boards, and the one platform here that publishes a pay range.
What Greenhouse publishes, and what it does not
Greenhouse's board endpoint is generous compared with most applicant tracking systems, which is why this Actor can return full descriptions where others cannot:
| Available | Not available |
|---|---|
| Job title, location, offices | Salary as a structured field |
| Departments and sub-departments | Applicant counts or hiring stage |
| Full description as HTML | Recruiter or hiring-manager contacts |
| First published and last updated timestamps | Anything behind the employer's login |
| Requisition id, where the company sets one | Candidate data of any kind |
Descriptions arrive as entity-escaped HTML. This Actor decodes them twice — Greenhouse double-encodes some fields — strips tags, and returns readable plain text, so you are not writing a parser.
Reading the data
jobId is stable. It does not change while a posting is open, so companySlug:jobId is a safe
key for detecting what opened and closed between runs.
offices and departments are arrays. A role can sit in several offices; a role can belong to
one department with a parent above it. Both come through as published rather than flattened to a
single string.
firstPublishedAt is when the role went live, and updatedAt moves when the employer edits it.
For "what is new" logic, use firstPublishedAt — an edit should not make an old role look fresh.
descriptionText is capped at 20,000 characters. Long postings are truncated rather than
returned in full, which keeps rows a predictable size.
Recipes
Watch one employer. Run daily with a single slug, keep the jobId set, and diff it. New ids are
openings; missing ids are roles that closed.
Build a target list. Pass twenty company slugs, set titleFilter to the role you sell into, and
you have a list of employers hiring for it right now, each with an apply link you can verify.
Feed a niche job board. Pick the companies your audience cares about, run hourly, and publish
straight from the dataset. Because every row carries the employer's own applyUrl, your users
apply at the source.
Research a company's direction. Departments and office locations across a board show where headcount is going before any announcement does.
Finding slugs at scale
This Actor needs you to name the boards. If you do not know who uses Greenhouse, the companion
Career Site Job Feed ships a registry of
5,751 known Greenhouse boards — run it with providers: ["greenhouse"] and the companySlug
column is your list.
Limits worth knowing
- Boards are read in sequence. A dozen companies is seconds; several hundred is minutes. For breadth across thousands of employers, use the feed Actor instead.
- A wrong slug is not fatal. It lands in
FAILED_BOARDSand the rest of the run continues. - Embedded boards work, but you need the slug from the page source (
?for=acme), not the URL of the page you are looking at. - No login, no proxies. If a company's board is private, no scraper can see it, including this one.
Performance
A large board (800+ roles) completes in well under a minute with descriptions on. Boards are read in sequence, so a run over a dozen companies is still a matter of seconds.
Use it as an API
Most people who rely on this Actor never open the Apify console after the first run — they call it from their own code and read the rows straight back. One request in, job rows out, no polling and no dataset id to chase:
curl -X POST "https://api.apify.com/v2/acts/starbright_overlap~greenhouse-jobs-scraper/run-sync-get-dataset-items?token=YOUR_TOKEN" \-H "Content-Type: application/json" \-d '{"boards": ["databricks", "stripe"], "maxJobsPerBoard": 100}'
import requestsrows = requests.post("https://api.apify.com/v2/acts/starbright_overlap~greenhouse-jobs-scraper/run-sync-get-dataset-items",params={"token": "YOUR_TOKEN"},json={"boards": ["databricks", "stripe"], "maxJobsPerBoard": 100},timeout=300,).json()for r in rows:print(r["company"], "—", r["title"], "—", r["applyUrl"])
const rows = await (await fetch('https://api.apify.com/v2/acts/starbright_overlap~greenhouse-jobs-scraper/run-sync-get-dataset-items?token=YOUR_TOKEN',{ method: 'POST', headers: { 'Content-Type': 'application/json' },body: JSON.stringify({"boards": ["databricks", "stripe"], "maxJobsPerBoard": 100}) },)).json();
Your token is at Settings → Integrations in the Apify console. Keep maxJobs set to what you
actually need — it is the cap that decides what the call costs.
Running it on a schedule instead
If you want the rows to arrive without asking, add a Schedule from the Actor page (Actions →
Schedule) and point a webhook at your endpoint. A daily schedule plus the postedWithinDays filter
is the usual setup for a job board or an alerting pipeline.
Pricing
Pay per result — you are charged per job row delivered. A board that fails or returns nothing costs you nothing. Platform usage is included rather than billed on top, so the per-result price is the whole price.
Notes
- Bad or dead slugs never abort the run. They are collected into a
FAILED_BOARDSrecord in the key-value store, so you can see exactly which ones failed and why. jobIdis stable per posting, which makescompanySlug:jobIda reliable key for detecting new and removed roles between runs.- Set
includeDescription: falsewhen you only need titles and links — it is several times faster and cheaper.
Frequently asked
Is there a free Greenhouse API? Greenhouse's board endpoint is public and unauthenticated, so yes, technically. This Actor exists because reading it well is fiddly: descriptions arrive as escaped HTML, offices and departments are nested, and slug discovery from a careers page is not obvious. You get clean plain text and a flat schema instead.
Can I scrape several companies at once?
Yes — put as many slugs in boards as you like. Each is read independently, and one failing does not stop the others.
How do I get only engineering roles?
Set titleFilter to engineer. It is a case-insensitive substring match, so it catches "Software Engineer", "Engineering Manager" and "Site Reliability Engineer".
Do I get salary data?
Only when the employer writes it into the job description, and only with includeDescription: true. Greenhouse's board endpoint has no structured salary field, so this Actor does not invent one.
How do I find new postings since yesterday?
Either diff jobId between runs yourself, or use New Job Alerts, which keeps that memory for you across 40,000+ boards.
What if the company is not on Greenhouse? Then this Actor cannot see it. Career Site Job Feed covers seven applicant tracking systems and can tell you which one a company uses.
Does it work with embedded boards?
Yes. If a company frames Greenhouse into its own careers page, find the slug in the page source (?for=acme) and pass that.