Stale Vacancy Lead Finder avatar

Stale Vacancy Lead Finder

Pricing

$20.00 / 1,000 employer rows

Go to Apify Store
Stale Vacancy Lead Finder

Stale Vacancy Lead Finder

Finds employers whose job openings have stayed open for weeks. Type job titles, a place and an age limit in days. Get one row per employer: open matching roles, age of the oldest and newest role, posting links and a stale flag. For recruitment and staffing agencies.

Pricing

$20.00 / 1,000 employer rows

Rating

0.0

(0)

Developer

Lontra Industries

Lontra Industries

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

3 days ago

Last modified

Share

What it does

You type the job titles you recruit for, a country or city, and how many days open counts as stale. The Actor reads public job listings that show a date, keeps the ones that match, and groups them by employer.

You get one row per employer: how many matching roles are open, how old the oldest and the newest one are, every role with its link and location, the career page link, and a stale flag. Rows with the stale flag come first, then the employers with the most open roles, then the oldest roles. A list like this is a reason to call an employer today: they have been trying to fill these roles for weeks.

Where it reads:

  • Greenhouse, Lever and Ashby job boards. You paste the board links. The Actor reads the public job feed that each of these services publishes for a board.
  • Other public career pages you link to, when the page publishes its jobs as schema.org JobPosting data, or when it links to or embeds one of the three kinds of board above.
  • The free Arbeitnow public job feed, which lists jobs from many employers, mostly in Germany and the rest of Europe. This is on unless you turn it off, so the Actor also works when you have no links yet.

How it behaves:

  • It reads the robots.txt of every site first and obeys it. It waits between requests to one site (2 seconds unless you change it) and names itself in every request as StaleVacancyLeadFinder.
  • It never logs in and never works around a block or a CAPTCHA. If a source refuses, the run says which source and goes on with the others.
  • It never collects names or emails of people and it contacts nobody. Finding contacts is not part of this Actor.
  • If a listing shows no date, the age is "unknown". It is never guessed.
  • The same role posted twice is counted once.
  • No language model is used. The work is keyword matching, rules and date arithmetic.

Who it is for

Recruitment and staffing agencies: the business development person, or the owner of a small agency, who needs employers to pitch and today reads job boards by hand to find them. Run it once a week on a schedule, or every morning if you pitch daily.

Input

FieldWhat to typeDefault
Job titles or keywords (keywords)The roles you recruit for, one per line. A listing matches when every word of one entry is in its job title, in any order. "Software Engineer" also finds "Senior Software Engineer".none, you must type at least one
Country or city (location)One place, for example Germany or Berlin. For several places put a semicolon between them: Germany; Berlin; Munich. Empty means any place.empty
Days open that count as stale (staleDays)An employer is flagged when its oldest matching role has been open at least this many days.30
Career page links (careerPageUrls)Optional. Links such as https://boards.greenhouse.io/company, https://jobs.lever.co/company, https://jobs.ashbyhq.com/company, or another public career page.none
Also search a public job feed (usePublicJobSearch)On or off. When on, the Arbeitnow feed is read too.on
Pages of the public job feed to read (maxSearchPages)The feed comes in pages, newest listings first. More pages reach further back.5
Largest number of employers (maxEmployers)The run returns at most this many rows, the most stale first.200
Wait between requests to one site (requestDelaySeconds)Seconds between two requests to the same site.2

A sample input is in sample_input.json.

Output

Each employer is one item in the default dataset. This item is what the Actor produces from the sample pages in tests/fixtures (a made-up employer on a Greenhouse board), with the keyword "Software Engineer", the place "Germany" and a limit of 30 days, checked on 2026-10-11:

