# Changelog of Hiring.Cafe Scraper 💰 $1.15/1K — 2.8M AI Jobs, 46 ATS (`blackfalcondata/hiringcafe-scraper`) Actor

- **URL**: https://apify.com/blackfalcondata/hiringcafe-scraper/changelog.md
- **Full Actor documentation**: https://apify.com/blackfalcondata/hiringcafe-scraper.md

## Changelog

### 0.1.x — 2026-09-20

- **Changed: you are no longer charged for results without the description you asked for.** With job details enabled, a run that reaches its time limit before enriching every job used to deliver the rest with core fields only — and charge for them at the same price. Those jobs are now left undelivered and unbilled, and the run says how many were held back. In incremental mode they are not recorded as seen either, so the next run picks them up. If you would rather receive them anyway, the new **Also Deliver Jobs Without a Description** option returns them with their core fields, charged as normal results.
- Improved: the run now warns as soon as its own measured pace shows it will not enrich everything requested — early enough to stop and re-run smaller — instead of only checking a theoretical best case at the start. The closing summary reports how many results carry a full description and how many do not.
- Fixed: runs with job details enabled delivered results in large batches, so the result counter could sit unchanged for a long time while the run was in fact still working — and aborting during that window threw away everything collected since the last batch. Results are now delivered in much smaller batches, so progress is visible throughout and an aborted run keeps almost all of the work it finished.
- Improved: when the number of results requested is more than a run can enrich with full descriptions before its timeout, the log now says so at the start, and says what to change, instead of leaving it to be discovered from a slow-moving counter.

### 0.1.x — 2026-09-16

- Improved: fetching a job's full description now usually takes one request instead of two. Runs with details enabled are faster and get further within the same run timeout, which matters most on days when the source answers slowly.

### 0.1.x — 2026-09-15

- Fixed: incremental runs with job details delivered their results only at the very end. When such a run was interrupted or restarted by the platform partway through, everything it had collected was lost and the work started over. Results are now delivered in batches as soon as they are ready, and a run that restarts continues after the results already delivered instead of delivering and charging them again.
- Fixed: runs given a timeout longer than about 6 hours were stopped early at that point. A run now uses the full timeout it was given.
- Improved: a run that is aborted now reports that status instead of ending without one.
- Improved: when a run stops early (timeout, abort or an error after results were delivered), the log now says what to do next — for incremental runs, start it again with the same settings to continue. A run started while a stopped run still holds the same state key now says roughly how many minutes to wait, instead of only that another run is busy.

### 0.1.x — 2026-09-13

- Improved: a run that stops because of an unexpected internal error now reports that it failed, instead of ending without any status at all. The outcome is unchanged — such a run still fails — but it is no longer silent.
- Fixed: runs could fail at startup with an internal error when the source's network edge refused the connection outright. The actor now also asks a second, independently protected address for the same information, so a block on one side no longer ends the run.
- Fixed: long runs with detail enrichment could spend their whole time budget waiting for the source to accept more requests and then end as failed, delivering nothing. The run now stops waiting once it needs the remaining time to deliver, and returns every job it found with its core fields.
- Improved: a run that is waiting on the source now reports progress while it waits, instead of appearing to have stopped responding.

### 0.1.x — 2026-09-04 (audit)

- Fixed: `location` was ignored for United States searches — `{country:"US", location:"New York"}`
  returned nationwide results. It now filters the result window by location text for every
  country, and the search over-fetches to fill the window.
