Job Pay-Transparency Extractor avatar

Job Pay-Transparency Extractor

Pricing

from $2.00 / 1,000 job successfully checkeds

Go to Apify Store
Job Pay-Transparency Extractor

Job Pay-Transparency Extractor

Extract pay ranges from public Greenhouse, Lever and Ashby job feeds, with evidence and company disclosure summaries. Flag postings without an extracted range. Informational extraction, not legal compliance advice. By Steadfeed.

Pricing

from $2.00 / 1,000 job successfully checkeds

Rating

0.0

(0)

Developer

Steadfeed

Steadfeed

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

a day ago

Last modified

Categories

Security

Limited permissions

Share

Job Pay-Transparency Extractor — Steadfeed

Version 0.2.0 — job-level evidence, company summaries and guarded pay-per-event billing.

Extract salary/pay ranges from public Greenhouse, Lever (US and EU) and Ashby job-board feeds. Get job-level evidence and a summary of the share of checked postings with an extracted pay range. Useful for research and data quality review—not legal determinations.

Informational data extraction, NOT legal compliance advice. A missing range in a posting does not by itself mean a law was broken. This Actor does not determine whether a law applies, whether an employer has complied, or whether a posting is lawful. Location filters do not encode legal requirements. A false disclosure flag means the extractor did not obtain a two-bound range from the fetched sources, not that no range exists anywhere.

Input example

{
"boards": [
{"ats": "greenhouse", "slug": "discord", "company": "Discord"},
{"ats": "lever", "slug": "palantir", "company": "Palantir", "region": "us"},
{"ats": "ashby", "slug": "ramp", "company": "Ramp"}
],
"maxJobsPerCompany": 10,
"locations": [],
"countries": [],
"remoteOnly": false,
"titleKeywords": []
}

Supply board slugs, not arbitrary URLs. A display name is optional; otherwise the source company name or board slug is used. Lever EU uses "region":"eu". Other ATS types do not accept a region.

Strict validation

Invalid input fails before any network request or dataset output: unknown keys (including nested board keys), missing/empty boards, unsupported ATS, unsafe slugs, duplicate boards, invalid region/country codes, wrongly typed booleans, invalid caps and empty/oversized filter strings. There is no silent type coercion. Boards: 1–20. Cap: integer 1–1000 per board, default 100. Filter arrays: up to 30 entries, each 1–100 characters. Runtime also validates every output record before writing results.

Filters and scope

  • locations: case-insensitive literal words/phrases in listed locations. Examples: California, New York, Colorado. Short abbreviations can be ambiguous; full state names are preferable.
  • countries: uppercase ISO country codes (US, GB, DE), or EU expanded to 27 EU members. Best-effort inference from source country fields, explicit country names/aliases, or US state names/codes. City-only postings can be excluded. This is not geocoding or jurisdiction analysis.
  • remoteOnly: explicitly remote jobs only. Hybrid and unknown are excluded. Missing workplace information stays unknown (null), not automatically onsite.
  • titleKeywords: case-insensitive literal substrings, such as engineer or designer; not regular expressions.
  • OR within a filter list, AND between groups. Filters are applied before the check cap; cap follows feed order, not random sampling.
  • Company percentages cover only jobs checked after filters and caps, not necessarily the company's full workforce or board. partial flags caps, feed truncation or unavailable details affecting results. jobsAvailable and jobsMatched make this scope visible.

Extraction

  1. Fetch official public board feeds without authentication or proxies. Lever pagination is supported, capped at 20,000 listed jobs per board for safety.
  2. Request Greenhouse public job detail with pay_transparency=true for selected jobs. Read Lever salaryRange and Ashby compensation tiers/components.
  3. Prefer structured salary/wage fields. Ashby equity and bonus components are not treated as salary. A source-labelled Salary component can itself be OTE; compensationBasis marks explicit OTE/base wording where present, and raw evidence must be reviewed. Unspecified does not mean base salary. If structured numeric pay is unavailable, scan description text for explicit numeric money ranges.
  4. Preserve all distinct structured geographic/level tiers in payRanges. Do not combine them into an artificial minimum-to-maximum envelope. When there are multiple ranges, flat min/max/currency/period fields are null; inspect the array.
  5. No currency conversion or annualization. Greenhouse cents are divided by 100 as specified by its source fields. Unknown periods/currencies remain null; bare $ does not imply USD. Hour/year/month/week/day are supported when stated. Greenhouse frequently does not state a structured interval, so it remains unknown.

Evidence and interpretation

  • payRangeDisclosed: true when at least one extracted range has both valid numeric bounds, including explicitly equal bounds.
  • payFound: true if at least one structured/parsed numeric range or single structured bound was obtained.
  • extractionStatus: parsed_range, single_value, missing, unparseable, or source_error.
  • missing means no recognized numeric range/pay evidence was found. unparseable is a conservative review flag for pay-related text with numbers that could not be extracted; it can include irrelevant numbers/benefits. Neither status is a legal conclusion.
  • Single structured values/open-ended ranges do not count as a disclosed two-bound range. Single-value description formats are not parsed in this version.
  • paySource: structured, description, or null. Structured evidence is the raw field JSON or source-provided component summary; description evidence is the nearby raw text snippet.
  • structuredCheckComplete: false if Greenhouse detail could not be fetched. A description range can still be reported, but a job with failed detail and no extracted pay is a source_error, not counted as undisclosed.

