Greenhouse Jobs Scraper - Fast ATS API avatar

Greenhouse Jobs Scraper - Fast ATS API

Pricing

Pay per event

Go to Apify Store
Greenhouse Jobs Scraper - Fast ATS API

Greenhouse Jobs Scraper - Fast ATS API

Jobs from Greenhouse boards (Airbnb, Stripe, Anthropic, Figma, thousands more). Title/dept/location filters + diff mode for daily new-jobs. New `report` mode: 1 row/company with hiringScore, velocity, priorityRoles, remoteShare. $0.50/1k jobs or $0.025/report.

Pricing

Pay per event

Rating

0.0

(0)

Developer

An Tan Huynh

An Tan Huynh

Maintained by Community

Actor stats

0

Bookmarked

1

Total users

0

Monthly active users

2 days ago

Last modified

Share

Greenhouse Jobs Scraper – Fast ATS API

Scrape Greenhouse-hosted career pages via the public JSON API. One row per job, all open positions returned in a single API call, no proxy required.

$0.50 per 1,000 jobs ($0.0005 / job, pay-per-event).

Who is this for?

Thousands of companies host their careers on Greenhouse — including Airbnb, Stripe, Anthropic, Coinbase, Reddit, Figma, Robinhood, Instacart, Discord, Pinterest, DoorDash, Chime, SoFi and many more. If a board URL looks like https://boards.greenhouse.io/<company> or https://job-boards.greenhouse.io/<company>, this actor can scrape it.

Input

FieldTypeDefaultNotes
boardSlugsstring[]requiredCompany slugs or full board URLs. The actor extracts the slug automatically. Pass as many companies as you want in a single run.
includeContentboolfalseAdd ?content=true to pull full description HTML, departments, offices and AI disclaimer flags. Response grows ~15× (Airbnb: 127 KB → 2.3 MB).
maxJobsPerBoardint5000Safety cap per board (no pagination on Greenhouse).
titleKeywordsstring[](empty)Keep only jobs whose title contains any of these (case-insensitive substring). Example: ["engineer","data scientist","ml"].
titleExclusionsstring[](empty)Drop any job whose title contains any of these. Example: ["senior","manager","intern"].
departmentsstring[](empty)Keep jobs whose department name contains any of these substrings. Auto-enables includeContent because departments are only returned with content. Case-insensitive.
locationsstring[](empty)Keep jobs whose locationName contains any of these substrings. Case-insensitive.
workplaceTypesstring[](empty)Match the normalised workplaceType: remote, hybrid, onsite. Many boards expose this via "Workplace Type" or "Location Type" metadata (Airbnb, Anthropic).
onlyRemoteboolfalseShortcut: keep only jobs with workplaceType=Remote or locationName containing "remote".
updatedAfterstring(empty)ISO date / datetime cut-off; keep only jobs with updatedAt on or after.
onlyNewSinceLastRunboolfalseDiff mode. The actor remembers (per stateKey + board) the set of jobIds seen on previous runs in a named key-value store, and on each subsequent run only pushes jobs that are new. Every pushed row carries isNew: true. Pair with Apify Scheduler for daily deltas.
stateKeystringdefaultOptional label for the diff state bucket. Different stateKey values keep independent "seen" memories; shared values share memory.
proxyConfigurationobject(none)Optional Apify Proxy – the API is open and usually works direct.

Example input:

{
"boardSlugs": ["airbnb", "stripe", "anthropic"],
"includeContent": false,
"maxJobsPerBoard": 5000
}

Output (one row per job)

{
"boardSlug": "airbnb",
"jobId": 8184174,
"internalJobId": 3541546,
"title": "Account Manager",
"absoluteUrl": "https://careers.airbnb.com/positions/8184174?gh_jid=8184174",
"locationName": "London, United Kingdom",
"workplaceType": "Hybrid",
"metadata": [
{"name": "Is this job part of ACC?", "value": "no", "valueType": "yes_no"},
{"name": "Workplace Type", "value": "Hybrid", "valueType": "single_select"}
],
"departments": [],
"offices": [],
"requisitionId": "MULTI",
"firstPublished": "2026-09-09T04:35:19-04:00",
"updatedAt": "2026-09-29T20:00:54-04:00",
"applicationDeadline": null,
"language": "en",
"companyName": "Airbnb",
"contentHtml": null,
"aiDisclaimer": null,
"aiOptOutUrl": null,
"scrapedAt": "2026-10-15T20:55:27+00:00"
}

When includeContent=true, contentHtml, departments, offices, aiDisclaimer and aiOptOutUrl are populated.

workplaceType normalisation

