Follow-up to the release below, from an adversarial audit of it.
- Fixed: a weekly salary was reported as a daily one. The time-unit match tested
for "day" before "week", and the Polish word for week contains the day stem.
- Fixed: when an offer prices each contract type differently, the structured
salary is no longer taken from whichever contract happened to be listed first —
which contradicted
salaryText and flipped with array order. The aggregate
range now stands unless the offer states a single salary.
- Fixed: pasting several search URLs only scraped the first one. A page is
fetched whole, so the first URL consumed the entire result budget; each search
now gets a share of it.
- Fixed: a search term and a pasted search URL are now both searched. The term
used to be dropped whenever any URL was pasted.
- Fixed: a pasted URL whose filters could not be read is skipped instead of being
treated as an unfiltered search of the whole portal.
- Fixed:
startUrls no longer requires a search term to be set as well.
- Fixed: with
emitExpired, a listing we could not reach because of a network or
server error is no longer reported as expired — only a confirmed removal is.
- Fixed: expired markers now count towards Max Results instead of being emitted
without limit, and carry their own
expiredAt field rather than overwriting
expirationDate, which holds the expiry the source advertised.
- Fixed: pasted offer URLs beyond Max Results are no longer fetched.
- Changed: detail requests no longer retry twice over (the HTTP layer already
retries), so a struggling source is not hit harder than necessary.
- Changed: run logs no longer include internal retrieval details.
- Rejected: URLs that are not http(s), and offer ids too large to represent
exactly, which previously resolved to a different listing.
- Added:
employerType input — return only offers posted directly by the hiring
company, or only offers posted by recruitment agencies. Applied at the source,
so it costs no extra requests and works without includeDetails.
- Added:
isDirectlyFromEmployer output field. Resolved from the offer detail
when details are fetched, and implied for every record of a run narrowed with
employerType; null when neither applies.
- Added:
startUrls input — paste pracuj.pl offer URLs (fetched individually)
or search/listing URLs (paginated like a search). Filters in a pasted URL take
precedence; the input filters still apply to whatever the URL leaves unset.
- Added:
emitExpired now emits rows for listings that have disappeared since a
previous incremental run (jobId, changeType: "EXPIRED", seen-timestamps).
- Fixed: structured salary, region, coordinates, contract types, work modes,
work schedules and position levels were read from an
attributes.employmentTypes
path that does not exist on the detail response, so detail enrichment silently
never filled them. salaryMin/salaryMax/salaryCurrency/salaryPeriod now
come from the offer's actual salary data instead of only a parsed display string.
- Changed: detail fetches now carry a per-request deadline, so one stalled
request can no longer hold a run open until the platform time limit.
- Added:
descriptionHtml, descriptionMarkdown output fields (triple-format descriptions for RAG/LLM pipelines)
- Added:
contentHash output field for stable change detection
- Added: cross-run repost detection (
isRepost, repostOfId, repostDetectedAt)
- Added:
skipReposts input to exclude detected reposts from output
- Search Polish job listings on pracuj.pl by keyword, location, and filters
- Salary extraction (min, max, currency, period)
- Detail enrichment: full description, responsibilities, requirements, benefits, technologies
- Contact info: apply URL and phone when available
- Incremental mode: only new or changed listings on subsequent runs
- Compact output mode for AI-agent and MCP workflows
- Filters: contract type, work mode, position level