Greenhouse Jobs Lookup: jobs scraper by company from $4/1k
Pricing
from $2.00 / 1,000 successful lookups
Greenhouse Jobs Lookup: jobs scraper by company from $4/1k
Greenhouse jobs scraper: live postings by company. Look up any company's live Greenhouse job board and get every open role: title, location, department, apply link. Filter by title, location, remote, or posted date. Charged once per company checked, however many roles it has open.
Pricing
from $2.00 / 1,000 successful lookups
Rating
0.0
(0)
Developer
Adrian Voss
Maintained by CommunityActor stats
1
Bookmarked
2
Total users
1
Monthly active users
3 days ago
Last modified
Categories
Share
Greenhouse Jobs Lookup: Greenhouse Jobs Scraper — Live Postings by Company
Paste a list of companies and get every open role from each one's Greenhouse job board — title, location, department, and apply link — as one row per posting, or one row per company. No API key and no HTML scraping: this reads Greenhouse's own public job board API directly. Optional filters narrow results by title, location, or posting date, and a delta mode returns only the postings that are new since your last run.
Who it's for
Sourcers and recruiters use this to pull every open Greenhouse role for a target account list in one pass instead of opening each careers page. Sales and RevOps teams watch a competitor's Greenhouse board on a schedule to catch expansion signals — a company opening five sales roles in a new region is a warmer lead than a cold one. Investors and market researchers score hiring momentum across a portfolio or a sector by reading job counts over time rather than company by company. What makes it worth automating rather than checking by hand: Greenhouse's own API returns the whole board in one response, so a company with 600 open roles costs the same single lookup as one with two.
Why this one
- Direct from Greenhouse's own API. No HTML scraping to break when a careers page gets
redesigned — this calls
boards-api.greenhouse.iothe same way Greenhouse's own careers pages do. - You pay per company, not per job. A board with 578 open roles and a board with 2 both cost
one
item-lookupcharge. A company with no board, or zero open roles, is a free miss. - Real filters, not a wall of raw fields. Title keyword include/exclude, location, remote-only, and posted-after-date all combine, so you can ask for exactly the roles you want instead of filtering 578 rows by hand afterward.
- A delta mode for watching hiring over time. Turn on
onlyNewSinceLastRun, put the actor on a schedule, and every run after the first returns only postings you haven't seen — a quiet week still gets a row, so you can tell "nothing new" from "the run broke." - Flexible input. A bare Greenhouse token or the full
boards.greenhouse.io/job-boards.greenhouse.iocareers URL both work — no need to go find the raw API slug first.
What you get
One row per open job posting by default — toggle "One row per job posting" off to get one row per
company instead, with the job list nested in jobs. The 17 columns below are fixed, whether or
not you've turned on filters or delta mode.
| Field | Type / format | Description |
|---|---|---|
query | text | The company value you passed in, unchanged. |
found | boolean | true if the company has a Greenhouse board with at least one open role. Gates billing. |
status | text | OK, NOT_FOUND (no board or no open roles), or BAD_FORMAT (not a token or URL). |
boardToken | text | The board token, extracted from whatever you pasted. |
jobCount | number | Roles matched, after filters and delta filtering. |
totalJobCount | number | Roles on the board before filtering — shows what a filter dropped. |
newJobs | number | Roles new since your last run. Empty unless onlyNewSinceLastRun is on. |
firstRun | boolean | true on the baseline run for a watchlist. Empty unless delta mode is on. |
jobs | array | All matching jobs, each with the fields below. Expanded into separate rows in one-row-per-job mode. |
title | text | Job title. |
location | text | Office or region as Greenhouse lists it — e.g. "San Francisco", "Remote". |
departments | array | Department name(s), fetched from the board's separate department listing. |
remote | boolean | true if location or title reads as remote — matches "remote", "anywhere", "work from home", "wfh", "distributed". |
postedAt | date (ISO) | When Greenhouse first published the posting, falling back to updated_at. |
updatedAt | date (ISO) | When the posting was last edited. |
applyUrl | link | Direct apply link for the role. |
scrapedAt | date (ISO) | When this actor fetched the row. |
A company with no board, or a board with zero open roles, still returns one row with
found: false and a status/message explaining why — never charged.
How delta mode works
Turn on onlyNewSinceLastRun and put the actor on a schedule, and it stops being a full
re-dump and starts being a hiring alert. Here's what actually happens across runs:
- First run: returns everything matching your filters and remembers every posting id it
showed you.
firstRunistrueon this row. - Every run after: returns only postings it hasn't shown you before, and counts them in
newJobs. A quiet week still returns a row per company —jobCount: 0,newJobs: 0— so you can tell "nothing changed" from "the schedule silently stopped running." Every company checked is still billed once, whether or not anything is new. - Filters are part of the watchlist's identity. Two schedules watching the same company
with different
titleKeywordskeep separate seen-lists automatically. Edit a filter and that watchlist re-baselines on its next run — you'll see everything matching once more, then deltas resume from there. - Where it lives: a named key-value store,
greenhouse-jobs-lookup-state, inside your own Apify account. Nobody else can read it, storage cost is negligible, and deleting the store resets every watchlist to a fresh baseline.
Price
$4 per 1,000 companies, plus a $0.00005 start fee. Misses (found:false) are never charged.
That works out to $0.004 per company whose board is found, on the FREE pricing tier (drops to
$0.002 on GOLD) — you're never billed for a company with no Greenhouse board or a BAD_FORMAT
input, no matter how many companies are in the list. However many roles a found company has open,
it's one charge: a board with 578 postings costs the same as one with 2.
1,000 companies through this actor: ~$4 if every one has a findable board — in practice less, since a company with no Greenhouse presence costs nothing. Run the same 1,000 rows through a credit-based enrichment platform and expect $80–$400. This actor does one job — Greenhouse postings — and isn't trying to be a general enrichment waterfall.
How to use
- In the Apify Console. Open the actor page and click Start — the
companiesfield is already pre-filled with a working example. Results land in the run's dataset as soon as each item is found. - Via the API. Call it directly with a POST request — no Console needed once you have an API token:
curl "https://api.apify.com/v2/acts/accountable_eel~greenhouse-jobs-lookup/run-sync-get-dataset-items?token=<YOUR_TOKEN>" \-X POST \-H "Content-Type: application/json" \-d '{"companies":["stripe"]}'
- On a schedule. Save this actor as an Apify Task with the input you want, then add a Schedule (hourly, daily, weekly) so it runs on its own — no server of your own required.
Paste one company per line — a bare Greenhouse token like stripe, or the full careers URL
(https://boards.greenhouse.io/stripe or https://job-boards.greenhouse.io/stripe). The token is
extracted automatically either way, so you don't need to go look up the raw API slug.
Common ways teams use this:
- Source candidates by pulling every open role matching a title keyword across a list of target companies in one pass.
- Watch a competitor's hiring with a fixed company list on a weekly schedule and delta mode on, so each run shows only what's new.
- Score hiring momentum across a target account list by reading
jobCountper company in one-row-per-company mode. - Find roles in a specific market by combining
locationswithtitleKeywordsinstead of exporting everything and filtering by hand. - Feed an ATS-aware outbound sequence by joining
applyUrlandpostedAtonto an existing account list in Clay or n8n.
For a standing watchlist, turn on onlyNewSinceLastRun in the 🔁 Watch for new jobs
section and put the actor on a schedule — see How delta mode works
below for exactly what changes across runs.
Input
{"companies": ["stripe"]}
One company per line — paste the Greenhouse token or the full careers page URL. Accepted formats: stripe, https://boards.greenhouse.io/stripe, https://job-boards.greenhouse.io/stripe.
To narrow what comes back, open the 🎯 Job filters section. All of these are optional, and they combine with AND across fields, OR within a field:
| Input | What it does |
|---|---|
titleKeywords | Keep only roles whose title contains at least one of these — ["engineer","designer"]. Case-insensitive, partial words count. |
excludeTitleKeywords | Drop roles whose title contains one of these — ["intern","contract"]. Applied after titleKeywords. |
locations | Keep only roles whose location contains one of these — ["Berlin","United Kingdom"]. |
remoteOnly | Keep only roles that read as remote. |
postedAfter | Keep only roles first published on or after this date — "2026-06-01". |
Leave every filter empty and you get every open role. jobCount (matched) and totalJobCount
(before filtering) both come back on every row, so a filtered result is never ambiguous — and a
company whose roles all get filtered out still returns a found: true row with jobCount: 0,
charged like any other found company, because it answered the question you asked.
Sample output
| query | found | status | boardToken | jobCount | totalJobCount | newJobs | firstRun | jobs | title | location | departments | remote | postedAt | updatedAt | applyUrl | scrapedAt |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| stripe | true | OK | stripe | 573 | 573 | <all matching jobs (full list)> | Account Executive, AI Sales | San Francisco, CA | ["1175 Enterprise - Account Executives (NA)"] | false | 2026-02-03T15:19:01-05:00 | 2026-08-25T17:40:40-04:00 | https://stripe.com/jobs/search?gh_jid=7532733 | 2026-08-31T06:40:11.844Z |
That table lists every available column. In "one row per job posting" mode — the default — a real row reads like this:
{"query": "stripe","found": true,"status": "OK","boardToken": "stripe","jobCount": 589,"totalJobCount": 589,"title": "Account Executive, AI Sales","location": "San Francisco, CA","departments": ["1175 Enterprise - Account Executives (NA)"],"remote": false,"postedAt": "2026-02-03T15:19:01-05:00","updatedAt": "2026-08-25T17:40:40-04:00","applyUrl": "https://stripe.com/jobs/search?gh_jid=7532733","scrapedAt": "2026-08-26T06:00:44.199Z"}
A company with no Greenhouse board comes back as a miss and is never charged:
{"query": "not-a-real-company","found": false,"status": "NOT_FOUND","message": "No Greenhouse board found for this token","scrapedAt": "2026-08-26T06:00:44.199Z"}
Miss rows are still written to the dataset so you can see exactly which companies failed, rather than diffing your input list against the output.
Use it from Clay, n8n, Make, or an AI agent
This actor runs synchronously over plain HTTP — call it directly from a script, a workflow tool, or an AI agent, no Apify Console needed once you have an API token.
curl "https://api.apify.com/v2/acts/accountable_eel~greenhouse-jobs-lookup/run-sync-get-dataset-items?token=<YOUR_TOKEN>" \-X POST \-H "Content-Type: application/json" \-d '{"companies":["stripe"]}'
n8n. Add an HTTP Request node: Method POST, URL https://api.apify.com/v2/acts/accountable_eel~greenhouse-jobs-lookup/run-sync-get-dataset-items?token=<YOUR_TOKEN>, Body Content Type JSON, JSON Body {"companies":["stripe"]} (swap in an expression from an earlier node for a real value).
Clay. Add an "HTTP API" column: Method POST, URL https://api.apify.com/v2/acts/accountable_eel~greenhouse-jobs-lookup/run-sync-get-dataset-items?token=<YOUR_TOKEN>, Body {"companies":["{{company}}"]}, mapping the row's company into the companies array.
MCP. In Claude, Cursor, or any MCP client with the Apify MCP server, ask for "Greenhouse Jobs Scraper | Apify" — the agent will find and run this actor.
Tips
- Combine filters to build a watchlist query, not just a keyword search. plustitleKeywords: ["engineer"]
locations: ["Berlin"]plusremoteOnly: falsegets you Berlin-based engineering roles only, in one pass, instead of filtering a full dump afterward. - Put delta mode on a weekly schedule for competitor tracking.
onlyNewSinceLastRunplus a fixed company list turns this into a hiring-alert feed — you only see what changed since last time, and a quiet week still confirms the schedule is running. - Use
postedAfterfor "what opened this month", notupdatedAt. Greenhouse bumpsupdatedAton any edit, including typo fixes —postedAtonly moves when a role first goes live. - Switch to one-row-per-company mode for a headcount signal. Read
jobCountper company without pulling every posting into your table. - Don't combine delta mode with "Only keep rows containing." That filter runs on the finished
row and can drop the
newJobs: 0heartbeat row — the one row that tells you the schedule is still alive on a quiet week.
vs. alternatives
| What it costs | What you get | Trade-off | |
|---|---|---|---|
This actor (greenhouse-jobs-lookup) | $0.004 per company whose board is found (less on paid tiers), $0.00005 actor start, nothing for a company with no Greenhouse board | Every open posting for a company's Greenhouse board — title, location, department, apply URL — filtered by role keyword, location or posting date, with onlyNewSinceLastRun for deltas | Greenhouse only. If your list is mixed-ATS, use ats-jobs-unified-lookup. Office data isn't returned — Greenhouse's own endpoint costs 78× the payload to include it. |
| TheirStack | $0.005–$0.03 per company | A job-postings dataset with history, normalised across many ATS platforms and job boards | Broader coverage than one platform, and it keeps history. This reads Greenhouse's own API live, at the bottom of that price range. |
| Greenhouse's own API | Free | The same JSON | It is the same JSON. This handles the board-token guessing, the second request departments actually live behind, filtering, deltas, and the retries — and hands you a flat table. |
| Clay | $0.08–$0.40 per enriched row in credits, on top of a seat | A whole enrichment workspace — waterfalls across dozens of providers, plus the table and the sequencing around it | If you want one place that does everything and you're not counting rows, that's Clay. This is one column, priced per column, callable from Clay via its HTTP step. |
(TheirStack and Clay prices above are their published August 2026 list prices — check each vendor's own page before relying on the comparison.)
FAQ
Why does found say false, or a row come back empty?
Either the input didn't look like a Greenhouse token or URL (status: BAD_FORMAT), the company
doesn't have a Greenhouse board, or it currently has zero open roles (status: NOT_FOUND either
way). Check the message column for the specific reason.
Was I charged for that row?
Only if found is true. A company with no board, or no current openings, is free — check
status to confirm which case it was.
I got charged for a company that returned jobCount: 0 — why?
Its board exists and has open roles; they just didn't match your filters, or weren't new since
your last delta run. totalJobCount shows what was on the board before filtering. That's a real
answer to the question you asked, so it bills like any other found company. A company with no
board, or nothing open, is found: false and free.
How does delta state work, and can I reset it?
It lives in a named key-value store, greenhouse-jobs-lookup-state, inside your own Apify account
— nobody else can see it, and it holds only job IDs per company. Delete that store to reset every
watchlist. Changing your filters also starts a fresh watchlist automatically, since the filter
values are part of what identifies a seen-list.
Why is there no office field, and where do departments come from?
Greenhouse's job-listing endpoint omits both unless you request its heavyweight response, which
inlines every job's full HTML description — a large payload multiplier on a board with hundreds of
roles. So departments is filled from one extra request to the board's separate department
listing; if that request fails, every role still comes back, just with an empty departments
cell. Offices aren't reported at all, because the only endpoints carrying them cost far more than
the field is worth — better no column than a permanently blank one. location carries the place
information, and remoteOnly matches on both location and title.
Does this hit rate limits on large company lists? It calls Greenhouse's public board API once per company (twice with the departments hop), with its own timeouts and retries. That API is unauthenticated and public — the same one Greenhouse's own careers pages call — so no proxy is needed. Large lists just take proportionally longer.
Is this GDPR-safe to run against EU companies? Everything returned is already published publicly on each company's own careers page — titles, locations, departments, apply links. No personal data about applicants or employees is collected. You remain responsible for how you use the output downstream.
Can an agent run this without a human clicking Start? Yes — it's registered with the Apify MCP server, so a Claude or Cursor agent with that MCP connected can find "Greenhouse Jobs Lookup" and pull rows back on its own; see Use it from Clay, n8n, Make, or an AI agent.
Related actors
- ATS Jobs Unified Lookup — check a company across Greenhouse, Lever, Ashby, and other ATS platforms in one call when you don't know which one they use.
- Lever Jobs Lookup — the same lookup, filters, and delta mode for companies on Lever.
- Ashby Jobs Lookup — the same lookup, filters, and delta mode for companies on Ashby.