ATS Hiring Intent - Buying Signals (Greenhouse + 4 ATSes) avatar

ATS Hiring Intent - Buying Signals (Greenhouse + 4 ATSes)

Pricing

from $8.00 / 1,000 enriched job fetcheds

Go to Apify Store
ATS Hiring Intent - Buying Signals (Greenhouse + 4 ATSes)

ATS Hiring Intent - Buying Signals (Greenhouse + 4 ATSes)

Aggregates jobs across Greenhouse, Lever, Ashby, SmartRecruiters, and Workday with auto ATS detection from a domain. Gemini classifier emits per-company buying signals (expanding-sales, evaluating-CRM, platform-build-out). Monitor mode tracks 100s of companies weekly via KV deltas.

Pricing

from $8.00 / 1,000 enriched job fetcheds

Rating

5.0

(1)

Developer

Omar Eldeeb

Omar Eldeeb

Maintained by Community

Actor stats

2

Bookmarked

4

Total users

0

Monthly active users

8 days ago

Last modified

Security

Limited permissions

Share

What does ATS Hiring Intent do?

ATS Hiring Intent aggregates open job postings across five major applicant tracking systems — Greenhouse, Lever, Ashby, SmartRecruiters, and Workday — into one normalized feed and turns the raw job data into per-company buying signals ready for a sales / RevOps pipeline.

Give it a list of company domains. It auto-detects each company's ATS, pulls every open role, normalizes function and seniority across platforms, and (in signals mode) runs each role through Gemini 3.5 Flash Lite to extract intent tags (expanding-sales, evaluating-CRM, series-B-build, platform-build-out, …) and infer the specific vendors / tech stack the company is buying or building on. One composite CompanySignal per refresh tells your reps which prospects just lit up — Stripe is expanding-sales (8/8 AE roles posted, AI-customer focus, vendor hypothesis: OpenAI, Anthropic, NVIDIA).

Zero anti-bot. Zero proxies needed by default. No login. Every data source is an official public ATS endpoint. Replaces $99-$15K/seat tools (Apollo, ZoomInfo, Bombora, Cognism) on the hiring-signal job-to-be-done.

Why use ATS Hiring Intent?

  • B2B sales / SDRs — find every prospect actively hiring for a role that signals they're buying your product. Hiring 5 DevOps + 2 Platform Engineers = evaluating IaC + observability tooling. Hiring a VP Sales + 3 AEs = expanding GTM motion. Apollo only counts headcount; this actor explains what they're buying.
  • RevOps & GTM intelligence — score your ICP list weekly, build prospect lists from buying-intent tags, replace your stale $1-5K/mo intent-data line item.
  • Recruiters & talent intelligence — track hiring velocity at competitors, see which functions/seniorities they're loading up on.
  • VC / corporate development — detect Series A → C ramp signals, exec hires, geographic expansion. Headcount-delta tracking with KV-checkpointed monitoring.
  • Founders doing competitive intel — type a competitor's domain, see every open role, every detected vendor, every team they're scaling.
  • AI / data teams — a clean 30+ field unified ATS schema across 5 platforms with deterministic taxonomy normalization. Drop straight into a vector store or BI tool.

How to use

  1. Pick a mode:
    • discover — given domains, detect which ATS each one uses (cheap onboarding pass).
    • jobs — fetch normalized jobs only, no AI.
    • signals — jobs + Gemini intent tags + per-company composite signal (the value tier).
    • monitor — same as signals but only emits deltas vs. the previous run. Best with a daily/weekly schedule.
  2. Provide either companyDomains (list of domains — auto-detected) or atsTargets (direct ATS slugs like greenhouse:stripe, lever:palantir, ashby:linear, smartrecruiters:visa, workday:nvidia.wd5/NVIDIAExternalCareerSite).
  3. Optionally filter by keywordFilter, functionFilter (engineering / sales / …), seniorityFilter, locationFilter, remoteOnly, postedWithinDays.
  4. Run. Job rows (and discovery rows in discover mode) land in the run's default dataset. Company signals and change events land in two separate named datasets in your account — see Where each row type lands.
  5. Export as JSON, CSV, or Excel from the Apify Console — or pull via API / webhook.
  6. Want weekly intent updates? Set mode to monitor and schedule the actor. Subsequent runs only emit new/changed/closed roles + the latest composite signal.

