# Changelog of ATS Jobs Feed: Greenhouse & Lever Job Postings (`madpai/ats-jobs-feed`) Actor

- **URL**: https://apify.com/madpai/ats-jobs-feed/changelog.md
- **Full Actor documentation**: https://apify.com/madpai/ats-jobs-feed.md

## Changelog

### 0.1.4 (2026-11-06)

#### Fixed

- `ats-jobs-feed-3` (public runs under `LIMITED_PERMISSIONS`): opening, reading or writing the named state store `ats-jobs-feed-state` now fails open instead of crashing the run. Under `LIMITED_PERMISSIONS`, if a pre-existing `ats-jobs-feed-state` store is inaccessible, the Actor falls back to `ats-jobs-feed-state-lp`; if named key-value stores are denied altogether (such as restricted test tokens), it logs a warning, delivers jobs as a first run and sets `statePersisted: false` and `stateStoreError` in `OUTPUT_SUMMARY`.
- Empty, missing or placeholder `boards` input (for example `{}` or `{"boards": []}`) now emits one free `invalid-input` dataset item and finishes cleanly instead of failing the platform run. Single-string `boards`, `keywords` and `locations` values are also accepted without throwing.

### 0.1.3 (2026-11-05)

#### Fixed

- bulk-url-status-checker-2 (DNS rebinding): every request, including each redirect hop and robots.txt, now connects only to an address checked at connect time. A host name that resolves to a private, loopback, link-local or reserved address at connect time is refused (free `blocked-private`, no retry), even if an earlier lookup returned a public address.

### 0.1.2 (2026-11-02)

#### Fixed

- `changesOnly` with `maxJobsPerBoard` below the board's size no longer sells old jobs as `new`. 0.1.1 only recorded delivered jobs, so each run delivered (and charged `job-changed` for) the next batch of never-delivered jobs as `new`. Now the first run for a board records every matching job, delivers up to the cap and records the rest silently (not output, not charged). Later runs report only jobs that really appeared or closed.
- `maxJobsPerBoard` in `changesOnly` mode now caps changes per run (closed first, then new). Changes over the cap or past the max charge stay pending and come out on a later run, charged once. A pending new job is kept out of the state until it's delivered, and a pending closed job is kept with status `closed`.

#### Changed

- The saved state holds only job ID, status (`open`/`closed`) and `firstSeenAt`/`lastSeenAt` per job. No titles, locations, URLs or other text (compliance). State format version 2.
- Closed items carry only `source`, `board`, `jobId`, `url` (posting URL rebuilt from the ID), `changeType`, `firstSeenAt`, the new `lastSeenAt`, `checkedAt` and `notice`. The other fields are `null`.
- Default mode: the first run for a board also records jobs over the cap (as `existing` later). Delivery and charging are unchanged.
- `OUTPUT_SUMMARY`: `stateMigrated`, and per board `baselined`.

#### Migration

- 0.1.x state is read with every extra field dropped, then rewritten as version 2. Boards from old state get one silent baseline run (closed jobs are still reported), because the old state can't tell new jobs from never-delivered ones. If such a board fails on that run, it stays marked (`pendingBaseline`) until it's read.

### 0.1.1

- README: note that descriptions may contain names from the original posting.

### 0.1.0 (unreleased)

#### Added

- Jobs from Greenhouse (Job Board API) and Lever (Postings API, US and EU) boards, normalized to one item shape.
- Change detection per state key in the named key-value store `ats-jobs-feed-state`: `new`, `existing`, and `closed` (reported once, then dropped).
- Filters: keywords (title, department, team), locations, remote only. Optional plain-text description, up to 5,000 characters.
- `changesOnly` mode: only new and closed jobs are output.
- Free error items: `board-not-found`, `robots-disallowed`, `fetch-failed`, `invalid-input`.
- Politeness: about 1 request per second per host, robots.txt per API host (RFC 9309) with Crawl-delay (up to 30 s), Retry-After (up to 30 s), and a pause after 3 consecutive HTTP 429s.
- Personal data: person fields are never copied. Emails (including obfuscated forms) and phone numbers in descriptions are redacted.
- `notice` on every item and in `OUTPUT_SUMMARY`.

#### Pricing

- Pay per event, placeholder in `.actor/pay_per_event.json`: `job` $0.0015 (default mode, per new or existing job; closed items free) and `job-changed` $0.0015 (changesOnly mode, per new or closed job). Listing & Ops sets the final price.
