One clean feed of job postings from public Greenhouse, Lever and Ashby boards. Salary when posted, remote flag, delta mode for new jobs only, 21-studio games preset. No personal data.
All notable changes to the Multi-ATS Jobs Feed Actor. Format: Keep a Changelog ;
versions follow Semantic Versioning . The Apify build version (.actor/actor.json)
tracks MAJOR.MINOR.
[Unreleased]
Added
README section on the persistent multi-ats-jobs-feed-state store: posting ids only, kept in the Apify account, and how to delete or reset it.
Startup logs Source commit: <sha> from BUILD_INFO.json when the run is a packaged upload build. A source checkout logs that it is not packaged.
Changed
The Docker image installs production dependencies with npm ci from the monorepo lockfile. The Dockerfile copies every workspace package.json so that install matches the lockfile.
Security
Careers-page fetches (and every other request through the shared HTTP client) reject non-http(s) URLs, embedded credentials, and destinations that resolve to loopback, private, link-local, shared, benchmarking, multicast or reserved addresses, including after a redirect. IPv6 ::1, fc00::/7, fe80::/10, IPv4-mapped and IPv4-translated forms, NAT64 and 6to4 are included. Decimal, octal and hexadecimal IP literals are rejected. The client connects only to an address it checked, and tries the next checked address when one is unreachable. A missing socket address fails closed. Response bodies are capped at 32 MB. The request timeout covers DNS. Invalid JSON errors do not quote the body.
When every board fails with "Blocked non-public address", the run error says so and names fake-IP VPNs and proxies (198.18.0.0/15).
A slug-only board uses the preset display name when that board is in a preset (Jam City, thatgamecompany). Otherwise it uses the name the API returns, then a readable form of the slug.
A URL that contains an email in a description snippet is replaced with one [link removed] placeholder.
A public IPv6 address is not blocked only because a hextet is ffff. Mapped and translated forms are still judged by the embedded IPv4 address. 2606:4700:ffff::1 and 2001:4860:4860::ffff are allowed.
[0.2.2] - 2026-10-27
Added
Actor output schema (.actor/output_schema.json), referenced from actor.json as output, so Apify Console can publish the Actor. It links the default dataset (overview table) and the OUTPUT run summary.
A test that every field on a normalized fixture posting is listed in .actor/dataset_schema.json, and that the schema does not declare a field the normalizers do not push. Nullable fields stay nullable.
[0.2.1] - 2026-10-27
Fixed
Removed the input-schema sources default. Apify merges schema defaults into every run, so {"boards":[{"ats":"lever","slug":"jamcity"}]} also fetched Riot Games, Kabam and thatgamecompany. The console prefill is unchanged. An input of {} still uses those three boards in code. Sending only boards or only a preset does not add them.
lever.co hosts wait at least 1 second between requests. api.lever.co, api.eu.lever.co and jobs.eu.lever.co publish Crawl-delay: 1. minRequestIntervalMs can only raise that floor. Greenhouse and Ashby stay on the configured gap (default 250 ms); their robots.txt files set no crawl-delay.
Nordeus applyUrl and postingUrl are https://job-boards.greenhouse.io/embed/job_app?for=nordeus&token=ID. The employer URL http://www.nordeus.com/careers?gh_jid=… redirects to https://www.nordeus.com, whose certificate does not cover www. job-boards.greenhouse.io/nordeus/jobs/ID redirects to that same host, so it is not used. Any Greenhouse absolute_url that is not https, or whose host is nordeus.com, uses the embed. https employer URLs are unchanged.
npm run verify-presets waits 30 seconds for a board page or apply URL and retries that request once. The Jam City board page exceeded the old 12 second limit.
Description snippets also remove word-form emails (jane at studio dot com), 7-digit phone numbers (555-1234), and Firstname Lastname pairs near recruiter, hiring, manager, team-lead, a sign-off, or "reach out to" / "talk to" / "ask". Snippets stay off by default. The scrub is best-effort.
Changed
Pricing text no longer says platform or compute cost is charged on top of the per-result price. Under pay-per-event, Apify charges only the event price.
Docs no longer say 4 requests per second. They describe the real per-host gaps above.
The game-studios verification note matches what verify-presets checks. A 401/403 on an employer https site (Epic Games can do this) is a warning, not a claim that every apply URL returned 200.
[0.2.0] - 2026-10-27
Fixed
Ashby hybrid and onsite jobs are no longer remote: true just because isRemote is true (53 game-studio rows, and 139 on the ramp board). When workplaceType is known, remote is true only for remote. isRemote is used only when workplaceType is missing. Greenhouse already infers workplace from text and sets remote from that. Lever already follows workplaceType and does not let location text override hybrid or onsite.
An unparseable postedSince fails the run with the parse message (for example Could not parse postedSince "last tuesday") instead of Unexpected error.
A sources value that is one string is treated as one source. It is no longer iterated character by character.
Careers-page scans use an 8 second timeout and one retry. API requests keep their own timeout and retry settings, so a host that resets the connection (careers.roblox.com does this for non-browser clients) cannot stall the run for about 150 seconds.
Changed
Removed Supercell from the game-studios preset and from the sources prefill. jobs.ashbyhq.com/supercell and its apply URLs return 404; the company's postings are on supercell.com. The sample source is ashby:thatgamecompany. Also removed Theorycraft Games: jobs.lever.co/theorycraftgames and its apply URL return 404. The preset is 21 boards.
sources now has a default (Riot Games, Kabam, thatgamecompany), so an input of {} fetches those boards instead of failing with "No valid job boards".
npm run verify-presets also requests each board's ATS-hosted page and a sample apply URL. A 404 fails the board. A 401/403 or dropped connection on the employer's own domain is a warning.
README: Ashby applyUrl is the ATS-hosted posting, not the employer's site. Location filters are plain text (22 Montreal/Vancouver rows do not contain "Canada"; 88 remote rows do not contain "Remote"). Platform cost is about $0.005 per 1,000 rows, mostly dataset writes.
Pay-per-event note no longer mentions npm run start:ppe. Local charging is simulated with apify run and ACTOR_TEST_PAY_PER_EVENT.
Added
README section on deploying from the GitHub monorepo so the Docker build includes shared/.
[0.1.0] - 2026-10-27
Added
Sources: Greenhouse Job Board API, Lever Postings API (global + EU host fallback), Ashby public job posting API.
Input: board URLs, job URLs, careers pages that embed a board, ats:slug, bare slugs (auto-probed), explicit boards JSON.
"Game studios" preset: 23 boards verified live on 2026-10-27 (npm run verify-presets).
Filters: keywords (title/department/team), exclude keywords, location, remote only, posted since (date or span).
Normalized output: company, ATS, title, department, team, employment type, locations, remote flag + workplace type,
salary min/max/currency/period (only when the employer publishes it), ISO posted date, employer apply URL, source job id.
Delta mode (onlyNew) with a named key-value store, so scheduled runs only return and charge for new postings.
Retries with exponential backoff + jitter on 429/5xx/timeouts/network errors, Retry-After, per-host concurrency and
minimum request interval; bad slugs are reported and skipped.
De-duplication of inputs, job ids and identical re-posts (optional).
Run summary in the OUTPUT key-value record; dataset schema with "Overview" and "With salary" views.
Pay-per-event config (result = $0.0015) in .actor/pay_per_event.json (not applied on Apify yet).