Greenhouse Jobs Scraper: Job Board to JSON & CSV
Pricing
from $2.00 / 1,000 results
Greenhouse Jobs Scraper: Job Board to JSON & CSV
Export any company's Greenhouse job board through the official public Job Board API: job ID, title, location, company, apply URL and publish dates. Several companies per run, and scheduled monitoring that lists only new postings. No login, no proxies, no LLM.
Pricing
from $2.00 / 1,000 results
Rating
0.0
(0)
Developer
Turgay NANTA
Maintained by CommunityActor stats
0
Bookmarked
1
Total users
0
Monthly active users
23 days ago
Last modified
Categories
Share
Greenhouse Jobs Scraper: Export Any Company's Greenhouse Job Board to JSON, CSV or Excel
Thousands of companies publish their open positions on a Greenhouse job board. Checking those careers pages by hand every week is slow, copying postings into a spreadsheet is error-prone, and you usually notice a new opening days after it went live.
This Actor reads any company's public Greenhouse job board through Greenhouse's official Job Board API and returns every open posting as clean, de-duplicated JSON: job ID, title, location, company name, canonical apply URL, first-published date and last-updated date. Add several companies in one run, and turn on monitoring to get only the postings that are new since your last run.
- Who it is for: recruiters and sourcers, B2B sales teams watching hiring signals, niche job board operators, talent and market researchers, job seekers tracking target companies, and AI agents.
- What it costs: about $0.04 for 20 postings and about $1.00 for 500 ($0.0001 per run plus $0.002 per posting). See Pricing.
- One click: the default board token
striperuns as is, so you see real output before you change anything. - No login, no proxies, no HTML parsing: the data comes from Greenhouse's public, key-free board API.
Greenhouse boards only. This Actor reads job boards hosted on Greenhouse. It does not search LinkedIn, Indeed or other job sites, and it does not read careers pages that run on a different applicant tracking system.
What one output record looks like
This record comes from a live run on the figma board:
{"id": "5426468004","title": "Account Executive, Enterprise","url": "https://boards.greenhouse.io/figma/jobs/5426468004?gh_jid=5426468004","location": "San Francisco, CA • New York, NY • United States","seller": "Figma","updated_at": "2026-07-22T05:37:08-04:00","first_published": "2025-01-28T18:57:29-05:00"}
seller holds the company name. A few shared columns that stay empty for Greenhouse data are listed in the Output section.
At a glance
| Input | One or more Greenhouse board tokens, comma-separated (stripe,figma) |
| Output | One record per open job posting + a _summary row (+ a _changes row in monitor mode) |
| Key fields | id, title, location, seller (company), url (apply link), first_published, updated_at |
| Source | Greenhouse Job Board API (boards-api.greenhouse.io), public and key-free |
| Volume | Up to 500 postings per run, across all companies in the run |
| De-duplication | By Greenhouse job ID |
| Monitoring | Optional: lists job IDs that are new since the previous run with the same companies |
| LLM | None; output is deterministic |
| Runs on | Apify Console, API, schedules, n8n, Make, Zapier, AI agents via MCP |
Quick start (1 minute)
- Click Start. The default board token
striperuns as is and returns 20 postings. - Open the Dataset tab when the run finishes.
- Export as CSV, Excel, JSON or HTML, or fetch the items through the API.
Then enter your own companies:
{"query": "figma,airbnb,stripe","maxResults": 500}
How to find a company's board token
The board token is the short company identifier Greenhouse uses in URLs:
- Open one of the company's job postings. If the address contains
boards.greenhouse.io/<token>orjob-boards.greenhouse.io/<token>, the part after the slash is the token. - If the company shows jobs on its own website, look at the apply link or the page source for
greenhouse.io/<token>orfor=<token>. - Tokens are usually the company name in lowercase (
stripe,figma), but not always, so check before you schedule.
An unknown token returns no postings; it does not fail the run.
Why people use it
- Official API instead of scraping HTML. Greenhouse publishes open jobs through a public board API. Reading it directly means no browser, no proxies and no breakage when a careers page changes its design.
- Many companies in one run. Put a list of board tokens into one field. The output is one table, with the company name on every row.
- Clean, stable IDs. Every posting keeps its Greenhouse job ID, so you can join runs over time and import into a database without duplicates.
- Canonical apply URLs. Tracking parameters such as
utm_*,ref,fbclidandgclidare removed; the Greenhousegh_jidjob parameter is kept so the link still opens the right posting. - Dates you can sort by.
first_publishedtells you how long a role has been open;updated_atshows recent edits. - New-posting alerts. With
monitoron and a schedule, each run lists only the postings that were not there the previous time.
Use cases by profession
Recruiters and sourcers
Track open roles at the companies you recruit from or compete with. A new senior opening at a competitor often means a team change; a posting that stays open for months (old first_published) can point to a hard-to-fill role where your candidates are welcome.
B2B sales and account-based marketing
Hiring is a buying signal. A company that posts three data engineering roles is investing in data; one hiring its first security lead is about to buy security tooling. Run your target account list daily with monitor on and route new postings with relevant titles to the account owner.
Niche job boards and communities
If you run a job board for a specific field or region, keep a list of employer board tokens and refresh them on a schedule. You get titles, locations and direct apply links for the companies you have chosen, in a format you can load straight into your site's database.
Talent and market researchers
Compare hiring volume across companies, count roles per location, and see how long postings stay open. With scheduled runs saved over time, you can build a hiring timeline per company.
Job seekers
Follow your shortlist of target companies without checking their careers pages every morning. A daily monitored run shows only new postings; connect it to email or Slack and you hear about openings on the day they appear.
Data teams and AI agents
The output is flat and typed, so it loads into a warehouse or spreadsheet without cleanup. An AI assistant can call the Actor through the Apify MCP server with a board token and reason over the open roles.
How it works
- Reads your token list. The
queryfield is split on commas, trimmed and lowercased. - Calls the Greenhouse Job Board API for each token:
GET https://boards-api.greenhouse.io/v1/boards/<token>/jobs. The list endpoint is called without full job descriptions, which keeps runs fast. - Retries transient errors. If a request fails or returns an unexpected status, it is retried up to three times with a growing pause. A
404(unknown token) is treated as "no postings" and the run continues with the next company. - Flattens each posting into a stable record: ID, title, apply URL, location name, company name, first-published and updated dates.
- Stops at
maxResults. The limit applies to the whole run across all companies, in the order you listed them. - Normalizes and de-duplicates by job ID and canonicalizes URLs.
- Optionally enriches each record with a
domainand acompletenessscore. - Optionally compares with the previous run of the same query and writes a
_changesrow with the new job IDs. - Writes a
_summaryrow with the totals.
Input
All fields are optional. An empty input runs the default example.
| Field | Type | Default | Description |
|---|---|---|---|
query | string | stripe | Greenhouse board token, or several separated by commas (stripe,figma,airbnb). |
maxResults | integer | 20 | Maximum number of postings for the whole run (1-500). |
enrich | boolean | false | Adds domain and completeness to every record. Charged per enriched record. |
monitor | boolean | false | Compares this run with the previous monitored run of the same query and writes a _changes row with new job IDs. Charged per new posting. |
Example: full board of one company
{"query": "figma","maxResults": 500}
Example: daily new-posting alerts for a target list
{"query": "stripe,figma,airbnb","maxResults": 500,"monitor": true}
Save it as a Task and attach a daily Schedule (see Integrations).
Output
Dataset records (one per job posting)
| Field | Description |
|---|---|
id | Greenhouse job ID (string) |
title | Job title as published |
url | Apply URL, canonicalized (tracking parameters removed, gh_jid kept) |
location | Location name as the company entered it, for example Dublin or San Francisco, CA |
seller | Company name from Greenhouse (falls back to the board token if the name is missing) |
first_published | When the posting was first published (ISO 8601 with time zone) |
updated_at | When the posting was last updated (ISO 8601 with time zone) |
domain | With enrich: host of the apply URL, for example stripe.com or boards.greenhouse.io |
completeness | With enrich: share of six general fields that are filled (0-1); the practical maximum for job postings is 0.67 |
Shared columns that stay empty for Greenhouse data: price (null), price_text (empty), rating (null), reviews (null) and description (empty). They exist because this Actor uses the same output core as our other scrapers; drop them in your export if you do not need them. The Actor does not return salary, department, employment type or the job description text.
Summary row (last row of every run)
{"_summary": {"total_raw": 120,"total_clean": 120,"deduped": 0,"with_url": 120,"with_price": 0,"enriched": 0}}
The numbers above are illustrative; your run reports its own. If no postings are found (for example every token was unknown), the summary carries a note field.
Changes row (monitor mode only)
{"_changes": ["8172508", "8169911"]}
The list holds the job IDs that were not present in the previous monitored run of the same query. The row is written only when there is at least one new posting.
Pricing
Pay per event, no subscription. Current prices are always shown on the Pricing tab; at the time of writing they are:
| Event | When it is charged | Price |
|---|---|---|
actor-start | Once per run | $0.0001 |
result | Per clean, de-duplicated job posting | $0.002 |
enrichment | Per enriched posting (only when enrich is on) | $0.01 |
change-alert | Per new posting since the previous run (only when monitor is on) | $0.005 |
A run that finds no postings costs only the start fee.
Worked examples
| Scenario | Events | Cost |
|---|---|---|
| Default run, 20 postings | 1 start + 20 results | $0.0001 + $0.04 = $0.0401 |
| One large board, 500 postings | 1 start + 500 results | $0.0001 + $1.00 = $1.0001 |
| Five companies, 300 postings in total | 1 start + 300 results | $0.0001 + $0.60 = $0.6001 |
Same five companies with enrich | + 300 enrichments | $0.6001 + $3.00 = $3.6001 |
| First monitored run, 120 postings (baseline) | 1 start + 120 results, no change alerts | $0.0001 + $0.24 = $0.2401 |
| Later daily run, 120 postings, 2 new | 1 start + 120 results + 2 change alerts | $0.0001 + $0.24 + $0.01 = $0.2501 |
The first monitored run of a query has no previous snapshot, so it only saves the baseline and charges no change alerts. After that, only new postings are charged as change alerts; the per-posting result charge applies to every posting returned in each run.
Apify platform usage is billed by Apify according to your plan. Apify's free plan includes monthly platform credit, enough to test the Actor on real boards.
Integrations
API
curl -X POST "https://api.apify.com/v2/acts/enezli~greenhouse-jobs-scraper/run-sync-get-dataset-items?token=YOUR_TOKEN" \-H "Content-Type: application/json" \-d '{"query": "figma,stripe", "maxResults": 200}'
Python
from apify_client import ApifyClientclient = ApifyClient("YOUR_TOKEN")run = client.actor("enezli/greenhouse-jobs-scraper").call(run_input={"query": "figma,stripe", "maxResults": 200})jobs = [r for r in client.dataset(run["defaultDatasetId"]).iterate_items()if "_summary" not in r and "_changes" not in r]engineering = [j for j in jobs if "engineer" in j["title"].lower()]print(len(jobs), "open postings,", len(engineering), "engineering")
JavaScript
import { ApifyClient } from 'apify-client';const client = new ApifyClient({ token: 'YOUR_TOKEN' });const run = await client.actor('enezli/greenhouse-jobs-scraper').call({ query: 'figma', maxResults: 500 });const { items } = await client.dataset(run.defaultDatasetId).listItems();const jobs = items.filter((r) => !r._summary && !r._changes);console.log(jobs.map((j) => `${j.title} | ${j.location}`).join('\n'));
Example: hiring snapshot per company
from collections import Counterper_company = Counter(j["seller"] for j in jobs)per_location = Counter(j["location"] for j in jobs)print(per_company.most_common())print(per_location.most_common(10))
Run this on scheduled datasets saved over time and you have a simple hiring trend per company and location, without any extra service.
Schedules (scheduled new-posting monitoring)
- Set your
query, setmaxResultsabove the total number of open postings, and turn onmonitor. - Click Save as a new task.
- In Schedules, create a daily or weekly schedule and add the task.
The comparison snapshot is stored per query in a named key-value store in your own Apify account (greenhouse-jobs-scraper-monitor). Tasks with different queries keep separate snapshots and do not interfere. To get notified, add an integration to the task (webhook, Slack or email on run success) and read the _changes row.
n8n, Make, Zapier
Use the official Apify node or app: Run Task (or Run Actor), then Get dataset items. Skip rows with _summary or _changes, filter titles by keyword, and send matches to Slack, Google Sheets, Airtable or your CRM.
AI agents (MCP)
This Actor is available through the Apify MCP server, so assistants such as Claude or Cursor can call it with a board token and read back the open roles.
Tips for best results
- Verify tokens once. Run a new token with the default
maxResultsfirst. An empty result almost always means the token is wrong. - Set
maxResultsabove the board size when monitoring. The limit cuts the list; postings beyond the limit are not compared and can later show up as "new". Large employers can have hundreds of openings (Stripe's board listed 701 when we checked on 2026-09-28). - Group companies by purpose. One task per watch list (for example "Fintech accounts" and "Design tools") keeps snapshots and notifications clean.
- Keep the token list stable in monitoring tasks. The snapshot is tied to the exact query text; editing it starts a new snapshot, so the next run only saves a new baseline and reports nothing as new.
- Use
first_publishedfor freshness, notupdated_at, which changes on any edit. - Filter metadata rows (
_summary,_changes) before you import the data.
Limitations
- Greenhouse boards only. Companies on other applicant tracking systems, and job sites such as LinkedIn or Indeed, are not covered.
- You need the board token. The Actor does not discover which companies use Greenhouse.
- 500 postings per run at most. For boards larger than that, a single run returns the first 500 postings in the order the API lists them, and monitoring cannot cover the whole board.
- No job descriptions, salary, departments or employment type. The Actor calls the list endpoint without content, so these fields are not returned.
- Monitoring reports additions only. Closed or edited postings are not reported.
- Public postings only. Internal postings and jobs a company has not published to its public board are not visible.
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
Only a _summary row with a "No results" note | Wrong board token, or the company has no open jobs | Check the token in a job posting URL |
| One company missing in a multi-company run | That token is wrong, or maxResults was reached before it | Fix the token, raise maxResults, or put the company earlier in the list |
| Fewer postings than the careers page shows | maxResults is lower than the board size, or some roles are listed on a different system | Raise maxResults (up to 500) |
No _changes row on the first monitored run | The first run of a query only saves the baseline | Expected; later runs list only new postings |
A posting appears in _changes although it is old | It was beyond maxResults in the previous run | Set maxResults above the board size |
location values look inconsistent | Companies type locations freely | Normalize in your own pipeline, for example by city keyword |
FAQ
Is this a LinkedIn or Indeed jobs scraper? No. It reads public job boards hosted on Greenhouse, using Greenhouse's official Job Board API.
Do I need a Greenhouse account or API key? No. The job board API is public and does not need a key. The Actor never logs in anywhere.
Can I scrape several companies at once?
Yes. Separate board tokens with commas. The maxResults limit applies to the whole run.
Does it return the full job description? No. The Actor uses the job list endpoint without descriptions. Each record has the apply URL, which opens the full posting.
How do new-posting alerts work?
With monitor on, the Actor stores the job IDs of the run under a key derived from your query, in a named key-value store in your account. The next monitored run of the same query writes the IDs that were not there before to a _changes row. You pay $0.005 per new posting.
Why is the company field called seller?
The Actor shares an output core with our other scrapers, where seller is the generic name for the organization behind a record. For job postings it is the company name.
Does it use AI? No. Every field comes from Greenhouse's response, processed with deterministic Python.
How fresh is the data? Each run calls the live API, so you get the postings the board shows at that moment.
Is it legal? The Actor reads job postings that companies publish on purpose through a public API, without logging in. You are responsible for how you use the data under the terms and laws that apply to you.
I found a bug or need a field. Open an issue on the Issues tab and include the board token you used.
Trademarks
This Actor is not affiliated with, endorsed by, or connected to Greenhouse. All product names are trademarks of their respective owners.