Free trial: the first 10 billable result events of every run (job rows, company signals, change events, discoveries — whichever come first) are free. The per-run start fee is always charged. Enough to check output shape and signal quality on a small run before committing budget.

Input examples

1. Discover which ATS your top 5 prospects use.

{
"mode": "discover",
"companyDomains": ["stripe.com", "linear.app", "notion.so", "vercel.com", "anthropic.com"]
}

2. Pull every open engineering role at 3 companies, no AI.

{
"mode": "jobs",
"companyDomains": ["stripe.com", "anthropic.com", "linear.app"],
"functionFilter": ["engineering"],
"seniorityFilter": ["senior", "lead", "principal"],
"maxJobsPerCompany": 100
}

3. Generate weekly buying signals across a 50-company prospect list.

{
"mode": "signals",
"companyDomains": [
"stripe.com", "notion.so", "linear.app", "vercel.com", "anthropic.com"
],
"maxJobsPerCompany": 50,
"intentBatchSize": 20,
"postedWithinDays": 30
}

4. Monitor 200 companies — emit deltas only on the second-and-subsequent run. (Schedule daily.)

{
"mode": "monitor",
"companyDomains": ["acme.com", "..."],
"maxJobsPerCompany": 100,
"remoteOnly": false
}

5. Skip auto-detection — pass direct ATS targets.

{
"mode": "signals",
"atsTargets": [
"greenhouse:stripe",
"lever:palantir",
"ashby:linear",
"smartrecruiters:visa",
"workday:nvidia.wd5/NVIDIAExternalCareerSite"
]
}

Output

Where each row type lands

Each row has a type field. Rows are split across datasets so each one has a single shape:

Row typeModesDatasetFetch via API
jobjobs, signals, monitorThe run's default datasetGET https://api.apify.com/v2/actor-runs/<RUN_ID>/dataset/items?clean=true&token=<TOKEN>
ats_discoverydiscoverThe run's default datasetSame as above
company_signalsignals, monitor (or emitCompanySignals: true)Named dataset signals-<RUN_SUFFIX> in your accountGET https://api.apify.com/v2/datasets/~signals-<RUN_SUFFIX>/items?clean=true&token=<TOKEN>
change_eventmonitor (or incrementalMode: true)Named dataset changes-<RUN_SUFFIX> in your accountGET https://api.apify.com/v2/datasets/~changes-<RUN_SUFFIX>/items?clean=true&token=<TOKEN>

<RUN_SUFFIX> is the last 12 characters of the run ID. Example: run Bef84LkoIt1oR2lnT writes its company signals to the dataset signals-LkoIt1oR2lnT. The leading ~ means "a dataset with this name in the account that owns the token"; <your-username>~signals-<RUN_SUFFIX> works too.

Good to know:

  • The run log prints the exact dataset ID and items URL for each named dataset (lines starting with [output]), and the run's status message names them.
  • Named datasets are not linked to the run, so they do not appear in the run's Storage tab and are not read by integrations that use "the run's dataset" (Google Sheets, Make, Zapier, the Output tab). Open them under Storage → Datasets in the Console, or fetch them via the API above.
  • Named datasets are not deleted automatically when the run's own storage expires. Delete old signals-… / changes-… datasets from Storage → Datasets when you no longer need them.

With the JavaScript client:

const run = await client.actor('constructive_calm/ats-hiring-intent-scraper').call(input);
const suffix = run.id.slice(-12);
const { items: jobs } = await client.dataset(run.defaultDatasetId).listItems();
const { items: signals } = await client.dataset(`~signals-${suffix}`).listItems(); // signals / monitor modes
const { items: changes } = await client.dataset(`~changes-${suffix}`).listItems(); // monitor mode

Sample enriched job record:

{
"type": "job",
"sourceId": "d3bc1ced-3ce4-4086-a050-555055dbb1ff",
"company": "Linear",
"domain": "linear.app",
"atsPlatform": "ashby",
"atsSlug": "Linear",
"title": "Senior / Staff Fullstack Engineer",
"function": "engineering",
"seniority": "senior",
"department": "Product",
"team": "Engineering",
"location": "Europe",
"isRemote": true,
"employmentType": "FullTime",
"postedDate": "2026-04-21T20:13:45.158+00:00",
"applyUrl": "https://jobs.ashbyhq.com/linear/d3bc1ced-3ce4-4086-a050-555055dbb1ff/application",
"intentTags": ["platform-build-out", "going-international"],
"vendorSignals": ["TypeScript", "React", "GraphQL", "PostgreSQL", "Redis", "Kubernetes"],
"intentSummary": "This role indicates a focus on expanding the core product platform and international growth.",
"enrichmentLevel": "enriched"
}

