Hiring Signals: Greenhouse, Lever & Ashby Jobs Scraper + AI
Pricing
from $1.00 / 1,000 job scrapeds
Hiring Signals: Greenhouse, Lever & Ashby Jobs Scraper + AI
Paste company domains. The Actor finds their Greenhouse, Lever or Ashby board, scrapes open jobs, and can add AI seniority, tech stack, salary and a company-level hiring signal. From $1 per 1,000 jobs on paid plans.
Pricing
from $1.00 / 1,000 job scrapeds
Rating
0.0
(0)
Developer
Crafted in Code
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
10 days ago
Last modified
Categories
Share
Paste a company domain and get the open jobs behind its Greenhouse, Lever or Ashby board, plus an optional AI hiring-intent signal for B2B sales, recruiting and competitive research. The Actor finds the board for you (careers-page links, then slug guesses), filters by date, department and location, and can diff new jobs since the last run.
AI is optional. With AI job enrichment and company signals turned off, or with no GEMINI_API_KEY, the Actor still scrapes and saves jobs. You are charged the primary job-scraped event only after a row is stored. AI events are charged only after the enriched row is stored and the model call succeeded.
What data can you extract?
| Field | On a job | On a company signal |
|---|---|---|
| Company, domain, ATS, board token, job URL | Yes | Company, domain, ATS |
| Title, department, team, location, remote, employment type | Yes | — |
| Posted and updated time | Yes | — |
| Description text (recruiter names, emails and phones removed) | Yes | — |
| Raw salary text from the ATS, or a salary sentence in the posting | Yes | — |
| Normalized salary (min, max, currency, period) | When that text parses. AI fills these only if parsing found nothing | — |
| AI seniority, function, tech stack, tools, visa, summary | When AI is on | — |
| Open roles, roles by function, new since last run, top tech stack | — | Yes |
| Buying signals with at least one evidence job URL, relevance 0–100, outreach angle | — | When company-signal AI succeeds |
itemType is job or company-signal. Use the Jobs and Company signals dataset views to switch.
How to scrape hiring signals
- Set Company domains (
stripe.com,https://www.figma.com/careers) and/or Board tokens if you already know the slug (greenhouse/stripe,lever/palantir,ashby/ramp). - Optionally filter with Departments, Locations and Posted within days. Jobs with no posted date are kept.
- Leave Only new since last run off for a full pull. Turn it on for a schedule: the first successful fetch of a board stores every job id in the named key-value store
hiring-intent-seen-jobs, and later runs return only ids that were not on that snapshot. - Leave AI off for a cheap scrape. Turn on AI job enrichment and/or Company hiring signals, and set Signal focus to what you sell (for example
we sell data-warehouse tooling). - Run. Open the dataset for rows, and the key-value record
RUN-STATSfor counters, warnings and boards that came back empty.
The prefilled input is a small demo: stripe.com and figma.com, 20 jobs per company, AI off. That is what the Store's daily test runs. It should finish with a non-empty dataset in well under five minutes.
Input example
{"companies": ["stripe.com", "figma.com"],"boardTokens": [{ "ats": "ashby", "token": "ramp" }],"departments": ["engineering"],"locations": ["remote"],"postedSinceDays": 30,"onlyNewSinceLastRun": false,"aiJobEnrichment": true,"companySignals": true,"signalFocus": "we sell data-warehouse tooling","maxJobsPerCompany": 300,"maxAiJobs": 2000}
Pass "companies": [] when you only want explicit board tokens. A domain and a token that resolve to the same board are scraped once.
Output example
{"itemType": "job","company": "Stripe","domain": "stripe.com","ats": "greenhouse","boardToken": "stripe","jobId": "8023928","title": "Account Executive, Bridge","department": "Sales","team": null,"location": "London","remote": false,"employmentType": null,"postedAt": "2026-07-30T10:59:38.000Z","updatedAt": "2026-08-04T11:02:31.000Z","url": "https://stripe.com/jobs/search?gh_jid=8023928","descriptionText": "Who we are ...","salaryRaw": null,"isNew": true,"aiStatus": "skipped","seniority": null,"function": null,"techStack": null,"summary": null}
Company signal (AI on):
{"itemType": "company-signal","company": "Stripe","domain": "stripe.com","ats": "greenhouse","openRoles": 42,"rolesByFunction": { "Engineering": 18, "Sales": 9 },"newSinceLastRun": 3,"topTechStack": ["Python", "Go", "Kubernetes"],"buyingSignals": [{"signal": "Data platform roles mention Snowflake and dbt.","category": "data","evidenceUrls": ["https://stripe.com/jobs/search?gh_jid=8023928"]}],"relevanceToFocus": 76,"outreachAngle": "They are staffing a warehouse team, which lines up with a tooling conversation.","aiStatus": "enriched"}
openRoles counts jobs that match your filters, before maxJobsPerCompany. Saved job rows are the newest of those, up to the cap. relevanceToFocus is null when Signal focus is empty.
How much does it cost?
Pay per event. Paid plans (Bronze, Silver, Gold, Platinum, Diamond) share one price. There is no apify-default-dataset-item charge. The platform still adds its standard Actor-start compute event.
| Event | When it is charged | Free plan | Paid plans |
|---|---|---|---|
job-scraped (primary) | After a job row is saved | $0.0015 | $0.001 |
job-ai-enriched | After that job row is saved and Gemini filled the AI fields | $0.006 | $0.003 |
company-signal | After the company row is saved and the AI signal was produced | $0.08 | $0.03 |
Worked examples on a paid plan:
| Run | Events | Price |
|---|---|---|
| 1,000 jobs, AI off | 1,000 × job-scraped | $1.00 |
| 1,000 jobs, all AI enriched | 1,000 × job-scraped + 1,000 × job-ai-enriched | $4.00 |
| 10 company signals | 10 × company-signal | $0.30 |
| 1,000 AI jobs + 10 signals | all three | $4.30 |
On the free plan the same 1,000-job sticker would be $1.50, but a free run is capped (below) so it cannot reach that volume. Free-plan users do not generate developer revenue; the caps exist so Gemini spend stays bounded. A stats-only company row (AI off, or the model call failed) is saved and not charged as company-signal.
The run also stops when your max total charge (maxTotalChargeUsd / the charging manager) cannot fit the next event. Rows already saved stay in the dataset. The run ends successfully with stoppedReason: "charge-limit" in RUN-STATS.
The finish log and estimatedChargeUsd use the Actor price tier, not the paid sticker by default. A paying Apify account (APIFY_USER_IS_PAYING=1) can still be on the Actor FREE tier (APIFY_USER_PRICING_TIER=FREE), which is $0.0015 per job. Forty jobs on that tier are $0.0600, not $0.0400. The log names the tier and the unit price. If the charging manager reports a numeric event price, that price is used instead. Volume caps (200 / 20 / 2) still follow the free Apify plan, separate from this price.
Free-plan limits
Detected with APIFY_USER_IS_PAYING (1 means a paid plan, 0 means free). A platform run with the variable missing is treated as free. Local runs without the variable are not capped.
| Cap | Per run on the free plan |
|---|---|
| Jobs saved | 200 |
| AI-enriched jobs | 20 |
| Company signals | 2 |
Companies are handled in input order. Put the accounts you care about first. When a cap is hit the Actor logs a clear warning, writes it into RUN-STATS, and finishes without failing the run.
maxAiJobs is split across companies. The remainder goes to earlier companies. If a company has fewer jobs than its share, the unused slots are redistributed the same way.
Limits and behaviour
- One HTTP call per board. No browser. Greenhouse uses
content=true, Lever usesmode=json, Ashby usesincludeCompensation=true. - Descriptions are plain text, capped at 30,000 characters. Recruiter emails, phone numbers and names in recruiting sentences are replaced with
[redacted]. - If a confirmed board is empty or errors, the Actor retries 3 times with backoff (400ms, 800ms), logs a warning that names the company and ATS, and records it under
emptyCompaniesinRUN-STATS. That company does not fail the run. - Newest
postedAtis kept when a cap applies. Jobs with no date sort last and are kept when a date filter is on. - The seen-jobs snapshot is the full board id list when a company finishes. "New" means the id was not on the previous board snapshot, not "it did not fit in maxJobsPerCompany last time".
- Default memory is 512 MB. Raise it only if you pass a very long company list with full descriptions.
- AI model defaults to
gemini-3.5-flash-lite(GEMINI_MODEL). The client sends JSON schema output and does not sendthinkingConfig(that field returns 400 on gemini-3.5). Failures retry 3 times, then the row is saved withaiStatus: "failed"and the AI event is not charged.
FAQ
Is this legal? The Actor calls the public, documented Greenhouse Job Board API, Lever Postings API and Ashby Job Posting API. It does not log in, does not use a browser, and does not submit applications. Recruiter names and emails found in descriptions are stripped before storage and before any model call. This is not legal advice; you are responsible for how you use the data.
How is this different from other ATS scrapers? Several Store Actors scrape one ATS, or require you to already know the board token, and they often have no date filter. This Actor starts from a domain, covers Greenhouse, Lever and Ashby, diffs new jobs for scheduled monitoring, and adds an optional company-level buying signal priced below older $0.05 "intent" Actors. Raw job price on paid plans is $0.001, under typical $0.0015–$0.0025 competitors.
What if the company does not use these three ATS products? The run continues. RUN-STATS.warnings explains that no board was detected. Other systems (Workday, Lever-hosted custom domains, iCIMS) are out of scope.
Will AI run without a key? No. Scraping still runs. Set the secret GEMINI_API_KEY on the Actor to enable enrichment. Use a paid Gemini key so prompts are not used to train Google's models.
Can I schedule "only new jobs"? Yes. Turn on Only new since last run, save a task, and schedule it. Leave it off for the Store demo and for any backfill. The first run returns the current matching jobs and writes the baseline.
Does one bad company fail the run? No. Empty boards, HTTP errors and detection misses are warnings. Other companies are still saved.
How do I connect it? Run it from the Apify API, schedules, Make, n8n, Zapier or the Apify MCP. The dataset is the integration output. RUN-STATS in the default key-value store is the run summary. Seen-job ids live in the named store hiring-intent-seen-jobs, which persists across runs for that user.
RUN-STATS
{"jobsPushed": 40,"jobsAiEnriched": 0,"companySignalsCharged": 0,"warnings": [],"emptyCompanies": [],"chargeLimitReached": false,"stoppedReason": "completed","freePlan": false,"chargeTier": "FREE","jobScrapedPriceUsd": 0.0015,"chargePriceSource": "pricing-tier","estimatedChargeUsd": 0.06}
stoppedReason is completed, charge-limit or free-plan-cap.