- Fixed: Start URLs on the `hiringcafe.com` host (the site's current domain) were not recognised
  and silently became a nationwide keyword search that billed unrelated rows. Both hosts are
  accepted, and a run whose Start URLs are all unusable now ends without searching.
- Fixed: rows from a structured Start URL were filtered by the separate Country input (default
  US), which threw away every non-US result of a UK or DE search URL.
- Fixed: in multi-country mode the default global `maxResults` (25) truncated a three-country ×
  25 run to the first country's rows. When `maxResults` is not set explicitly, the window is now
  sized by `maxResultsPerCountry` × countries.
- Fixed: a centre + radius (`lat`, `lon`, `radiusKm`) with no other input was treated as an empty
  request and returned nothing.
- Fixed: listings that the job site reports as closed at fetch time are no longer delivered (or
  billed) as open jobs.
- Fixed: with `emitUnchanged`, unchanged rows shipped without descriptions; they now carry the
  enriched record from the run that fetched them.
- Fixed: when a run's charge budget (`maxTotalChargeUsd`) cuts a batch short, the rows the
  platform refused are no longer counted, notified, exported, or remembered as seen — the next
  run picks them up.
- Fixed: two runs sharing a state key that start in the same moment could both deliver (and
  bill) the same rows. Ownership is now decided by an election and re-checked before the first
  write.
- Fixed: a momentarily refused search page (the site's occasional 403 flap) is retried the
  same way a job detail is, instead of ending the search early; a rate-limited search page is
  retried after the quiet window instead of being abandoned; a partial search is now called out in the log instead of passing silently.
- Fixed: after a rate-limit pause the first request of every retry was failing instantly
  (its network timeout had been armed before the pause); the timeout is now armed per attempt.
- Fixed: a changed search-page shape is reported as a failed page instead of an empty market.
- Changed: `location` input description now says what it does.
- Fixed: `skipReposts` never detected a repost (the comparison was always given an empty
  hash). Reposts are now detected and `isRepost` / `repostOfId` are filled.
- Fixed: a site deploy in the middle of a run retired the search's build id and every later page
  (and every job detail) failed; the run now notices and re-resolves it.
- Fixed: a job whose detail page is gone is no longer delivered as an open listing, and a
  listing that is only temporarily unreadable is retried rather than treated as gone.
- Fixed: in a filtered multi-country run the first country's rows could fill the whole window;
  `maxResultsPerCountry` is now enforced on delivered rows per country.
- Fixed: a pasted Hiring.Cafe search URL kept only some of its filters; every filter in the URL
  now travels with the request, and the actor's own filter inputs narrow it further.
- Fixed: `maxResults: 0` (unlimited) searched only one page's worth of title variations; it now
  expands the search fully.
- Fixed: a Start URL naming a city or region (`/jobs/<title>/locations/new-york`) searched the
  whole country; the location now filters that URL's rows.
- Fixed: in incremental mode a closed listing or a skipped repost was forgotten and re-fetched
  (and, after 30 days, re-delivered as new) on every run; it is now remembered as seen.
- Fixed: an update that was cut from a run by the result window or the charge budget could
  later be reported as expired with content the buyer never received.
- Fixed: a run's `maxResults` window is measured in rows delivered — a closed listing inside
  the window is backfilled by the next live one instead of shrinking the result.
- Added: `sortBy` (newest, oldest, highest/lowest salary, most/least experience, relevance),
  `companyNames`, `excludedCompanyNames`, `companyKeywords`, `industries`, `excludedIndustries`,
  `jobDescriptionQuery`, `technologyKeywordsQuery`, `requirementsKeywordsQuery` (all applied inside
  Hiring.Cafe's own search) and `minYearsExperience` / `maxYearsExperience`. Together with workplace type,
  commitment, seniority and salary these are now applied inside Hiring.Cafe's own search, so a
  filtered run fills its result window instead of fetching many rows it then discards.

### 0.1.x — 2026-09-04 (descriptions)

- Fixed: on larger runs only a small share of jobs came back with a job description. The job
  site limits how many requests one run may make in a short window, and the run kept asking
  faster than that — so after roughly the first fifty jobs, every further description request
  was refused and those records shipped with `description`, `descriptionHtml` and
  `descriptionMarkdown` empty. Description requests are now paced to stay inside the site's
  limit, and if the limit is hit anyway the run waits quietly for it to reset instead of
  making it worse. The pace also adapts: it eases off when the site pushes back and speeds
  up again once it stops. Jobs whose description request did not get through are retried
  once more at the end of each batch, so a busy moment no longer costs that job its
  description. Measured on a 300-job run: 11 descriptions before, 186 of the first 204
  after the first pacing fix, and the retry pass closes the rest.
- Changed: because of that pacing, runs that ask for descriptions take longer than before.
  If a very large run would run past its own timeout, the remaining jobs are delivered with
  their core fields rather than the run being cut short — every job found is still returned.

### 0.1.x — 2026-09-03

- Fixed: job descriptions came back empty on every record from 2026-09-02. The job site
  tightened its bot protection on the pages the scraper reads to work out which network
  route to use, and that made every route look blocked — so the run fell back to a mode
  that returns only the core job fields. Route selection now also checks that a real job
  description comes back before settling on a route, and the description request itself
  sends the same headers the search request does. `description`, `descriptionHtml` and
  `descriptionMarkdown` are populated again, and `descriptionFormat` still selects between
  them.
- Improved: a momentary refusal on a single job description is retried instead of losing
  that job's description for the run.

### 0.1.x — 2026-08-27

- Fixed: a single momentary search failure no longer discards the whole run. Only one specific
  kind of hiccup was retried; anything else stopped the search at once, and on a run with a
  single search target that meant ending with no results even though the job site was healthy.
  Every search page now gets one retry before the run gives up on it.

### 0.1.x — 2026-05-10

- Added: multi-country input with `countries` and `maxResultsPerCountry` for balanced runs across multiple verified markets
- Expanded: country dropdown to 50 verified markets and added runtime filtering for broad multi-country region matches
- Added: table-friendly derived fields `locationNormalized`, `experienceLevelBucket`, `clearanceRequired`, `companySizeBucket`, `applicationType`, and `freshnessBucket`
- Added: `salaryRange`, `commitmentTypes`, `technicalToolsNormalized`, `classifiedUrls`, and cleaner `socialProfiles` extraction
- Improved: compact/table outputs, multi-URL input support, salary parsing by compensation frequency, and email/phone extraction from descriptions

### 0.1.x — 2026-04-18

- Fix: restored compatibility with upstream SERP response shape
- Improved: richer structured data returned directly in search results

### 0.1.x — 2026-04-14

- Added: `descriptionHtml`, `descriptionMarkdown` output fields (triple-format descriptions for RAG/LLM pipelines)
- Added: `contentHash` output field (SHA-256 hash for change detection)

### 0.1.x — 2026-04-14

- Added: cross-run repost detection (`isRepost`, `repostOfId`, `repostDetectedAt`)
- Added: `skipReposts` input to exclude detected reposts from output

### 0.1.0 (2026-04-05)

- Initial release
- 85 structured fields per listing including AI-enriched data
- Salary, benefits, requirements, company enrichment from 46+ ATS sources
- Multi-query SERP expansion for large result sets (up to 5000)
- Incremental mode with change detection
- Compact output mode for AI/MCP workflows
