# Changelog of Ashby Jobs Scraper (`humble-echidna/ashby-jobs`) Actor

- **URL**: https://apify.com/humble-echidna/ashby-jobs/changelog.md
- **Full Actor documentation**: https://apify.com/humble-echidna/ashby-jobs.md

## Changelog

Versions follow MAJOR.MINOR.PATCH (`src/version.py`); Apify shows MAJOR.MINOR from `.actor/actor.json`.
Every run logs its version and records it in the `RUN_STATS` key-value record.

> **About future failures:** this scraper reads Ashby's public job-board API. Ashby can change it without
> notice. If a company that used to work starts failing, or `RUN_STATS` shows `skippedRecords`, suspect a
> format change first, not a regression in this code. The run log names the company and says what didn't parse.
> One broken company never affects the others in a run.

### 1.0.1 (2026-09-24)

- Shared code update: a pasted URL on a platform whose terms don't allow automated reading (Workday, Workable,
  Breezy, Rippling, SmartRecruiters) is refused with that reason instead of a pointer to another actor. No change to
  results for this actor's own platform.

### 1.0.0 (2026-09-24)

First release. Ashby-only edition of Company Career Page Jobs Scraper (`ats-jobs`), sharing its code
(`common/mms_ats`) and output format.

- Shared safety code (`mms_common`, the same in every actor of this portfolio): robots.txt per RFC 9309 (byte-order
  marks, product-token matching, percent-encoding, a 500 KiB cap counted while streaming), a Crawl-delay above 30 s
  reports that site instead of stalling the run, `Retry-After` honoured (seconds or a date; above 30 s reported),
  the private-network guard on every request and redirect hop (resolve-and-check, then connect to the checked
  address; ports 80 and 443 only; IPv6 forms that wrap an address, 6to4 and site-local addresses refused), a time
  limit per attempt and one for the whole request (redirects, retries and robots.txt included), and decompression in
  small steps with a capped size. robots.txt rules are matched in linear time, so a hostile file can't stall a run;
  a robots.txt cut at the size cap loses its partial last line, and its retries honour `Retry-After`. A malformed
  address or redirect target is reported for that company instead of crashing the run.
- Company names: a guess that fails for robots.txt or network reasons (e.g. an unknown Personio name redirecting to
  Personio's site, whose robots.txt answered 429) just means "not on that platform", with no retries and no
  robots.txt message; an unknown name is answered in seconds.
- A careers domain that doesn't exist is reported as such, not as a robots.txt problem.
- Jobs reserved against the run's limit for a company whose job details robots.txt then disallows are given back to
  the other companies in the run.
- Companies as a name (looked up on Ashby only, and used only when the board's own company name agrees), a
  Ashby board URL or `ashby:slug`, or a careers page that links to an Ashby board. Boards on other platforms are
  refused with a pointer to `ats-jobs`; the rest of the run continues.
- Filters: title keywords, exclude keywords, locations, remote only, posted within N days, max jobs per company;
  "only jobs new since my last run", remembered per company and filter combination in the user's own
  `ashby-jobs-seen` store.
- Charged per job returned ($2.00 per 1,000, plus $0.00005 per run start), through Apify's standard
  `apify-default-dataset-item` event.
- "Max jobs per run" input, and the maximum cost per run is honoured: once either is covered, companies still
  being fetched are cancelled and the rest are skipped (listed as `boardsSkipped` in `RUN_STATS`).
- Failure isolation: a failing company, a malformed job record or a failed detail request only affects itself.
- A run where no company is on Ashby (or none can be found) ends successfully with 0 jobs and a status
  message saying so; `RUN_STATS` (`unresolved`) and the log say why for each company. A malformed company line
  (e.g. `http://[bad`) is reported for that line and never crashes the run.
- "Only jobs new since my last run" remembers a job only once it's in your dataset, so a run that stops early never
  hides jobs from the next one.
- `RUN_STATS` has the same keys on every exit path. No proxy input: Ashby's public API needs none.
- Identifies itself honestly (User-Agent `HumbleEchidnaApify/1.0 (+https://apify.com/humble-echidna)`) and
  checks every host's robots.txt first, careers pages included: anything a site disallows is never requested and the
  company is reported with the reason; Crawl-delay is respected.
