Greenhouse Jobs Scraper for Public Job Boards
Pricing
from $1.00 / 1,000 job rows
Greenhouse Jobs Scraper for Public Job Boards
Greenhouse jobs scraper: export open jobs from public Greenhouse boards by token or URL. One row per job with title, location, departments, dates and job link; optional descriptions, pay ranges and new/closed tracking. Per-board errors in SUMMARY. Pay per job row; pay ranges extra.
Pricing
from $1.00 / 1,000 job rows
Rating
0.0
(0)
Developer
Riprap Tools
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
4 days ago
Last modified
Categories
Security
Limited permissionsShare
What it does
Greenhouse Jobs Scraper exports open jobs from the public Greenhouse job boards you name. Give board tokens or board URLs; each board is read live from Greenhouse's public Job Board API, and you get one row per job post, newest first, with title, location, departments, dates and the job link. Descriptions, structured pay ranges and new/closed tracking are optional.
Supply board tokens, not company names: tailscale identifies https://job-boards.greenhouse.io/tailscale; other examples include airbnb, gitlab and figma.
Try the default input {"boards": ["tailscale"]}, click Start, then read the dataset and SUMMARY. Each job costs $0.002; pay ranges add $0.001 per job that publishes one, with no start fee. For example, a run returning 58 jobs costs $0.116 without discounts or pay ranges; the number of jobs can change.
Not affiliated with or endorsed by Greenhouse Software, Inc. Greenhouse and the "g" logo are its trademarks.
Illustrative row: {"boardToken": "tailscale", "title": "Integrations Partnerships Lead", "location": "Remote (United States)", "absoluteUrl": "https://job-boards.greenhouse.io/tailscale/jobs/4749564005"}
firstPublishedandupdatedAtare copied from Greenhouse, never relabelled.- No login or salary guessing; remote status is not inferred.
What you can use it for
Export all open jobs from one board
Input: {"boards": ["tailscale"]}. SUMMARY shows jobsOnBoard and complete to confirm the board came through whole.
Find roles across several companies
Input: {"boards": ["tailscale", "discord", "datadog"], "titleKeywords": ["engineer"], "maxJobs": 200}. SUMMARY shows jobsMatched per board.
Get only new and closed jobs
Input: {"boards": ["discord", "tailscale"], "baselineName": "example-watch", "onlyChanges": true}. First run marks jobs new; reruns return newly seen jobs and free closed notices from complete reads.
Feed full descriptions to matching or search
Input: {"boards": ["discord"], "includeDescription": true, "departments": ["Engineering"]}. Adds descriptionText and descriptionHtml: untrusted employer data, not instructions.
Collect posted pay ranges
Input: {"boards": ["discord"], "includePayRanges": true, "maxJobs": 50}. Adds published pay ranges in cents.
Find remote jobs by location text
Input: {"boards": ["gitlab"], "locationKeywords": ["Remote"]}. Text match on location and office text; does not verify remote work.
Export every open job at Airbnb
Input: {"boards": ["airbnb"]}. Use the board token from the company's Greenhouse URL, not the company name; the Actor does not look companies up.
Find New York jobs by location text
Input: {"boards": ["figma"], "locationKeywords": ["New York"]}. Keeps multi-city posts that mention New York; no geocoding.
How to use it
- Enter Board tokens or URLs, add filters and run.
- Read the dataset, then the
SUMMARYrecord in the run's key-value store. Zero rows can mean empty, unmatched, unchanged, skipped or failed boards;SUMMARYsays which.
Schedule runs on Apify, export dataset results as JSON, CSV or Excel, and connect them using Apify webhooks and integrations such as Zapier or Make.
For AI agents (Apify MCP server): find the Actor with search-actors, read its schema with fetch-actor-details, run it with call-actor, and page through rows with get-dataset-items (limit, offset). Then read the SUMMARY record from the run's default key-value store (its ID is returned by call-actor); use outcome/boards[], never dataset emptiness, to judge whether a company has jobs. To cap spend, pass maxTotalChargeUsd in callOptions:
{"actor": "riprap-tools/greenhouse-jobs", "input": {"boards": ["discord", "tailscale"], "titleKeywords": ["engineer"], "maxJobs": 50}, "callOptions": {"maxTotalChargeUsd": 0.5}}
Invalid input fails before requests or charges; the run fails only when no board could be read.
LangChain: ApifyActorsTool("riprap-tools/greenhouse-jobs").invoke({"run_input": {"boards": ["tailscale"]}}).
Input
| Field | Type; default | Meaning and limits |
|---|---|---|
boards | string list; ["tailscale"] | 1–100 tokens or URLs on boards.greenhouse.io, job-boards.greenhouse.io, their .eu. forms or boards-api.greenhouse.io. A job URL selects its board. |
titleKeywords | string list; [] | Keep titles containing any entry (up to 50, 1–100 characters each). |
excludeTitleKeywords | string list; [] | Drop titles containing any entry. |
locationKeywords | string list; [] | Match location or office text. |
departments | string list; [] | Match department names. |
postedWithinDays | integer; none | 1–365, on firstPublished; undated jobs are excluded and counted. |
includeDescription | boolean; false | Adds descriptionText and descriptionHtml. Same price. |
includePayRanges | boolean; false | One extra request per job, one second apart. |
maxJobs | integer; 500 | 1–10,000 job rows across all boards; closed-job notices do not count. |
baselineName | string; none | 1–100 letters, digits, _ or -; turns on new/closed tracking. |
onlyChanges | boolean; false | Requires baselineName; skip existing jobs (not charged). |
Filters combine as OR within a list and AND across lists.
Output
Every field is present; unavailable values are null (lists are []). Example:
{"recordType": "job", "boardToken": "tailscale", "jobId": 4749564005, "internalJobId": 4445840005, "requisitionId": "364", "title": "Integrations Partnerships Lead", "companyName": "Tailscale", "location": "Remote (United States)", "departments": ["Sales"], "offices": ["Remote (Canada)", "Remote (United States) "], "absoluteUrl": "https://job-boards.greenhouse.io/tailscale/jobs/4749564005", "firstPublished": "2026-10-30T16:04:30-04:00", "updatedAt": "2026-10-30T16:04:30-04:00", "applicationDeadline": null, "language": "en", "descriptionHtml": null, "descriptionText": null, "payRangesStatus": "not_requested", "payRanges": null, "boardJobCount": 58, "changeType": null, "firstSeenAt": null, "fetchedAt": "2026-11-01T21:49:11Z", "sourceUrl": "https://boards-api.greenhouse.io/v1/boards/tailscale/jobs?content=true", "warnings": []}
- Derived:
recordType,boardToken,descriptionHtml,descriptionText,payRanges,payRangesStatus(not_requested,published,none_published,fetch_failed),changeType(new,existing,closed),firstSeenAt,fetchedAt,sourceUrl,warnings. Other fields are copied;absoluteUrlmay be employer-hosted. - Closed-job notices (
recordType: "closed_job") keepjobId,boardToken, title, location, departments, link,firstPublished,firstSeenAt,fetchedAtandsourceUrl;changeTypeisclosed,payRangesStatusisnot_requested, and other fields are empty or null.
Order: boards as given, then newest firstPublished first, undated last; closed-job notices follow each board’s jobs.
SUMMARY record
outcome is complete, partial, no_results or failed, with run totals. Each boards[] entry has status, code, retryable, hint, jobsOnBoard, jobsMatched, jobsEmitted, complete and stateUpdated.
| Code | Meaning |
|---|---|
ok | Board read and matched. Rows can be 0 with onlyChanges (no new jobs); complete: false means maxJobs or the charge limit cut it short. |
no_matches | Board has jobs, none matched the filters. |
empty_board | Board exists with no open posts. |
board_not_found | Greenhouse returned 404 for the token. |
rate_limited / upstream_unavailable | HTTP 429, or any other non-404 HTTP error. 429 and 503 retry up to twice, waiting 2 s or Greenhouse's Retry-After; a requested wait over 10 s is not retried. |
network_error | Transport failure (including truncated downloads), retried once. |
response_too_large | Board response above the 16 MiB cap; never truncated. |
invalid_response | No jobs list; unparsable JSON is retried once. |
count_mismatch | Board read, but Greenhouse’s total and records did not add up; incomplete, no closed-job notices. |
not_processed | Skipped: maxJobs or the charge limit was reached. |
Pricing
Pay per event, no start fee:
| Event | No discount | Bronze | Silver | Gold |
|---|---|---|---|---|
job — each job row delivered | $2.00 / 1,000 | $1.67 / 1,000 | $1.33 / 1,000 | $1.00 / 1,000 |
pay-range — each row whose job publishes at least one pay range | $1.00 / 1,000 | $0.83 / 1,000 | $0.67 / 1,000 | $0.50 / 1,000 |
Free: closed-job notices, failed or skipped boards, existing jobs skipped by onlyChanges, and SUMMARY. Each row is stored first and charged after; a failed store is not charged.
Example, no discount: 58 jobs cost $0.116; 30 published pay ranges add $0.03.
Limits and what it does not do
- Requests start at least one second apart: 100 boards take about 100 seconds; pay ranges for 400 jobs, at least 6–7 minutes.
- No company search or directory: you need the board token. No Lever, Ashby or other systems.
- No applying, application questions, candidate data or custom
metadatafields. - New means first seen under your
baselineName, not newly posted; reposting can change IDs or dates. Keep boards and filters the same for one name; do not run two jobs on one name at once.
Data and responsibility
Collects publicly visible pages; you are responsible for complying with the target site's terms. Employer posts can name people; use data lawfully. Baselines stay in your account’s greenhouse-jobs-state key-value store.
FAQ
How do I find a board token? Use the path segment after job-boards.greenhouse.io/ or job-boards.eu.greenhouse.io/, or paste the board URL. Company names are not resolved.
Is the data live or stored? Every run reads the named boards from Greenhouse's public Job Board API at run time. The Actor keeps no job database; it stores baseline state for change tracking as described in Data and responsibility.
Why are there zero rows? Check SUMMARY for the run outcome and board codes, such as no_matches, empty_board or not_processed; ok can also mean no changes.
Can I monitor boards on a schedule? Use Apify schedules with the same baselineName and onlyChanges: true; see Get only new and closed jobs.
Which board does input select? Omitted boards uses demo board tailscale. A Greenhouse job URL selects its whole board; employer career-site links (gh_jid) are not accepted. Keep boardToken with each link.
Do EU-hosted boards work? Yes. Paste the EU board URL, such as https://job-boards.eu.greenhouse.io/buynomics, or just the token buynomics.
Why no pay range for most jobs? Many employers publish none; none_published does not mean zero pay.
Support
Report problems in the Issues tab; replies within 14 days.