Greenhouse Jobs Scraper: Job Board to JSON & CSV avatar

Greenhouse Jobs Scraper: Job Board to JSON & CSV

Pricing

from $2.00 / 1,000 results

Go to Apify Store
Greenhouse Jobs Scraper: Job Board to JSON & CSV

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

Turgay NANTA

Maintained by Community

Actor 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 stripe runs 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

InputOne or more Greenhouse board tokens, comma-separated (stripe,figma)
OutputOne record per open job posting + a _summary row (+ a _changes row in monitor mode)
Key fieldsid, title, location, seller (company), url (apply link), first_published, updated_at
SourceGreenhouse Job Board API (boards-api.greenhouse.io), public and key-free
VolumeUp to 500 postings per run, across all companies in the run
De-duplicationBy Greenhouse job ID
MonitoringOptional: lists job IDs that are new since the previous run with the same companies
LLMNone; output is deterministic
Runs onApify Console, API, schedules, n8n, Make, Zapier, AI agents via MCP

Quick start (1 minute)

  1. Click Start. The default board token stripe runs as is and returns 20 postings.
  2. Open the Dataset tab when the run finishes.
  3. 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> or job-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> or for=<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

  1. 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.
  2. 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.
  3. Clean, stable IDs. Every posting keeps its Greenhouse job ID, so you can join runs over time and import into a database without duplicates.
  4. Canonical apply URLs. Tracking parameters such as utm_*, ref, fbclid and gclid are removed; the Greenhouse gh_jid job parameter is kept so the link still opens the right posting.
  5. Dates you can sort by. first_published tells you how long a role has been open; updated_at shows recent edits.
  6. New-posting alerts. With monitor on 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

  1. Reads your token list. The query field is split on commas, trimmed and lowercased.
  2. 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.
  3. 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.
  4. Flattens each posting into a stable record: ID, title, apply URL, location name, company name, first-published and updated dates.
  5. Stops at maxResults. The limit applies to the whole run across all companies, in the order you listed them.
  6. Normalizes and de-duplicates by job ID and canonicalizes URLs.
  7. Optionally enriches each record with a domain and a completeness score.
  8. Optionally compares with the previous run of the same query and writes a _changes row with the new job IDs.
  9. Writes a _summary row with the totals.

Input

All fields are optional. An empty input runs the default example.

FieldTypeDefaultDescription
querystringstripeGreenhouse board token, or several separated by commas (stripe,figma,airbnb).
maxResultsinteger20Maximum number of postings for the whole run (1-500).
enrichbooleanfalseAdds domain and completeness to every record. Charged per enriched record.
monitorbooleanfalseCompares 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)

FieldDescription
idGreenhouse job ID (string)
titleJob title as published
urlApply URL, canonicalized (tracking parameters removed, gh_jid kept)
locationLocation name as the company entered it, for example Dublin or San Francisco, CA
sellerCompany name from Greenhouse (falls back to the board token if the name is missing)
first_publishedWhen the posting was first published (ISO 8601 with time zone)
updated_atWhen the posting was last updated (ISO 8601 with time zone)
domainWith enrich: host of the apply URL, for example stripe.com or boards.greenhouse.io
completenessWith 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:

EventWhen it is chargedPrice
actor-startOnce per run$0.0001
resultPer clean, de-duplicated job posting$0.002
enrichmentPer enriched posting (only when enrich is on)$0.01
change-alertPer 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

ScenarioEventsCost
Default run, 20 postings1 start + 20 results$0.0001 + $0.04 = $0.0401
One large board, 500 postings1 start + 500 results$0.0001 + $1.00 = $1.0001
Five companies, 300 postings in total1 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 new1 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 ApifyClient
client = 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 Counter
per_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)

  1. Set your query, set maxResults above the total number of open postings, and turn on monitor.
  2. Click Save as a new task.
  3. 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 maxResults first. An empty result almost always means the token is wrong.
  • Set maxResults above 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_published for freshness, not updated_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

SymptomLikely causeFix
Only a _summary row with a "No results" noteWrong board token, or the company has no open jobsCheck the token in a job posting URL
One company missing in a multi-company runThat token is wrong, or maxResults was reached before itFix the token, raise maxResults, or put the company earlier in the list
Fewer postings than the careers page showsmaxResults is lower than the board size, or some roles are listed on a different systemRaise maxResults (up to 500)
No _changes row on the first monitored runThe first run of a query only saves the baselineExpected; later runs list only new postings
A posting appears in _changes although it is oldIt was beyond maxResults in the previous runSet maxResults above the board size
location values look inconsistentCompanies type locations freelyNormalize 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.