Multi-ATS Jobs Feed + Change Alerts
Pricing
from $1.50 / 1,000 job items
Multi-ATS Jobs Feed + Change Alerts
Job postings from the official public job-board APIs of Greenhouse, Lever, Ashby, SmartRecruiters, Workable and Recruitee in one normalized schema. Scheduled runs return only new, updated and removed jobs.
Pricing
from $1.50 / 1,000 job items
Rating
0.0
(0)
Developer
Tallow Lane Labs
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
14 hours ago
Last modified
Categories
Share
One job feed for every company you follow, from Greenhouse, Lever, Ashby, SmartRecruiters, Workable and Recruitee, in one clean schema. Schedule it, and each run returns only what changed: new jobs, updated jobs and closed jobs.
Built and maintained by Tallow Lane Labs.
What it does
- Reads the official public job-board APIs that each ATS publishes for career sites. There is no browser, no HTML scraping of job pages and no proxies, so runs are fast, cheap and stable.
- Normalizes every posting into one schema: title, department, team, employment type, remote/hybrid/onsite, locations, salary range (when the company publishes it), posted date, job URL, apply URL, and the description as plain text, Markdown and/or HTML.
- Change alerts. In
changesmode the Actor remembers every board between runs, in a named key-value store, and outputs only:new: postings that weren't there last runupdated: postings whose title, location, salary, team, description, URLs and so on changed. The record lists the changed fields and the previous title, department, location and URL.removed: postings that disappeared, meaning the job was closed or filled.
- Snapshot mode returns the full current list every run.
- Accepts whatever you paste: a job-board URL (
https://jobs.lever.co/palantir), anats:slugshortcut (greenhouse:stripe), a company careers page that embeds one of these boards (the Actor detects the ATS), or a bare slug (the Actor tries each ATS). - Filters: keywords, exclude keywords, locations, departments, workplace type, and posted within N days.
Typical uses include recruiting and sourcing alerts, sales signals ("company X just opened 12 data roles"), competitive hiring intel, job-board aggregation, and labor-market research.
Input
| Field | Default | What it does |
|---|---|---|
boards (required) | – | List of boards. Examples: https://boards.greenhouse.io/stripe, lever:palantir, lever:eu:somecompany (Lever EU), https://jobs.ashbyhq.com/ramp, smartrecruiters:Ubisoft2 (case-sensitive), https://apply.workable.com/huggingface/, bunq.recruitee.com, https://www.example.com/careers |
mode | changes | changes = only new/updated/removed since the last run. snapshot = every live posting. |
changeTypes | all three | Which change types to output. |
emitBaseline | true | On the first run of a board in changes mode, output all current jobs (marked baseline). Turn it off to start silently and alert only on later changes. |
keywords / excludeKeywords | – | Case-insensitive substring match on the job title. |
locations, departments | – | Case-insensitive substring match on any location (city, region, country) or on department and team, e.g. Germany, Remote, Engineering. |
workplaceTypes | – | remote, hybrid, onsite, unknown. |
postedWithinDays | – | Only jobs posted in the last N days. |
maxJobsPerBoard | – | Caps output per board per run (a cost control). |
includeDescriptions | true | Include job descriptions. Turning it off makes runs smaller and faster. |
descriptionFormats | text, markdown | Any of text, markdown, html. |
redactContactInfo | false | Masks e-mail addresses and phone numbers inside descriptions. |
includeUnlisted | false | Ashby only: also return postings the company marked unlisted. |
stateStoreName / stateKeyPrefix | multi-ats-jobs-feed-state / – | Where change-tracking state lives. Use a different name or prefix for each independent watch list. |
resetState | false | Forget the stored state and start a new baseline. |
detectFromCareerPages, probeBareSlugs | true | Turn off automatic ATS detection. |
boardConcurrency, maxConcurrency | 3 / 4 | Politeness limits. Per-ATS request spacing is built in and can't be lowered. boardConcurrency also sets memory use; see Memory. |
removalSafetyMinJobs | 5 | If a board that had at least this many jobs suddenly returns 0, the Actor treats it as an outage and does not report mass removals. |
Example input
{"boards": ["https://boards.greenhouse.io/stripe","https://jobs.lever.co/palantir","ashby:ramp","smartrecruiters:Ubisoft2","https://apply.workable.com/huggingface/","bunq.recruitee.com"],"mode": "changes","keywords": ["engineer", "data"],"workplaceTypes": ["remote", "hybrid"]}
Then put it on a schedule (daily is the recommended default; see Choosing a schedule) and connect the dataset to a webhook, Slack, Google Sheets or Zapier.
Output
One dataset record per job (snapshot or baseline) or per change. Errors for individual boards are written as recordType: "error" records and aren't charged. The other boards keep running.
{"recordType": "change","changeType": "new","changedFields": [],"id": "ashby:ramp:34413f8d-26bf-4bbc-8ade-eb309a0e2245","ats": "ashby","board": "ramp","companyName": "ramp","jobId": "34413f8d-26bf-4bbc-8ade-eb309a0e2245","requisitionId": null,"title": "Security Engineer, Cloud","department": "Engineering","team": "Backend","employmentType": "full_time","employmentTypeRaw": "FullTime","workplaceType": "hybrid","isRemote": false,"location": "New York, NY (HQ)","locations": ["New York, NY (HQ)", "Remote (Canada)", "Remote (US)", "Miami, FL"],"city": "New York City","region": "NY","country": "USA","salary": { "min": 211400, "max": 290600, "currency": "USD", "interval": "year", "text": "$211.4K - $290.6K" },"postedAt": "2026-04-07T17:12:35.753Z","firstSeenAt": "2026-10-27T13:33:02.101Z","detectedAt": "2026-10-27T13:33:02.101Z","url": "https://jobs.ashbyhq.com/ramp/34413f8d-26bf-4bbc-8ade-eb309a0e2245","applyUrl": "https://jobs.ashbyhq.com/ramp/34413f8d-26bf-4bbc-8ade-eb309a0e2245/application","descriptionText": "ABOUT RAMP\n\nRamp is building the smart infrastructure for finance teams ...","descriptionMarkdown": "# **About Ramp**\n\nRamp is building the smart infrastructure for finance teams ..."}
An updated record has the same shape, plus changedFields (for example ["title", "salary"]) and previous, which holds the last known title, department, location and URL. Other fields are tracked by hash, so the record tells you which ones changed but not their old values.
A removed record carries the last known title, company, department, location and URLs, plus firstSeenAt and lastSeenAt. A new record can carry lateDiscovery: true when the posting's postedAt is older than the previous run, meaning the ATS only started listing it now.
Each run also saves a RUN_SUMMARY record in the run's default key-value store. It holds per-board job counts, change counts, pages fetched, warnings and HTTP stats.
The dataset has three views: Overview, Changes and Errors.
Pricing (pay per event)
| Event | Price | When it's charged |
|---|---|---|
| Job item | $0.0015 | Each job returned in snapshot mode, and each baseline job on the first changes run of a board |
| Job change | $0.002 | Each new, updated or removed record on later changes runs |
| Board check | $0.001 | Each board checked in a run, changed or not. A board with more than 1,000 live jobs counts once per started 1,000 jobs (a 2,400-job board = 3 checks). |
| Actor start | $0.005 | Once per run (Apify's standard start event; the same price at 256, 512 or 1024 MB) |
You don't pay for error records, boards that fail (not found, rate-limited), or jobs your filters exclude, and memory doesn't change the price. The start fee and board checks cover the cost of fetching and comparing your boards on runs that find nothing new, so frequent schedules stay cheap for small watch lists and fair for big ones. RUN_SUMMARY lists the board checks charged for each board.
Examples
- Watching 6 companies (about 1,500 jobs): a run with no changes costs $0.011 ($0.005 + 6 × $0.001), about $0.33 a month on a daily schedule.
- Watching 50 companies with about 7,500 open jobs: the first run (baseline) costs about $11.31 ($11.25 for 7,500 jobs + $0.05 for 50 board checks + $0.005). After that, a daily run that finds 150 changes costs about $0.36 ($0.30 + $0.05 + $0.005), roughly $11 a month. A day with no changes costs $0.055.
- Turning off
emitBaselinemeans the first run costs only the start fee and board checks, and you pay only for changes after that. - A full snapshot of 1,000 jobs from 5 boards costs about $1.51.
- Set Maximum cost per run in Apify to put a hard cap on any run. The Actor stops cleanly at the cap and saves its state, so jobs it didn't deliver arrive on the next run instead of being lost.
Choosing a schedule
Every run pays the base fees (the $0.005 start plus the board checks), even when nothing changed. At 50 boards that is $0.055 a run ($0.05 for 50 board checks + $0.005 start). Over 30 days, including the start fee:
| Schedule | Runs a month | Base cost a month at 50 boards, no changes |
|---|---|---|
| Daily (recommended default) | 30 | about $1.65 |
| Every 6 hours (fresher change alerts) | 120 | about $6.60 |
| Hourly | 720 | about $39.60 |
Daily suits most watch lists. Use every 6 hours if you need change alerts sooner. Hourly also works, but at 50 boards it costs about $39.60 a month in base fees alone. Changes are billed on top of these base fees at $0.002 per new, updated or removed job.
Memory
The default 512 MB is enough for almost every watch list. What matters is not how many jobs you watch in total but how many are being processed at the same time: the Actor works on boardConcurrency boards at once (3 by default), so memory depends on your 3 largest boards. Measured peaks:
| Memory | Use it when | Measured peak |
|---|---|---|
| 256 MB | Your 3 largest boards together have at most 1,500 live jobs | ~190 MB at 1,500 jobs in flight |
| 512 MB (default) | Your 3 largest boards together have at most 7,500 live jobs and no single board has more than 5,000 | ~240 MB for a 29-board, 7,700-job no-change run; ~305 MB for its baseline; ~370 MB for 30,000 jobs across 20 boards; ~400 MB at 7,500 jobs in flight |
| 1024 MB | Any board has more than 5,000 live jobs, or your 3 largest boards together have more than 7,500 | ~630 MB for three 5,000-job boards; ~700 MB for one 15,000-job board |
Above about 15,000 jobs in flight, set boardConcurrency to 1 (memory then depends on your single largest board) and put very large boards in their own task. Lowering boardConcurrency also lets a 512 MB run handle a board of up to ~7,500 jobs. Each run's RUN_SUMMARY.memory shows the jobs in flight and the recommended memory, and the run log warns if you should move up.
Limits and good to know
- Public postings only. Internal, unlisted (except Ashby with
includeUnlisted) and draft postings aren't available through these APIs. - Field coverage varies by ATS. Greenhouse doesn't publish employment type, and its remote/hybrid value is inferred from the location text. Salary appears only when the company publishes it (pay transparency). For Lever and Ashby,
companyNameis the board slug because those APIs don't return a display name. - SmartRecruiters pagination isn't fully stable: the same posting can show up twice or be missing from one pass. The Actor makes up to three passes and checks every apparent removal, and every "new" posting, against the posting's own page. A job is only reported removed when SmartRecruiters confirms it's gone or inactive. Descriptions need one extra request per posting, but only for postings that are output.
- Workable rate-limits its public widget API heavily, so the Actor spaces Workable requests about 5.5 s apart. Large watch lists of Workable boards take longer.
- Identifiers: SmartRecruiters company IDs are case-sensitive. An unknown SmartRecruiters ID returns an empty list rather than an error, so the Actor reports it as "no postings found / check the identifier".
- Change tracking is per board and per state store. Running the same board from two different tasks? Give each task its own
stateStoreNameorstateKeyPrefix. - Normalizer updates (for example, better remote/hybrid detection) are applied silently and don't trigger "updated" alerts.
Data sources, terms and privacy
All data comes from the job boards' own public, unauthenticated endpoints, which exist for building career sites:
| ATS | Endpoint | Docs |
|---|---|---|
| Greenhouse | boards-api.greenhouse.io/v1/boards/{token}/jobs | https://developers.greenhouse.io/job-board.html |
| Lever | api.lever.co/v0/postings/{site} (EU: api.eu.lever.co) | https://github.com/lever/postings-api |
| Ashby | api.ashbyhq.com/posting-api/job-board/{name} | https://developers.ashbyhq.com/docs/public-job-posting-api |
| SmartRecruiters | api.smartrecruiters.com/v1/companies/{id}/postings | https://developers.smartrecruiters.com/docs/posting-api |
| Workable | apply.workable.com/api/v1/widget/accounts/{subdomain} | https://help.workable.com/hc/en-us/articles/115012771647 |
| Recruitee | {company}.recruitee.com/api/offers/ | https://docs.recruitee.com/reference/intro-to-careers-site-api |
The Actor identifies itself with a descriptive User-Agent. It spaces requests to each ATS, backs off on HTTP 429 and 5xx (honoring Retry-After), and never submits applications.
Personal data: the Actor collects only what's in the public job posting. Recruiter and creator names, avatars, internal mailbox addresses, application questions and internal IDs that some APIs return are dropped. Specifically, it drops SmartRecruiters creator, Recruitee mailbox_email, and Greenhouse internal metadata and compliance fields. A posting's description can contain a contact address the company chose to publish, such as an accommodations or recruiting mailbox. That text is kept as published, the run summary counts how many postings contain one, and redactContactInfo masks them. You're responsible for using the data in line with the job boards' terms and the laws that apply to you, such as GDPR and CCPA.
Changelog
- 0.1: First version. Six ATSs, changes and snapshot modes, PPE pricing (job item, job change, board check, start). SmartRecruiters team, department and employment type are compared by their stable IDs, because the API sometimes returns a page with localized labels (for example Chinese) and that must not show up as an "updated" job.
Support
Questions or issues: open an issue on this Actor's Issues tab, or email Tallow Lane Labs at wdeornellas+tallowlanelabs@gmail.com.