ATS Jobs & Change Feed avatar

ATS Jobs & Change Feed

Pricing

from $1.00 / 1,000 results

Go to Apify Store
ATS Jobs & Change Feed

ATS Jobs & Change Feed

Normalize public Greenhouse, Lever, Lever EU, Ashby, and SmartRecruiters job boards, then emit stable job-change events.

Pricing

from $1.00 / 1,000 results

Rating

0.0

(0)

Developer

Roman V

Roman V

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

2 days ago

Last modified

Categories

Share

Normalize public Greenhouse, Lever, Lever EU, Ashby, and SmartRecruiters job boards into one stable schema. Use snapshot mode for a current export or changes mode to emit NEW, UPDATED, and REMOVED records against a prior snapshot.

Why this Actor

  • Uses public job-board APIs without applicant data, cookies, or customer credentials.
  • Preserves the existing provider normalization, canonical_job_id, and content_hash contract.
  • Deduplicates jobs before delivery and keeps the previous representative of a duplicate URL group when that job is still observed.
  • Keeps source errors in the OUTPUT summary instead of emitting them as job records.
  • Preserves the last complete state for an incomplete source observation, so a temporary outage, rejected provider row, or result cap does not create false removals on the next run.
  • Uses SmartRecruiters public Posting API list and numeric-detail endpoints only. It never follows redirects, converts returned references into requests, or accesses applicant or administration data.

Input

Use at least 256 MB of memory. Larger inputs within the documented limits may need more memory; this minimum is not a measured hosted peak.

Add each public board to sources with its provider and board token. For example, use stripe for Greenhouse, spotify for Lever, or ashby for Ashby. The optional companyName becomes the display name when the provider does not return one.

SmartRecruiters boardName is the case-sensitive company identifier from its public job URL. The provider checks list and detail identity before accepting a posting. It retains distinct numeric posting IDs even when two postings share a URL, title, reference number, language, or default-ad relationship.

The default sample checks the three listed boards and selects at most three matching jobs per board, nine jobs total. Larger boards therefore normally report PARTIAL with snapshot_complete=false. Larger exports are opt-in: set the existing maxJobsPerSource and maxTotalJobs limits explicitly.

For a recurring change feed, set outputMode to changes and select the prior run's complete_state_dataset_id in previousDatasetId. The read-only dataset picker works with limited Actor permissions. You can instead pass normalized items in previousJobs, but do not provide both inputs.

The Actor accepts at most 10,000 prior-state records. It reads up to 10,001 records to detect an oversized baseline even when maxTotalJobs is smaller. Malformed, inaccessible, conflicting, or oversized baselines fail before any source request or dataset-item delivery. The run can still incur the platform's automatic Actor-start charge; it does not deliver billable dataset items. Exact repeated records are collapsed. Different records with the same canonical_job_id are rejected with PREVIOUS_DATASET_INVALID; do not combine multiple observations of the same job into a baseline.

Complete state and partial observations

Filters run before maxJobsPerSource and maxTotalJobs. If either cap clips matching jobs, the run is PARTIAL, snapshot_complete is false, and prior rows from the affected source are carried into the saved state. Failed sources and sources with rejected provider rows use the same carry-forward rule. SmartRecruiters also reports EMPTY, COMPLETE, TRUNCATED, PARTIAL, or MALFORMED in each source diagnostic. Only validated EMPTY and COMPLETE observations authorize removals. Pagination changes, missing or short pages, duplicate IDs, list/detail contradictions, malformed detail fields, and detail request failures keep useful fresh rows but carry the unobserved prior rows. Carried rows are state only: they are not emitted as default-dataset change events, and their original scraped_at value is preserved.

The candidate state labels rows as FRESH or CARRIED in _snapshot_freshness. fresh_records, carried_records, clipped_records, rejected_records, and planned_state_records report its exact composition. After persistence is confirmed, saved_state_records reports the stored count and the summary reports the same dataset ID in current_snapshot_dataset_id and complete_state_dataset_id; the latter is the explicit chaining field. snapshot_complete describes observation completeness, not whether carried state was saved.

Changing query, location, remoteOnly, or publishedAfter starts a new filtered view and suppresses removals from the old view. Removing a source drops that source from the selected view; adding it again produces new events. A legacy snapshot without scope metadata receives a conservative migration. Unknown legacy scope is never promoted by carry-forward. A removal requires that the individual prior row has verified matching scope and its source was observed completely in the current run. An unrelated legacy or failed source does not suppress a verified removal from a complete source. Rows already lost by an older incomplete implementation cannot be reconstructed.

If every source fails, the Actor carries bounded prior state, emits no change events, and exits with a nonzero all-sources-failed status. Recovery can still compare against that carried state.

Delivery and billing integrity

In pay-per-event mode, the Actor advances state only after every planned dataset item has a confirmed charged_count acknowledgement. A zero or partial acknowledgement, an invalid receipt, or a write exception produces a stable failure summary and does not save or advertise a next snapshot. Confirmed counts remain exact; a write exception reports an explicit lower bound because the attempted item's delivery is unknown. Retry with the same prior snapshot.

The platform's automatic Actor-start charge is separate from dataset-item delivery. This repair does not change pricing or metering configuration.

Output

Each default-dataset item includes stable identity, normalized title, company, location, description, direct application and job URLs, provider provenance, timestamps, and change_type. The OUTPUT record includes source diagnostics, event counts, scope and completeness fields, delivery accounting, and the next state dataset ID when both delivery and state persistence are confirmed.

SmartRecruiters output preserves validated raw applyUrl and postingUrl links. Its content comparison removes only oga, trid, and the five named UTM keys on jobs.smartrecruiters.com; other query keys, paths, trailing slashes, fragments, and unknown-host links remain semantic. Language, default-ad, active, and visibility changes are semantic. UUID, job-ad ID, job ID, and reference-number-only changes are not. When includeRaw is enabled, the list and detail records omit creator and avatarUrl fields.

Each SmartRecruiters source is finite: 100 records per list page, at most 50 list pages, 5,050 list-plus-detail requests, 512 KiB per response, 64 MiB of received response bodies, 15 seconds per request, and 45 seconds for the source. Detail requests are sequential. Blocking DNS, TLS, headers, and body reads run behind a killable worker-process deadline. Redirects, proxies, credentials, cookies, and retries are not used. These limits can make a source partial and are reported in provider_diagnostics for later unit-cost checks.

Scope

This Actor reads published employer job postings only. It does not submit job applications, collect candidate data, bypass access controls, or read private ATS administration APIs.