Sample company_signal record:

{
"type": "company_signal",
"company": "Stripe",
"atsPlatform": "greenhouse",
"atsSlug": "stripe",
"signal": "expanding-sales",
"confidence": 1.0,
"evidence": "8/8 open roles match this signal. Examples: Account Executive AI Sales, Account Executive AI Startups, Account Executive Commercial.",
"vendorHypothesis": ["Stripe", "OpenAI", "Anthropic", "NVIDIA", "Shopify"],
"openRolesCount": 8,
"newRolesLast30d": 8,
"topFunctions": [{ "function": "sales", "count": 8 }],
"secondarySignals": [
{ "signal": "series-B-build", "confidence": 0.66, "evidence": "3/8 roles tagged." }
]
}

Data fields (selected)

FieldTypeNotes
typeenumjob | company_signal | ats_discovery | change_event
companystringDisplay name pulled from board metadata when available
atsPlatformenumgreenhouse | lever | ashby | smartrecruiters | workday
functionenumNormalized: engineering, sales, marketing, product, data, design, operations, support, finance, hr, legal, other
seniorityenumintern | junior | mid | senior | lead | principal | director | vp | cxo | unknown
intentTagsarray22-tag fixed taxonomy — see input schema for the full list
vendorSignalsarraySpecific vendor / tech names mentioned in the JD
intentSummarystringOne-line plain-English takeaway from Gemini
salaryMin / salaryMax / salaryCurrencynumbers + ISO codeWhen the ATS exposes pay bands (Greenhouse + Ashby do)
applyUrlstringDirect link to the application page
signal (on company_signal)enumThe dominant intent tag, or general-hiring / shrinking / stable
vendorHypothesisarrayTop 8 vendors aggregated across the company's open roles
changeType (on change_event)enumrole-opened | role-closed | role-updated | salary-changed

How much does it cost?

EventPriceFires when
apify-actor-start$0.01 per GB of run memoryOnce per run. At the default 4 GB memory that is $0.04 per run
basic-job-fetched$0.0015A normalized job without AI fields is emitted (mode jobs; in signals / monitor only for a job the AI could not classify)
enriched-job-fetched$0.008A Gemini-enriched job is emitted (modes: signals, monitor)
company-signal-event$0.05Composite buying signal per company (modes: signals, monitor). Not charged when the AI gave no answer for any of that company's jobs
change-event$0.02Role opened / closed / updated vs. previous run (modes: monitor, or any mode with incrementalMode). An opened or updated role also emits (and charges) its job row; a closed role emits only the change event
ats-discovered$0.005Domain → ATS detection record (mode: discover). Domains whose ATS could not be detected (detectionMethod: "failed"), detected boards that could not be loaded, and direct atsTargets echoed back in discover mode are still listed but not charged

The first 10 billable result events of every run are free (any mix of the events above except apify-actor-start, which is always charged). The run log prints how many events were free and how many were paid.

Typical run costs

ScenarioVolumeCost
Discover ATS for 50 prospects50 discoveries~$0.24
Pull 50 roles each from 10 companies500 basic jobs~$0.78
Weekly signals run, 50 companies × 40 roles2,000 enriched + 50 signals~$18.50
Daily monitor, 200 companies × 30 roles, ~3 deltas/co/day600 change events (+ a job row for each opened/updated role) + 200 signals/day~$22–27/day

All figures include the $0.04 start fee at the default 4 GB memory and the 10 free events per run.

Comparison: Apollo Pro $99/seat/mo (no signals), ZoomInfo + Bombora $3-5K/mo, Clay Growth $1.5-2.5K/mo, Cognism + intent ~$2K/mo. The 50-company weekly run above costs ~$80/mo — same job-to-be-done, ~5-25× cheaper than every comparable.

Tips & advanced options

  • monitor mode is built for scheduling. Set up an Apify schedule (daily or weekly), feed in your prospect list, and only pay for deltas + the latest composite signal: an opened or changed role costs its job row + a change event, a closed role costs a change event, and each company's refreshed signal costs one company-signal-event. The first run charges full freight (everything is "new"); later runs are deltas-only. When nothing changed, the run's status message says so ("No new or changed jobs since the previous run …").