Boards tag workplace under different metadata names. The actor looks for Workplace Type, Location Type, Work Arrangement, Work Setup, Remote Type, Work Location, Remote Work, and maps the value to Remote / Hybrid / Onsite whenever recognisable. Boards that don't tag workplace at all (Stripe, Figma, Discord in our smoke tests) return workplaceType: null.

Smoke run numbers (2026-10-16)

InputBoardsRowsCost
Default smoke – 3 boards no contentairbnb, discord, figma355~$0.00013 compute + $0.1775 PPE
Content + filter – engineering & data, remote/hybridairbnb only (includeContent=true, departments=[engineering,data], workplaceTypes=[remote,hybrid])48~$0.00029 compute + $0.024 PPE
Big boards + updatedAfteranthropic + stripe, updatedAfter=2026-09-011115~$0.00076 compute + $0.5575 PPE

Field coverage over the default smoke (355 rows):

FieldCoverage
jobId / title / absoluteUrl / locationName / companyName / firstPublished / updatedAt / language / requisitionId100%
workplaceType46% (Airbnb tags it; Discord and Figma don't)

Airbnb workplace distribution: 136 Remote / 27 Hybrid / 2 Onsite. Anthropic workplace distribution: 348 Onsite / 14 Remote / 3 Hybrid / 22 untagged.

Pricing

Pay-per-event. Two charge types:

  • $0.0005 per job row pushed (default mode: "jobs").
  • $0.025 per report row pushed (mode: "report", one row per company).

Example runs:

  • 3 boards (Airbnb + Discord + Figma, 355 jobs, jobs mode) → $0.18.
  • 2 big boards (Anthropic + Stripe, 1,115 jobs, jobs mode) → $0.56.
  • 1 board full content HTML (Airbnb, ~165 jobs, jobs mode) → $0.08.
  • 3 boards in report mode (Airbnb + Stripe + Anthropic) → $0.075 (3 reports).

Compute is almost free (direct API, no proxy, ~1 second per board).

Report mode (mode: "report")

Instead of one row per job, emit one hiring-signal report per company. Each report row carries:

  • totalOpenRoles + hiringScore 0-100 + hiringLabel (aggressive_hiring / active_hiring / steady_hiring / low_hiring / no_signal).
  • hiringVelocity: { recentWindowDays, netOpenRolesChange, hadPreviousRun, previousTotal } — the delta vs the previous run of the same stateKey.
  • priorityRoles[]: top 5 role families (engineering, data, research, product, design, sales, customer_success, support, marketing, operations, finance, legal, people_hr, security, other) with example titles.
  • departmentSignals[], regionSignals[] (us / canada / europe_uk / europe_eu / latam / mena / apac / africa / remote_global / remote_other / other / unknown), remoteShare 0.0-1.0, seniorityMix (junior / mid / senior / leadership).
  • keywordHits: user-supplied strings mapped to how many open titles contain each (case-insensitive substring).
  • expansionSignals: newDepartments[], newRegions[], newRoleFamilies[] compared to the previous run.
  • recommendedOutreach[]: short English suggestions for BD / sales / talent outreach based on the above signals.
  • reportConfidence 0.1-1.0 plus warnings[].

Example input:

{
"boardSlugs": ["airbnb", "stripe", "anthropic"],
"mode": "report",
"keywordHits": ["AI", "ML", "LLM", "security"],
"stateKey": "weekly-pipeline"
}

Previous-run deltas live in the named key-value store greenhouse-report-<stateKey> on your account.

Daily deltas (onlyNewSinceLastRun)

Pair the actor with Apify Scheduler to get only newly-posted jobs each run. The first run of a given (stateKey, boardSlug) records every open jobId and pushes the first maxJobsPerBoard rows (all tagged isNew: true). Every later run compares the current open set against the stored one and emits only rows whose jobId was not seen before.

{
"boardSlugs": ["airbnb", "stripe", "anthropic"],
"onlyNewSinceLastRun": true,
"stateKey": "daily-pipeline",
"titleKeywords": ["engineer", "data"],
"includeContent": true
}

The dedup state lives in a named key-value store greenhouse-diff-<stateKey> on your account; delete it from the Apify Console to force the next run to treat every job as new. Use different stateKey names when you want independent memories for different downstream pipelines (e.g. daily-slack vs weekly-report).

Notes

  • The Greenhouse public API returns every open job on a board in a single response. There is no pagination – use maxJobsPerBoard only to trim very large boards (Stripe/Anthropic ~700 jobs).
  • All timestamps are ISO 8601 with timezone offset exactly as Greenhouse returns them (2026-09-29T20:00:54-04:00).
  • Unknown board slugs return HTTP 404; the actor logs and skips.
  • The actor also accepts pasted URLs like https://boards.greenhouse.io/airbnb or https://job-boards.greenhouse.io/anthropic – the slug is extracted automatically.