ATS Jobs Scraper API - Greenhouse, Workday, Lever & Ashby
Pricing
from $1.60 / 1,000 job or change records
ATS Jobs Scraper API - Greenhouse, Workday, Lever & Ashby
ATS job scraper & jobs API for Greenhouse, Workday, Lever, Ashby, Workable, Personio, Recruitee & Teamtailor career pages. Scrape normalized job postings with apply URLs, or a change feed of new, updated & closed jobs. No false closures when a source fails.
Pricing
from $1.60 / 1,000 job or change records
Rating
0.0
(0)
Developer
Kamer Ozkan
Maintained by CommunityActor stats
0
Bookmarked
3
Total users
2
Monthly active users
2 hours ago
Last modified
Categories
Share
Monitor company hiring changes across eight ATS platforms - directly from career pages, with reliable closed-job events and no false closures when a source fails.
ATS Job Change Feed is a source-direct ATS job scraper API, career page monitor, and
hiring-signal feed for Greenhouse, Workday, Lever, Ashby, Workable, Personio, Recruitee,
and Teamtailor. Give it company websites or ATS board URLs and receive one normalized schema
with stable job IDs plus deterministic CREATED, UPDATED, and CLOSED events.
Use it to track competitor hiring, trigger B2B sales workflows, build job alerts, or maintain
a reliable recruiting-analytics feed. In scheduled changes mode, unchanged jobs produce no
result records, so frequent monitoring does not repeatedly bill the same snapshot.
Unofficial integration. This Actor is independently developed and is not affiliated with, sponsored by, or endorsed by Greenhouse, Lever, Ashby, Workable, Recruitee, Personio, Teamtailor, or Workday. Product names are used only to identify compatible public career-board formats. It reads public company job postings only - never candidates, applications, resumes, or private ATS data.
Why this ATS job scraper is different
Most job scrapers return a fresh snapshot and leave change tracking, deduplication, and failure handling to you. This Actor is designed as an operational job change feed.
| Typical snapshot scraper | ATS Job Change Feed |
|---|---|
| Returns every open job again | Emits only real changes in changes mode |
| Downstream system must infer closures | Emits explicit CLOSED events |
| Empty response can look like mass closure | Failed or malformed sources retain prior state |
| One ATS-specific schema | One schema across eight ATS formats |
| Requires known ATS URLs | Can discover boards from company websites |
| Generic locations and text | DACH locations, KldB, languages, skills, and salary signals |
| Duplicate suppression is opaque | Cross-ATS duplicates remain visible and auditable |
What can this career page monitor do?
Job postings often appear on the employer's own ATS career board before they reach job aggregators. The Actor connects to those source endpoints and:
- reads public career-board JSON, XML, and RSS without a browser, login, or candidate account;
- discovers supported ATS boards from public company and careers pages;
- normalizes eight ATS formats into one stable schema;
- assigns deterministic IDs based on the ATS board and source job ID;
- assigns stable company identities and marks likely cross-ATS duplicates;
- records
firstSeenAt,lastSeenAt, published, and updated timestamps; - detects new, edited, and closed jobs across scheduled runs;
- merges multi-location variants instead of returning duplicates;
- recognizes DACH job types such as
WORKING_STUDENT,APPRENTICESHIP, andMINIJOB; - enriches DACH locations, KldB groups, language requirements, skills, and salary ranges;
- keeps a failed board's previous snapshot, preventing false mass closures;
- can bootstrap a versioned 10,000-board candidate catalog and process it in deterministic lifecycle-managed chunks.
Start monitoring jobs in 30 seconds
Paste one or more company career pages. The Actor discovers supported ATS boards and creates the first baseline:
{"boards": [],"companyUrls": ["https://linear.app/careers"],"outputMode": "changes","stateKey": "competitor-hiring-watchlist","emitInitialSnapshotChanges": true}
Save the input as a task and schedule it every ten minutes. Later runs return only new, updated, and closed jobs. If a board fails, its last successful snapshot is preserved and no synthetic closures are emitted.
Supported career boards
| ATS | Accepted board URL |
|---|---|
| Greenhouse | https://job-boards.greenhouse.io/{company} |
| Greenhouse legacy | https://boards.greenhouse.io/{company} |
| Lever | https://jobs.lever.co/{company} |
| Lever EU | https://jobs.eu.lever.co/{company} |
| Ashby | https://jobs.ashbyhq.com/{company} |
| Workable | https://apply.workable.com/{company} |
| Recruitee | https://{company}.recruitee.com |
| Personio | https://{company}.jobs.personio.de |
| Teamtailor | https://{company}.teamtailor.com |
| Workday | https://{tenant}.wd{cluster}.myworkdayjobs.com/{career-site} |
You can also provide an explicit ats and slug. A single Workable job URL does not contain
the company's account slug and cannot be used as a board URL. Workday requires a board URL
because both its tenant and career-site name are needed.
Teamtailor is read from its public jobs RSS feed and requires no API token. Workday search
results are fetched with consistency-checked pagination. When includeDescriptions is
enabled, the Actor also requests every Workday job-detail record; disabling descriptions
reduces request count substantially on large Workday boards.
Company-domain discovery
When the ATS URL is unknown, pass a company home page or careers page in companyUrls.
The Actor scans a small, bounded set of public pages and follows career-related links. Every
discovered board and job keeps its origin URL and discovery-page provenance.
{"boards": [],"companyUrls": ["https://linear.app/careers"],"outputMode": "changes","stateKey": "linear-watchlist"}
The discovery result is written to the default key-value store as DISCOVERY_REPORT. A URL
that cannot be resolved does not stop other companies by default. Set
failOnDiscoveryError to true when complete discovery is required.
Monitor explicit ATS board URLs
Use changes mode for monitoring. The first run emits every current job as CREATED.
Later runs return only jobs that appeared, changed, or closed.
{"boards": [{"url": "https://job-boards.greenhouse.io/greenhouse","company": "Greenhouse"},{"url": "https://jobs.lever.co/ae-2","company": "AE"}],"outputMode": "changes","stateKey": "competitor-watchlist","includeDescriptions": true,"includeCompensation": true,"emitInitialSnapshotChanges": true}
For continuous monitoring, save this input as an Apify task and run it on a schedule. Keep
the same stateKey for that watchlist and do not overlap runs using the same key.
Input reference
| Field | Type | Default | Description |
|---|---|---|---|
boards | array | [] | Board URL strings or { url, ats, slug, company } objects |
companyUrls | string[] | [] | Company home or careers URLs to scan for supported ATS boards |
catalog | object | disabled | Bootstrap/process the bundled 10K candidate catalog and configure chunk/lifecycle rules |
outputMode | string | changes | changes, jobs, or both |
stateKey | string | default | Snapshot namespace; use a unique value per independent watchlist |
includeDescriptions | boolean | true | Return descriptions and detect description edits |
includeCompensation | boolean | true | Normalize salary data when exposed by the ATS |
emitInitialSnapshotChanges | boolean | true | Emit CREATED for every job on the first run |
maxConcurrency | integer | 10 | Boards fetched in parallel, from 1 to 50 |
requestTimeoutSecs | integer | 30 | Timeout for each request attempt |
maxRequestRetries | integer | 2 | Retries for rate limits, server errors, and network failures |
failOnBoardError | boolean | false | Fail the run instead of returning a partial result |
discoveryMaxPages | integer | 5 | Maximum public pages scanned for each company URL |
failOnDiscoveryError | boolean | false | Fail if any company URL cannot be resolved |
freshnessSlaMinutes | integer | 10 | Source-to-detection SLA target used in freshness reports |
At least one item is required across boards and companyUrls, unless catalog.enabled
is true.
Output modes
changes- recommended for scheduled monitoring. Full jobs on the first run, then only changes.jobs- every currently open job on every run.both- open jobs followed by change records. This can produce two billable records for the same job on the first run.
Output example
The records below follow one real job on a real Recruitee career board through its
lifecycle. The normalized job record and the CREATED event are unedited output from
live scheduled runs on 2026-07-28. The UPDATED and CLOSED events were produced by
the production change engine replaying the same board with one salary edit and one
closure, because neither had yet occurred naturally on this board when this page was
written; every field is real engine output. Long descriptions are shortened here.
1. Normalized job record (jobs mode)
One remote role posted across four locations, merged into a single record instead of
four duplicates. The Cologne location activates DACH enrichment: structured
locationDetails, dachRegion, a conservative KldB occupational group, and extracted
language requirements. Fields the source does not publish stay null.
{"recordType": "job","status": "OPEN","id": "fb650323dd54bfe995e5ed74be41edf3","jobKey": "recruitee:jobs:2659009","ats": "recruitee","boardSlug": "jobs","sourceJobId": "2659009","company": "Tellent","title": "Senior Backend Engineer (Node.js)","locations": ["Remote job","Amsterdam, Noord-Holland, Netherlands","Cologne, Nordrhein-Westfalen, Germany","Poznań, Wielkopolskie, Poland"],"locationDetails": [{"raw": "Remote job","city": null,"region": null,"country": null,"countryCode": null,"isDach": false,"isRemote": true},{"raw": "Amsterdam, Noord-Holland, Netherlands","city": null,"region": null,"country": null,"countryCode": null,"isDach": false,"isRemote": false},{"raw": "Cologne, Nordrhein-Westfalen, Germany","city": "Cologne","region": "North Rhine-Westphalia","country": "Germany","countryCode": "DE","isDach": true,"isRemote": false},{"raw": "Poznań, Wielkopolskie, Poland","city": null,"region": null,"country": null,"countryCode": null,"isDach": false,"isRemote": false}],"dachRegion": "DE","primaryLocation": "Remote job","department": null,"team": null,"employmentType": "FULL_TIME","employmentTypeRaw": "fulltime_permanent","workplaceType": "REMOTE","workplaceTypeRaw": "remote","salary": null,"descriptionText": "Note: The listed salary range is based on the salary range for the Netherlands. Salary ranges may vary by loca ...","descriptionHtml": "(HTML description, shortened here)","publishedAt": "2026-07-23T11:22:12.000Z","updatedAt": "2026-07-23T11:22:12.000Z","sourceUrl": "https://careers.tellent.com/o/senior-backend-engineer-nodejs","applyUrl": "https://careers.tellent.com/o/senior-backend-engineer-nodejs/c/new","boardUrl": "https://jobs.recruitee.com","sourceApiUrl": "https://jobs.recruitee.com/api/offers","boardOrigin": "explicit","discoveredFrom": null,"companyId": "e4a478fc861a91b37c616f24","companyDomain": null,"canonicalJobId": "4810f6bb90b4cd1e72be89863716be64","duplicateGroupId": null,"isDuplicate": false,"duplicateOf": null,"kldb": {"code": "43","title": "Informatik-, Informations- und Kommunikationstechnologieberufe","level": 2,"version": "KldB 2010 – Fassung 2020","confidence": 0.9,"method": "title-keyword-v1"},"languageRequirements": [{"language": "English","level": "C1","evidence": "rimary area of expertise. Strong communication skills and fluency in English. What we offer Hybrid or remote working setup across Amsterdam, the Cologne area, and Po"}],"skills": ["Node.js","React"],"firstSeenAt": "2026-07-28T12:27:33.329Z","lastSeenAt": "2026-07-28T12:27:33.329Z","scrapedAt": "2026-07-28T12:27:33.329Z"}
2. CREATED and UPDATED change events (changes mode)
Every change event carries convenient top-level fields for tables plus the full
normalized job in job. The CREATED event below is the real first observation of the
job above.
{"recordType": "change","eventType": "CREATED","eventId": "fa7c2b45bb559a4feaa57995a7914972","detectedAt": "2026-07-28T12:27:33.329Z","jobKey": "recruitee:jobs:2659009","ats": "recruitee","boardSlug": "jobs","company": "Tellent","title": "Senior Backend Engineer (Node.js)","primaryLocation": "Remote job","sourceUrl": "https://careers.tellent.com/o/senior-backend-engineer-nodejs","boardOrigin": "explicit","discoveredFrom": null,"companyId": "e4a478fc861a91b37c616f24","canonicalJobId": "4810f6bb90b4cd1e72be89863716be64","duplicateGroupId": null,"isDuplicate": false,"duplicateOf": null,"changedFields": [],"job": "(full normalized job record embedded, identical to the record above)"}
When the employer later publishes a salary range, the fingerprint diff emits exactly one
UPDATED event, and changedFields names precisely what changed:
{"recordType": "change","eventType": "UPDATED","eventId": "18f9d2662c7a695f4995e59fd2004de1","detectedAt": "2026-07-28T13:27:31.212Z","jobKey": "recruitee:jobs:2659009","ats": "recruitee","boardSlug": "jobs","company": "Tellent","title": "Senior Backend Engineer (Node.js)","primaryLocation": "Remote job","sourceUrl": "https://careers.tellent.com/o/senior-backend-engineer-nodejs","boardOrigin": "explicit","discoveredFrom": null,"companyId": "e4a478fc861a91b37c616f24","canonicalJobId": "4810f6bb90b4cd1e72be89863716be64","duplicateGroupId": null,"isDuplicate": false,"duplicateOf": null,"changedFields": ["salary","updatedAt"],"job": {"status": "OPEN","title": "Senior Backend Engineer (Node.js)","salary": {"min": 65000,"max": 80000,"currency": "EUR","interval": "year","summary": "EUR 65,000 to 80,000 per year","source": "structured","confidence": 0.95},"firstSeenAt": "2026-07-28T12:27:33.329Z","lastSeenAt": "2026-07-28T13:27:31.212Z","note": "remaining job fields unchanged, shortened here"}}
3. CLOSED: what the feed guarantees when a job disappears
- Explicit closure. The job's final known state is delivered with
status: "CLOSED"and its completefirstSeenAt/lastSeenAthistory, so downstream systems never have to infer closures from absence. - No stale content. Stored snapshots deliberately drop descriptions, so a
CLOSEDrecord reports them asnullinstead of replaying outdated text. - No false mass closures. A failed, throttled, or malformed board keeps its previous
snapshot and emits no
CLOSEDevents at all. - No noise. The unchanged third job on this board produced no record in the same run, and an immediate repeat run on an unchanged board writes zero rows and bills zero events (measured: a 22-role board billed 22 events on first observation, 0 on the repeat).
{"recordType": "change","eventType": "CLOSED","eventId": "68f701d5a7f1d6fdfe428090d3c77b9e","detectedAt": "2026-07-28T13:27:31.212Z","jobKey": "recruitee:jobs:2650251","ats": "recruitee","boardSlug": "jobs","company": "Tellent","title": "Team Lead Customer Support - DACH","primaryLocation": "Remote job","sourceUrl": "https://careers.tellent.com/o/team-lead-customer-support-dach","boardOrigin": "explicit","discoveredFrom": null,"companyId": "e4a478fc861a91b37c616f24","canonicalJobId": "a2862a89b543c4ad7f2bb7576fc9b217","duplicateGroupId": null,"isDuplicate": false,"duplicateOf": null,"changedFields": ["status"],"job": {"recordType": "job","status": "CLOSED","id": "cfcce6be6280bf1e233895acbbcb511c","jobKey": "recruitee:jobs:2650251","ats": "recruitee","boardSlug": "jobs","sourceJobId": "2650251","company": "Tellent","title": "Team Lead Customer Support - DACH","locations": ["Remote job","Poznań, Wielkopolskie, Poland"],"locationDetails": [{"raw": "Remote job","city": null,"region": null,"country": null,"countryCode": null,"isDach": false,"isRemote": true},{"raw": "Poznań, Wielkopolskie, Poland","city": null,"region": null,"country": null,"countryCode": null,"isDach": false,"isRemote": false}],"dachRegion": null,"primaryLocation": "Remote job","department": null,"team": null,"employmentType": "FULL_TIME","employmentTypeRaw": "fulltime_fixed_term","workplaceType": "REMOTE","workplaceTypeRaw": "remote","salary": null,"descriptionText": null,"descriptionHtml": null,"publishedAt": "2026-07-14T07:24:04.000Z","updatedAt": "2026-07-14T12:48:50.000Z","sourceUrl": "https://careers.tellent.com/o/team-lead-customer-support-dach","applyUrl": "https://careers.tellent.com/o/team-lead-customer-support-dach/c/new","boardUrl": "https://jobs.recruitee.com","sourceApiUrl": "https://jobs.recruitee.com/api/offers","boardOrigin": "explicit","discoveredFrom": null,"companyId": "e4a478fc861a91b37c616f24","companyDomain": null,"canonicalJobId": "a2862a89b543c4ad7f2bb7576fc9b217","duplicateGroupId": null,"isDuplicate": false,"duplicateOf": null,"kldb": null,"languageRequirements": [],"skills": [],"firstSeenAt": "2026-07-28T12:27:33.329Z","lastSeenAt": "2026-07-28T12:27:33.329Z","scrapedAt": "2026-07-28T12:27:33.329Z"}}
Company identity and cross-ATS duplicates
Jobs receive a deterministic companyId. Discovered boards use the input company domain;
explicit boards fall back to a normalized company name. Across successful boards in the same
run, likely duplicate jobs are clustered using normalized title, compatible location and
employment type, description similarity, and publication timing.
The Actor does not silently delete duplicate records. It marks non-canonical members with
isDuplicate=true, sets duplicateOf to the richer canonical source record, and gives every
cluster member the same canonicalJobId and duplicateGroupId. Filter isDuplicate=false
when a single-record feed is desired. Keeping the source records makes migration periods and
partial board failures auditable.
DACH enrichment
Every job includes structured locationDetails and a dachRegion (DE, AT, CH, or
MULTI) when a supported country or major city is recognized. Titles are mapped
conservatively to the official two-digit occupational main groups from the
KldB 2010 - revised 2020.
The result includes its level, version, confidence, and the title-keyword-v1 method so it
cannot be confused with an official individual-case coding decision.
languageRequirements extracts German, English, French, Italian, Dutch, and Spanish
requirements with explicit CEFR levels or a normalized qualitative level. skills contains
canonical matches for common engineering, data, cloud, enterprise, and go-to-market tools.
When the ATS has no structured compensation, plausible EUR or CHF ranges can be recovered
from salary-context sentences in the description. Every salary reports source and
confidence.
Change detection and reliability
Each successfully fetched board receives its own snapshot in the persistent
ats-job-change-feed-state key-value store. Business fields are fingerprinted individually,
so UPDATED records identify exactly which normalized fields changed.
Safety behavior:
- HTTP rate limits, server errors, and network failures are retried.
- A malformed HTTP 200 response is rejected instead of interpreted as an empty board.
- Failed boards keep their previous snapshot and emit no
CLOSEDevents. - Location ordering is canonicalized for comparison to avoid false updates.
- Snapshot state advances only after that board's complete output is stored.
- If the run's maximum charge cannot cover a whole board, that board is deferred atomically. Increase the maximum charge and rerun; its prior snapshot remains intact.
The RUN_SUMMARY record reports discovery, successful, failed, and budget-deferred board
counts. DISCOVERY_REPORT lists the pages scanned, resolved boards, and per-company errors.
Ten-minute monitoring and SLA
For a ten-minute feed, save the Actor input as an Apify task and create an exclusive schedule
with cron expression */10 * * * *. Exclusive scheduling prevents a new run from starting
while the prior one is still active. Keep one stable stateKey and use the same timezone for
operational dashboards; UTC is the simplest default.
Every run writes FRESHNESS_REPORT to the default key-value store. It includes per-board
fetch duration, source timestamp age, failures, and p50/p95/max detection latency. Baseline
CREATED events are excluded. Later CREATED events use publishedAt, UPDATED events use
updatedAt, and events without a compatible source timestamp are not presented as measured
SLA samples. complianceRate=null therefore means “no measurable events,” not 0% compliance.
Use publishing/examples/ten-minute-freshness-monitor.json as the task input.
Push changes into Make, n8n, or your own webhook
Add a webhook for the ACTOR.RUN.SUCCEEDED event (Actor page > Integrations). In
scheduled changes mode a run's dataset contains only the new CREATED, UPDATED,
and CLOSED records, so your endpoint receives the run notice and fetches exactly the
delta in one call:
GET https://api.apify.com/v2/datasets/{resource.defaultDatasetId}/items?format=json&clean=1
In Make and n8n you can skip the webhook entirely: both have a native Apify integration with a run-finished trigger and a get-dataset-items action, so a scheduled monitor-then-notify flow takes two modules.
Pilot quality benchmark
Every run also writes QUALITY_REPORT. It measures board and discovery success rates, unique
companies toward the 500-company pilot target, field completeness, ATS distribution,
duplicate groups, DACH coverage, enrichment coverage, and errors grouped by ATS. This keeps
the pilot exit criteria tied to observed data instead of a successful process exit alone.
The bundled pilot was generated from versioned public board candidates and validated on 26 July 2026. It selected 500 live boards across all eight adapters and normalized 15,923 open jobs. A rate-limited immediate repeat emitted zero observed changes or synthetic closures on successful sources. The same fixed 500-board list was then rechecked with per-provider concurrency limits: 500/500 boards succeeded and returned the same aggregate job count. The machine-readable reports and fixed input are included with the release artifacts.
Historical catalog candidates are never presented as verified companies. A candidate enters the live pilot only after its adapter returns a structurally valid response.
10,000-board catalog
Set catalog.enabled=true and catalog.bootstrapBundled=true to initialize the bundled
10,000-board candidate catalog. Its URLs were derived from versioned Common Crawl URL Index
snapshots; every new record starts as PENDING, not ACTIVE.
{"boards": [],"companyUrls": [],"catalog": {"enabled": true,"key": "production-10k","bootstrapBundled": true,"process": true,"chunkSize": 100},"outputMode": "changes","stateKey": "production-10k","includeDescriptions": false,"includeCompensation": false,"emitInitialSnapshotChanges": false,"maxConcurrency": 20}
The catalog uses PENDING, ACTIVE, RETRY, STALE, and EXPIRED states. Successful
fetches activate an entry. Failures get a retry deadline without advancing the job snapshot.
Repeatedly failing old entries become tombstoned EXPIRED records; a later company-domain
rediscovery can reactivate them as PENDING.
Every catalog run writes CATALOG_REPORT and CATALOG_TASK_PLAN. The task plan contains a
deterministic input for each chunk. A single rotating task does not provide ten-minute
freshness across 10,000 boards; create one exclusive scheduled task per chunk, then verify
account concurrency and CU cost in the cloud before enabling the full schedule.
StepStone scope decision
StepStone is intentionally not included as a ninth adapter. Its current general terms explicitly prohibit scraping content for another purpose, while its documented JobFeed API is a contracted customer/HR-tech interface for sending listings to StepStone rather than a public bulk-read API. The separate StepStone feasibility phase therefore triggered its predefined compliance stop-loss. No proxy, CAPTCHA bypass, or StepStone data collection code is shipped. Reconsider it only with written bulk-read/reuse permission or an appropriately licensed official read API.
Pricing behavior
The billable job-result event is one record written to the default dataset. In changes
mode, unchanged jobs produce no result events at all - if nothing moved on a board, that run
costs you nothing beyond the Actor start fee.
This is verified behaviour, not a claim: a first observation of a board with 22 open roles billed 22 events; the very next run against the same board found no changes and billed zero.
What daily monitoring actually costs
Free-plan example: one board with 500 open roles, checked every day for 30 days, with a typical ten changes a day:
| Job or change events | Actor start | Total | |
|---|---|---|---|
| Day 1 baseline | $1.00 | $0.005 | $1.005 |
| Each later day | $0.02 | $0.005 | $0.025 |
| 30-day total | $1.58 | $0.15 | $1.73 |
The first run bills the initial 500 records. The following 29 runs bill only ten changed records each, plus the start event on every run. Plan discounts can reduce these prices.
Apify's maximum total charge setting is respected, and the exact current prices are displayed in the Apify Store pricing section before every run.
Apify's maximum total charge setting is respected. This Actor does not silently advance a board's snapshot after withholding some of its results for budget reasons.
Use cases
- Monitor target companies for new or closed roles
- Build a job-board feed from primary company sources
- Generate sales signals from hiring activity
- Track competitor headcount priorities
- Analyze role, location, salary, and remote-work changes
- Create DACH-focused job alerts for Ausbildung, Werkstudent, or Minijob roles
This Actor is also exposed to AI agents through Apify's MCP server (mcp.apify.com): an agent can discover it by search and run it with the same pay-per-event billing, with no separate integration.
Data and privacy
This Actor reads public company job postings only. It does not:
- log in to an ATS;
- access internal jobs, candidates, applications, resumes, or contact details;
- submit job applications;
- send source data to an external enrichment service.
For company-domain discovery, it requests at most discoveryMaxPages public HTML pages per
input URL. Private, local, credential-bearing, nonstandard-port, and private-DNS targets are
rejected to prevent server-side request forgery.
Inputs, outputs, and snapshots remain in the Apify storages associated with the user's account. Users are responsible for ensuring their use of public job data complies with applicable laws and source-site terms.
Current limitations
- Company-domain discovery reads public static HTML. JavaScript-only sites, custom career stacks, or pages without a supported ATS link can remain unresolved; provide an explicit board URL in those cases.
- Company identity uses the discovered domain or normalized company name; legal-entity hierarchies and renamed/acquired companies can still require downstream overrides.
- Duplicate detection is conservative metadata, not destructive suppression. Same-titled requisitions can require downstream review.
- KldB, language, skill, location, and description-salary enrichment is deterministic heuristic metadata. It should be reviewed before regulated, statistical, or compensation decisions; the built-in city and skill dictionaries are intentionally bounded.
- Source providers can change their public formats; partial-run safety prevents a format failure from creating false closures.
- The bundled 10K catalog is a candidate inventory, not a claim that all 10,000 historical URLs are currently live. Lifecycle state and live fetch reports provide that distinction.
- Full 10K/10-minute operation requires multiple scheduled tasks and enough cloud concurrency. Local benchmarks cannot substitute for the final account-specific CU and pricing check.
- Snapshot writes are atomic per board, but dataset output and snapshot persistence cannot be committed as one cross-storage transaction. A crash between them can repeat an event.
Support
Open an issue on the Actor page with the board URL, run ID, expected behavior, and the relevant output field. Do not post API tokens, candidate information, or other secrets.