Greenhouse, Lever & Ashby Jobs Scraper API
Pricing
$1.00 / 1,000 job postings
Greenhouse, Lever & Ashby Jobs Scraper API
Normalize public Greenhouse, Lever, and Ashby jobs into one dataset. Detect official career URLs and optionally emit only new postings across scheduled runs.
Pricing
$1.00 / 1,000 job postings
Rating
0.0
(0)
Developer
Kunteper Koyu
Maintained by CommunityActor stats
0
Bookmarked
1
Total users
0
Monthly active users
6 days ago
Last modified
Categories
Share
Fetch published job postings from official public Greenhouse, Lever, and Ashby endpoints and normalize them into one stable schema. Build job boards, hiring-signal datasets, market research, alerts, and recruiting automations without maintaining three provider-specific integrations.
Editing an AI-jobs newsletter? Start with the daily Anthropic jobs feed. Collect a baseline once, then receive previously unseen postings for editorial review on later runs. The setup below explains scheduling, duplicate handling, export, and cost limits.
What you get
- company, title, primary and secondary locations, department, and team
- employment type, workplace type, and remote status when available
- plain-text and HTML descriptions
- canonical job and application URLs
- compensation when the provider exposes it
- publication and update timestamps
- deterministic SHA-256 fingerprints for change detection
- careers-URL auto-detection for supported provider URL formats
- persistent
newOnlymode for scheduled monitors - isolated board errors, so one invalid board does not discard successful results
Common use cases
- power a niche job board or searchable jobs API
- monitor target companies for hiring and expansion signals
- create role, keyword, location, and remote-job alerts
- normalize jobs before analytics, enrichment, or RAG ingestion
- track new and changed postings across multiple ATS providers
Quick start
- Find the board token in the company's public careers URL.
- Add the provider, board name, and optional company display name.
- Add keyword, location, remote, or publication-date filters if needed.
- Run the Actor and open the normalized dataset.
Try the ready-to-run Anthropic AI jobs example.
{"boards": [{"provider": "greenhouse","boardName": "anthropic","companyName": "Anthropic"},{"provider": "lever","boardName": "leverdemo","companyName": "Lever","region": "global"},{"provider": "ashby","boardName": "Ashby","companyName": "Ashby"}],"searchTerms": ["engineer"],"locations": ["remote", "New York"],"remoteOnly": false,"includeDescription": true,"includeCompensation": true,"publishedAfter": "2026-07-01","maxJobsPerBoard": 25,"concurrency": 5,"timeoutSeconds": 30}
Lever supports global and eu regions. Board names are the public token or site name in the provider's careers URL.
Alternatively, paste official hosted careers URLs and let the Actor identify the provider and board without crawling the page:
{"careerUrls": ["https://boards.greenhouse.io/example","https://jobs.eu.lever.co/example","https://jobs.ashbyhq.com/example"],"newOnly": true,"stateStoreName": "my-daily-job-monitor"}
Auto-detection is deliberately limited to boards.greenhouse.io,
job-boards.greenhouse.io, jobs.lever.co, jobs.eu.lever.co, and
jobs.ashbyhq.com. It parses the URL locally and calls the provider's public
JSON API; it does not scrape arbitrary company careers domains or follow
redirects. For a custom careers domain, use its official hosted ATS link or
enter the provider and board token under boards.
New-jobs-only scheduled runs
Set newOnly to true to persist stable hashes of provider, board, and source
job ID in the named stateStoreName. The first run emits all matching jobs;
later runs using the same store emit and charge only for previously unseen job
IDs. Edits to a known posting do not make it new. Use a separate store name for
each independent monitor and avoid concurrent runs against the same store,
because key-value-store updates are not transactional. State retains the most
recent 100,000 job identities. The default newOnly: false preserves the
original behavior.
Daily jobs feed for AI-newsletter editors
Use this workflow when you check the same AI company's careers board every day and need a queue of new postings to review. This example monitors Anthropic's public Greenhouse board, including non-technical roles; it does not discover every AI employer or decide which jobs belong in your newsletter.
- Open the daily newsletter example and copy it into your account. Give
stateStoreNamea unique name for your newsletter and keep that name on later runs. - Run once to collect the baseline. Inspect the dataset and the
BOARD_ERRORSrecord in the run's key-value store. A successful run can still contain a board error, so check both before treating an empty dataset as "no new jobs." - Run again with the same input and store. Previously stored job IDs should be absent; a genuinely new posting can still appear. Export the dataset as CSV for your editorial sheet, or read it through your existing integration. The Actor does not append to Sheets or send a newsletter itself.
- Once the baseline is complete, schedule your copied task daily in Apify Console, for example at 08:00 in your selected timezone. Keep runs sequential. Set a maximum charge in the task's run options and review the first scheduled results before relying on the feed.
{"boards": [{"provider": "greenhouse","boardName": "anthropic","companyName": "Anthropic"}],"newOnly": true,"stateStoreName": "my-ai-newsletter-anthropic-v1","maxJobsPerBoard": 5000,"includeDescription": false,"includeCompensation": false,"concurrency": 1,"timeoutSeconds": 30}
Why 5,000 instead of the default 25? The per-board limit is applied before previously seen IDs are removed. A low limit repeatedly examines only the first matching slice and can miss jobs outside it. This example uses the supported maximum for one board. It still cannot promise complete coverage above 5,000 matching jobs. A spending cap can also leave the baseline incomplete; inspect the run and output before turning on a schedule. If you change boards or filters, use a new state store when you want a fresh baseline.
Editorial handoff: use provider, boardName, and sourceJobId together as
your destination's unique key. Review title, companyName, location, and
jobUrl before publishing. Deduplicate at the destination too: interruptions
between dataset writes and state saves can replay rows. A zero-row incremental
run does not mean old jobs are still open. This feed does not emit removals or
edits; verify the source link before including a job in an issue.
Budget example: a 500-job baseline represents $0.50 in result-event charges; 100 newly discovered jobs over the following month represent $0.10 more. These are price examples, not forecasts of Anthropic's posting volume or your final bill. The saved example uses a $5 maximum event charge per run, matching the 5,000-job output ceiling. Lower that ceiling in your task's run options if needed, while checking that the baseline remains complete. Check Apify's current account pricing and limits before scheduling.
Example result
{"provider": "greenhouse","boardName": "anthropic","companyName": "Anthropic","sourceJobId": "4461450008","title": "Account Executive, AI Native","location": "San Francisco, CA | New York City, NY","secondaryLocations": [],"remote": false,"jobUrl": "https://job-boards.greenhouse.io/anthropic/jobs/4461450008","publishedAt": "2024-12-20T13:53:38-05:00","updatedAt": "2026-06-08T20:49:32-04:00","fingerprint": "cb3e2724e0fde508046d496236f32f598457dc566625918c48655cc80e836697","retrievedAt": "2026-07-06T17:33:19.395Z"}
Run by API
Set APIFY_TOKEN in your environment and keep it out of source control.
cURL
curl -X POST \"https://api.apify.com/v2/acts/rtworule~public-ats-job-feed-normalizer/run-sync-get-dataset-items?token=$APIFY_TOKEN" \-H "Content-Type: application/json" \-d '{"boards":[{"provider":"greenhouse","boardName":"anthropic","companyName":"Anthropic"}],"remoteOnly":true,"maxJobsPerBoard":100}'
JavaScript
import { ApifyClient } from 'apify-client';const client = new ApifyClient({ token: process.env.APIFY_TOKEN });const run = await client.actor('rtworule/public-ats-job-feed-normalizer').call({boards: [{ provider: 'greenhouse', boardName: 'anthropic', companyName: 'Anthropic' }],remoteOnly: true,maxJobsPerBoard: 100,});const { items } = await client.dataset(run.defaultDatasetId).listItems();console.log(items);
Python
import osfrom apify_client import ApifyClientclient = ApifyClient(os.environ["APIFY_TOKEN"])run = client.actor("rtworule/public-ats-job-feed-normalizer").call(run_input={"boards": [{"provider": "greenhouse","boardName": "anthropic","companyName": "Anthropic",}],"remoteOnly": True,"maxJobsPerBoard": 100,})items = client.dataset(run["defaultDatasetId"]).list_items().itemsprint(items)
Pricing
This Actor uses pay per event. A job-normalized event costs $0.001 per stored job: 100 delivered jobs cost $0.10 and 1,000 cost $1.00 in event charges. The minimum configurable maximum-charge limit is $0.01, which is not a fixed $0.01 charge for every run. Filters, per-board limits, and newOnly mode help control output. The default input keeps the first run to 25 jobs per board; raise maxJobsPerBoard when you want a larger snapshot. The Actor stops storing results when the run's maximum total charge is reached. In newOnly mode, remembered postings are not emitted or charged as result events.
Integrations and automation
- schedule with
newOnly: trueto receive and pay for only newly discovered jobs - compare the output
fingerprintseparately when you want to detect edits to known jobs - send matching jobs to Slack, email, Make, Zapier, n8n, or a webhook
- load normalized jobs into a database, search engine, BI tool, or vector store
- export CSV/XLSX to Sheets for research and editorial review
- use
publishedAfter, search terms, and locations to keep recurring runs focused
FAQ and troubleshooting
Is this scraping private candidate or recruiting data? No. It reads only public job-posting endpoints and does not access candidates, applications, internal postings, or authenticated recruiter systems.
Where do I find the board name? It is the company-specific token in the public Greenhouse, Lever, or Ashby careers URL—not necessarily the company's display name.
Why is a job or field missing? Filters may exclude it, the provider may omit the field, or the posting may have been removed. The Actor does not invent missing source data.
How do I avoid duplicate alerts? Enable newOnly and reuse the same
stateStoreName on every scheduled run. The Actor then suppresses previously
seen job IDs before charging or writing output. Keep newOnly disabled if you
want a full snapshot and compare the output fingerprint in your destination
to detect changed postings.
Why did one board fail while the run succeeded? Boards are isolated. Inspect the BOARD_ERRORS key-value-store record for an invalid token, provider response, timeout, or other board-specific error.
Data sources, responsible use, and limitations
The Actor uses public posting endpoints:
- Greenhouse Job Board API:
https://boards-api.greenhouse.io/v1/boards/{board}/jobs - Lever Postings API:
https://api.lever.co/v0/postings/{site} - Ashby Job Postings API:
https://api.ashbyhq.com/posting-api/job-board/{board}
Provider schemas, availability, and job content can change. Compensation, remote status, and timestamps are returned only when the source exposes enough information. Users remain responsible for their downstream use, retention, applicable terms, and compliance.
For a reproducible issue, open an issue from the Actor page with the run ID, provider, public board name, and expected behavior. Never include credentials, candidate data, or other sensitive information.