How monitor mode decides what is a change

  • Opened = a posting that was not on the company's board at the last full read and is in scope (passes your filters and fits maxJobsPerCompany). A role that was already on the board and only now fits your cap or filters (for example because another role closed) is recorded silently, not billed as opened.
  • Updated = title, location, or salary band changed (plus the posting's update date on Greenhouse, Lever, Ashby and SmartRecruiters). Workday exposes no real update date, so Workday roles are compared on title, location and salary only.
  • Closed = a tracked role whose posting is gone from a board that was read to the end this run. Closures are not reported (and tracked roles are kept for the next run) when a board fails to load or is rate-limited, lists 0 postings, is larger than the 1,000-posting read limit, or the run stopped before finishing that company (maxItems, maximum charge, error). The status message tells you when that happened. A role that is still open but no longer fits your filters or cap is dropped from tracking without a charge.
  • Changing filters or maxJobsPerCompany re-baselines. On the first run after such a change, roles that were already on the board and now fit are recorded silently (no rows, no charges); postings that are genuinely new since the last full read are still reported as opened. Monitors created before October 29, 2026 re-baseline once on their first run on this version (every company, including companies that had no matching roles before). To get the full current list at any time, run the same input in signals mode.
  • Do not run two monitor runs on the same companies at the same time. Each run compares against the state the previous run saved; two overlapping runs both see the same changes and both deliver and bill them. The actor detects an overlapping run on the same targets and says so in the log and status message, but it does not block it.
  • Progress is saved while rows are delivered (at most every 15 seconds) and when the run is migrated or aborted, so a timeout, abort or migration does not re-bill already delivered roles on the next run. After a hard kill, at most the roles delivered in the last ~15 seconds can be delivered (and billed) once more.
  • AI cost control: monitor runs send only new or changed roles to the AI. Unchanged roles reuse the intent tags from earlier runs for the company signal, and unchanged Workday roles skip their description request (a 200-role Workday company drops from ~4 minutes to ~1 minute per monitor run).
  • Size limit per monitor: tracking state for one input is stored in a single key-value record (about 350 bytes per tracked role; 200 companies × 30 roles ≈ 2 MB, 200 × 100 ≈ 7–8 MB). Keep one monitor under roughly 20,000 tracked roles and split bigger prospect lists across several monitors. If the state ever cannot be saved, the run says so in its status message (it starts with "ERROR"), because the next run would then deliver and bill the same changes again. On boards too large to read to the end, a tracked role not seen for 5 runs stops being tracked (no charge).
  • Workday note — Workday tenants live at tenant.wd{N}.myworkdayjobs.com where N is 1-12. Use workday:tenant.wdN/SiteSegment form. Find the wdN from any URL on their careers page. Some Workday tenants throttle aggressive scraping — switch to RESIDENTIAL proxy if you see 403s.
  • Auto-detection covers ~25K-30K companies out of the box (all Greenhouse, Lever, Ashby, SmartRecruiters customers + most Workday F500). For non-detectable domains, pass a direct atsTargets entry and the actor skips resolution.
  • Filter aggressively to control budget. functionFilter, seniorityFilter, locationFilter, remoteOnly, postedWithinDays all run before Gemini enrichment, so you only pay enrichment cost on the roles that matter.
  • maxJobsPerCompany counts jobs that pass your filters. With maxJobsPerCompany: 5 and functionFilter: ["engineering"] you get up to 5 engineering roles per company, however many other roles the board lists first. On paginated boards (Workday, SmartRecruiters) the actor reads at most 1,000 postings per company.
  • maxItems counts job rows and company-signal rows. Change events are not counted: you get one change event per new or changed job row, plus one per closed role.
  • Workday descriptions. In signals / monitor mode the actor opens each kept Workday role's detail page (one request each, ~0.8 s) so the AI and keywordFilter see the full description. A 200-role Workday company takes about 3 minutes. In jobs mode Workday rows carry no description and keywordFilter matches Workday titles only.
  • A run with 0 rows always says why. The status message reports how many jobs were scanned and how many each filter removed (e.g. 0 jobs matched your filters. Scanned 124 open jobs across 2 companies; removed by functionFilter [legal]: 122 …), or that a job board could not be loaded.
  • intentBatchSize = 20 is the cost-vs-latency sweet spot. Larger batches cost less per job but increase end-to-end latency.
  • Vendor signals are tools/tech mentioned in JDs, not necessarily companies the prospect is buying. A role mentioning "Salesforce" usually means they use Salesforce — sometimes it means they sell to Salesforce customers. Cross-check with intentSummary for context.
  • Cross-ATS dedup — if the same company is somehow listed twice (rare), dedup by (atsPlatform, atsSlug) downstream.
  • Every ATS endpoint used by this actor is publicly published and intended for third-party career-site / aggregator use. No login or API key required.
  • Greenhouse Job Board API, Lever Postings API, Ashby Public Job Posting API, and SmartRecruiters Posting API are all explicitly documented as public — see developers.greenhouse.io/job-board, github.com/lever/postings-api, developers.ashbyhq.com/docs/public-job-posting-api, developers.smartrecruiters.com/docs/posting-api.
  • Workday CXS endpoints serve the same data as the public-facing careers page; we paginate at default 20-per-page with gentle 150ms pacing.
  • The actor is intended for sales intelligence, recruiting research, and market analysis. Do not use the data to discriminate, harass, or auto-apply at scale.
  • Job descriptions are © the posting employer. We store and re-emit them verbatim for analysis. Do not republish full JDs as your own content.

FAQ & support

Q: Which ATSes are supported in v1? A: Greenhouse, Lever, Ashby, SmartRecruiters, Workday. BambooHR, Recruitee, Teamtailor, Personio in v1.1.

Q: What's the difference between signals and monitor? A: signals runs the full pipeline every time. monitor does the same but auto-enables incremental mode — on subsequent runs you only see new / changed / closed roles, plus the refreshed company-signal record. Use monitor for scheduled subscriptions.

Q: Why didn't my domain auto-detect? A: ~10-15% of companies use custom careers pages that don't link to a known ATS host. Pass an atsTargets entry directly. Also try the discover mode in isolation — it tells you what was found and what failed.

Q: Can I get LinkedIn / Indeed jobs? A: No. This actor only scrapes companies' own ATS career pages. LinkedIn aggregations are heavily anti-botted and not worth the proxy cost. The data here is fresher anyway — it's the source ATS, not a 2nd-hand index.

Q: Will the intent classifier hallucinate tags? A: The classifier is constrained to a fixed 22-tag taxonomy and instructed to return ["unknown"] if uncertain. We've validated against ~100 real postings; false-positive rate is < 5%. If you see noise, lower the intentBatchSize to 5-8 (smaller context = sharper inference).

Q: How do I report bugs? A: Open an issue on the actor's Apify page. Include the input JSON and the offending record's sourceId + applyUrl.


Built with TypeScript on the Apify SDK. Zero proxies, zero anti-bot, native fetch. No headless browser, no CAPTCHA.

Changelog

  • October 29, 2026: maxJobsPerCompany now caps the jobs that pass your filters, not the first N postings on the board. Before, a strict filter plus a small cap could return 0 rows even when matching jobs existed. Runs that end with 0 rows now say why in the status message, and a board that is missing or down is reported as such instead of as empty. Workday: up to 1,000 postings per company are now read (previously only the first 40), and job descriptions are fetched again (a URL error had left them empty), so Workday runs return more rows and richer AI tags. Monitor mode billing fixes: Workday roles are no longer reported as "updated" on every run; a role is reported closed only when its board was read completely (no more closures from a failed or rate-limited board, a maxItems cut, or a lowered cap); changing filters or the cap re-baselines instead of billing old roles as opened, and existing monitors re-baseline once on their first run on this version. In discover mode, undetectable domains, detected boards that fail to load, and echoed atsTargets are no longer charged. role-closed rows now carry the real job title. README corrections: company signals and change events are written to named datasets (signals-… / changes-…), not the default dataset; the start fee is $0.01 per GB of memory ($0.04 per run at the default 4 GB), not $0.0001. No output field was added, removed or renamed.
  • October 2026: The intent classifier now uses Google Gemini 3.5 Flash Lite, because the provider retires Gemini 2.5 Flash on 2026-10-20. AI calls are retried on temporary errors and on incomplete answers. Billing correction: a job is charged as enriched-job-fetched only when the AI classified it (as before), and a company-signal-event is no longer charged when the AI gave no answer for any of that company's jobs; the signal row is still delivered. If the AI provider rejects the key or runs out of credit, the run stops using AI, says so in its status message, and still delivers every job as a basic row. No output field was added, removed or renamed.