Structured fields take precedence; description alternatives are not used to override structured pay. This Actor does not reconcile contradictions between source fields and page text.

Output

Default dataset contains recordType: "job" records and one recordType: "company_summary" per input board. Select by recordType in your downstream tools. Three Console views project overview, evidence and company-summary columns; they do not filter out other record types. COMPANY_SUMMARIES in the default key-value store is the clean summaries-only array. SUMMARY contains run totals and filters.

Job records include company/slug, title, job ID, locations, inferred countries, remote flag and workplace type, ATS/region, HTTPS job URL, source posted/updated dates, check timestamp, flat pay min/max/currency/period, raw pay text, evidence source, payRanges, disclosure flag and extraction status. Source dates remain null if not supplied; they are not replaced with the check date. Dates in machine-readable output use ISO 8601.

Company records include available/matched/checked job counts, budget stop reason, disclosed count, no-range-found count (including unparseable/single-value postings), unparseable count, source-error count, detail-lookup-failure count, disclosure percentage, status and partial flag. Percentage = disclosed / successfully checked × 100, rounded to two decimals; null when none were checked. Board fetch errors produce an error summary, not invented empty/undisclosed jobs. Budget-exhausted boards are skipped without network requests and get free skipped_limit summaries with unknown (null) available/matched counts. A partially checked board gets limit_reached and a charge_limit stop reason. Greenhouse detail failures without pay evidence are excluded from the percentage denominator.

Formal schemas live in .actor/record_schema.json, .actor/dataset_schema.json, .actor/output_schema.json, and .actor/input_schema.json.

Limits and responsible use

  • Public ATS data only, three providers in this version. Workable, SmartRecruiters, Workday, PDFs, screenshots and custom career pages are not supported.
  • Text parsing is heuristic, primarily English compensation wording with common currencies and numeric range formats. Unusual formats, languages, open-ended amounts, tax/net distinctions, OTE/total compensation phrasing and hidden page elements may be missed or misclassified. Review evidence before relying on it.
  • A feed is not guaranteed to match every rendered job page. Ranges may vary by location, level or employment type. Retain the raw evidence and source URL for review.
  • Best-effort country inference can be ambiguous (e.g. state/country names); use explicit location filters and spot-check. It is not a legal location resolver.
  • Source sites may block or rate-limit requests, move feeds or change schemas. Timeouts, per-host spacing and bounded retries are included. Errors are visible in summaries. No captcha bypass, login scraping or proxy evasion.
  • Each output record is schema-validated before writing; job results are streamed as checks complete. At most 20 boards × 1000 selected checks; large full-feed responses and the collected run result can still use memory. Use smaller caps for testing. Source order means capped results are not representative samples.
  • No customer messages, auto-applications, legal claims, secrets, private job data or credential fields are requested. Do not put credentials in input. Pricing changes are account settings, never performed by Actor code. The code emits only the explicit job-checked event when PPE is configured.

Local development

Requires Node.js 22+.

npm ci
npm test
apify validate-schema
npm run test:live
# Optional local Actor SDK execution (local storage only):
apify run --input-file inputs/smoke.json

Live test snapshots, results and manual spot-check notes are kept locally under test-results/ and excluded from deployment. Unit tests do not call live APIs.

Pay-per-event billing and budget limits

When PPE is configured, $0.002 per successfully checked job is charged through job-checked, once per complete check, whether or not a pay range was found. Filtered-out jobs are not checked or charged. Company summaries, source-error records and incomplete source fetches (including a Greenhouse detail failure with a usable description fallback) do not incur this job event.

The platform's separate Actor Start event is $0.00005 per GB of allocated memory, minimum one event, matching Steadfeed's Auditor. It can apply even when there are no checked jobs or the run fails. No automatic per-dataset-item event is permitted; the Actor refuses to run under PPE if that synthetic event is present or the configured job/start rates differ from these documented rates. Ordinary platform usage costs follow the effective Apify pricing configuration; owner development runs can still incur resource costs.

The Actor checks the remaining job-checked capacity before fetching another board, before checking another job, and again immediately before charging. It stops checks cleanly when no event fits the user's maximum total charge, logs the reason, saves partial results, and emits the remaining company summaries without job charges. The run's SUMMARY.billing shows charged-job count, uncharged job rows and whether the limit was reached. Start charges reduce the available event budget as tracked by Apify's charging manager.

Rows are validated and written before the explicit Actor.charge({eventName: "job-checked", count: 1}) call, so a failed output write does not incur a new job charge. If the charge API fails after a row was saved, the run fails visibly rather than retrying or misreporting it as a source error; that last saved row may be uncharged. Rerunning a job is a new check and may be charged again. Unpriced private development runs do not invoke product-event charging.

Deployment safeguards

Visibility and pricing are managed through the official Apify API/Console, not by this Actor at runtime. Publication and live pricing changes require explicit owner approval. Verify the intended account and private build results before release, and verify the effective PPE record and public listing afterward. This project needs no Git remote or PR.

Credits and version

Version 0.2.0. Public-feed endpoint patterns, text/ISO/workplace helpers and polite HTTP handling adapted from the existing Steadfeed ATS Job Board Monitor. This standalone Actor does not change that Actor or share its monitor state.