{
"employer": "Acme Robotics",
"career_page_url": "https://boards.greenhouse.io/acme",
"matching_roles": 3,
"stale": true,
"stale_roles": 1,
"oldest_role_age_days": 71,
"newest_role_age_days": 10,
"oldest_role_date": "2026-08-01",
"newest_role_date": "2026-10-01",
"roles_with_unknown_age": 0,
"stale_days_limit": 30,
"roles": [
{
"title": "Senior Software Engineer",
"url": "https://boards.greenhouse.io/acme/jobs/101",
"location": "Berlin, Germany",
"date": "2026-08-01",
"date_type": "posted",
"age_days": 71,
"age": "71 days",
"source": "greenhouse"
},
{
"title": "Software Engineer, Backend",
"url": "https://boards.greenhouse.io/acme/jobs/102",
"location": "Berlin, Germany",
"date": "2026-09-25",
"date_type": "posted",
"age_days": 16,
"age": "16 days",
"source": "greenhouse"
},
{
"title": "Software Engineer in Test",
"url": "https://boards.greenhouse.io/acme/jobs/106",
"location": "Munich, Germany",
"date": "2026-10-01",
"date_type": "updated",
"age_days": 10,
"age": "10 days",
"source": "greenhouse"
}
],
"roles_text": "Senior Software Engineer (Berlin, Germany, 71 days); Software Engineer, Backend (Berlin, Germany, 16 days); Software Engineer in Test (Munich, Germany, 10 days)",
"sources": ["greenhouse"],
"keywords_matched": ["Software Engineer"],
"checked_on": "2026-10-11"
}

What the fields mean:

  • stale is true when the oldest matching role is at or above your limit. stale_roles says how many roles are.
  • oldest_role_age_days and newest_role_age_days are empty (null) when no matching role of that employer shows a date. roles_with_unknown_age counts the roles without a date; for those, age_days is null and age says "unknown".
  • date_type is posted when the listing shows its posting date, and updated when it only shows when it was last changed. With updated, the role has been open at least that long.
  • career_page_url is the board or career page the roles were read from. It is empty for an employer that was only found in the public job feed.
  • roles_text is the same list of roles in one line, which is easier to read in a spreadsheet.

A short summary of the run is saved in the key-value store under the key OUTPUT. From the same sample pages (the list of sources is shortened to one here):

{
"employers": 5,
"employers_stale": 4,
"matching_roles": 10,
"roles_with_unknown_age": 2,
"duplicates_skipped": 1,
"listings_read": 17,
"employers_over_max": 0,
"employers_in_dataset": 5,
"employers_held_back": 0,
"spending_limit_reached": false,
"keywords": ["Software Engineer"],
"location": "Germany",
"stale_days_limit": 30,
"checked_on": "2026-10-11",
"sources": [
{
"source": "greenhouse",
"name": "Greenhouse board \"acme\"",
"url": "https://boards-api.greenhouse.io/v1/boards/acme/jobs",
"status": "ok",
"listings_read": 6,
"message": ""
}
],
"sources_not_read": [],
"message": "5 employers with matching roles, 4 flagged stale (a role open 30 days or more)."
}

Each source has a status: ok, partial (it stopped early), no_listings, not_found, refused or failed, with the reason in message.

Pricing

Pay per event. There is one event, employer-result: $0.02 for each employer row saved to the dataset.

You are not charged for listings that were read, for employers that did not match, or for sources that refused. A run that finds no matching employer charges nothing.

Two ways to cap the cost of a run: set "Largest number of employers", or set a maximum charge per run in Apify. When your limit is reached, the Actor saves the most stale rows that fit, stops, and says in the summary how many rows were left out. Rows that were left out are not charged.

Limits

  • It reads only the sources listed above. It does not read LinkedIn, Indeed, Glassdoor or any site that needs a login or blocks automated reading.
  • Without career page links, the only source is the Arbeitnow feed, which is mostly Germany and the rest of Europe, newest listings first, as far back as the number of pages you set.
  • Career pages that only build their job list in the browser, with no job data in the page itself, give no listings. The Actor does not run a browser.
  • The age is the number of days since the date the listing shows. For Greenhouse that is the first publication date, or the last update date when the first is missing. For Lever it is the day the posting was created. A role that was closed and posted again shows the new date.
  • A listing matches a place only when the listing names it. "Germany" does not match a listing that only says "Munich"; add the cities you care about, separated by semicolons. Remote roles match only when they name your place.
  • Keywords are matched against the job title only, as whole words. "Engineer" does not match "Engineering Manager".
  • For Lever and Ashby boards the employer name is the short name in the board link, for example "globex", because their feeds do not carry the company name.
  • Two listings of one employer with the same title in the same place are treated as one role, even if the employer really has two such openings.
  • It does not find contacts, and it does not remember earlier runs, so it cannot tell you what is new since last week.

Run the tests

From this folder, with Python 3.12 and nothing installed:

python -m unittest discover -s tests

The tests use saved sample pages in tests/fixtures and never touch the network.