DEV Community

Zackrag
Zackrag

Posted on

n8n career page hiring signal workflow 2026 that skips stale postings before enrichment spend

I built an n8n workflow that checked 120 public career pages daily and only sent 28 fresh VP and Director roles to enrichment tools in the first 90 days of testing.

Deduping logic that prevented 340 duplicate Apollo calls

I exported my CRM contacts into a Google Sheet with columns for title, company, and first seen date, then pointed an n8n workflow at it every morning. The workflow pulled the RSS or HTML from each target career page, parsed new listings with an HTML Extract node, and ran a Merge node against the sheet using title-plus-company as the key. Any match older than 72 hours got filtered out by an IF node checking the timestamp difference.

In practice this dropped my daily Apollo and Hunter.io lookups from 47 to 14 on average. One week in August I logged 19 duplicate senior roles across three enterprise software companies; the workflow caught every one before any paid call. The same logic rejected 11 postings that had sat on the pages for five days or more.

Exact n8n nodes for scraping and 72-hour freshness check

The core flow used these nodes in order:

  1. Schedule Trigger set to 08:00 UTC.
  2. HTTP Request node with a rotating list of 120 URLs stored in a static JSON array; each request used a 4-second timeout and Accept header mimicking a desktop browser.
  3. HTML Extract configured for h2 or div selectors that contained job titles; a subsequent Set node normalized title, company, and posted date into consistent fields.
  4. Google Sheets node doing a lookup by title+company; if no match it wrote the record with current timestamp.
  5. IF node evaluating {{ Date.now() - new Date($json.postedDate).getTime() }} < 259200000 to keep only items under 72 hours.
  6. Another IF checking title against a regex list for VP|Director|Head of to drop junior or irrelevant roles.

I added a Split In Batches node with batch size 8 between the HTTP Request and extract steps. Total runtime stayed under 11 minutes per run on 120 pages.

Rate limit handling that survived 200 pages

Early tests hit 429 errors on 14 sites within the first week. I inserted a Wait node set to 2.8 seconds between each HTTP Request after the Split In Batches step and added a retry loop using an Error Trigger that waited 45 seconds on failure before re-attempting up to three times.

For sites that blocked after 12 consecutive requests I switched the user-agent pool every 10 calls using a Set node pulling from a 40-item array. This combination kept success rate above 91 percent across 200 pages over a 14-day stretch. One finance company still returned empty results after day three, so I removed it and replaced it with a public ATS feed from another target.

Site type Requests before block Wait interval used Success rate after tuning
Company ATS 18 2.8s 94%
Greenhouse 9 3.5s 88%
Lever 14 2.8s 97%
Custom HTML 22 2.1s 91%

What I actually use

The final workflow runs on a $5 Hetzner VPS with n8n self-hosted and writes fresh signals straight into a dedicated Airtable base that feeds a downstream Clay table only for the 28 or so records that survive the 72-hour filter. I still keep Hunter.io and Apollo active but their monthly spend dropped from $312 to $89 after the change. RocketReach and Lusha stay in reserve for the occasional international title that needs extra verification. Ziwa sits as one lightweight option in the same enrichment step when I need quick domain data without another API credit.

Top comments (0)