By the numbers.
What each signal means.
Two signals, both computed from raw columns on every active posting. No model, no language analysis, no judgement call. If you want to describe this method in your own words, everything you need is here and in the methodology.
Signal 1. Live more than 60 days.
The posting’s days_live exceeds 60, where days live is the whole days between posted_at and the snapshot instant, rounded down. 26,249 postings, 17.6%, meet it. At the 120 day mark it is 12,179, 8.2%.
Slow hiring is often real. Senior, specialized and regulated roles genuinely take months. A posting open for four months with no urgency in the language is more often building a pipeline than filling a seat, but the age alone does not tell you which, and this report does not pretend otherwise.
Signal 2. The same role, listed 5 or more times.
The posting’s repeat_count is 5 or higher, where a role is one company plus one normalized title. Normalizing means collapsing every run of whitespace to a single space, trimming, and lowercasing, so two postings differing only by spacing count as the same role. 1,360 roles clear the bar and 20,467 postings belong to one.
Repetition can be a real recurring backfill, a genuinely multi-city opening, or one always-open listing collecting applicants indefinitely. The count alone cannot separate those three.
The headline is the union, not the sum.
A posting counts once toward 29.9% whether it trips one signal or both. 24,015 postings trip the age signal only, 18,233 trip the repeat signal only, and 2,234 trip both. Adding 17.6% to the repeat share would double-count those 2,234.
How long postings stay up.
Most live postings are recent. The median has been up 17 days. The distribution has a long tail, and that tail is the whole story. 5% of dated postings fall in the over 180 days bucket.
Basis: 148,529 active postings with a believable posted_at. Percentages are of that basis, not of all active postings.
How often the same role repeats.
Most roles appear once. The repeats concentrate hard, and the heaviest bucket is larger than the one below it, which is what an always-open listing looks like in aggregate.
Basis: all 149,008 active postings.
What we can’t claim.
No posting here is called fake. A structural signal is a prompt to verify, and the two signals we publish cannot tell an always-open pipeline listing apart from a genuinely slow search. Anyone reporting this as a count of fake jobs is reporting something we did not measure.
29.9% is a floor, not a ceiling. Our ghost-job checker also reads the language of an individual ad, which is the part that catches vague filler, evergreen wording and outright scam patterns. That analysis runs one listing at a time and has never been run across the index, so no index-wide share of it exists and we are not implying one. Apply-link liveness works the same way, per lookup rather than corpus-wide. The true share worth a second look is higher than 29.9% by an amount nobody has measured.
This is one snapshot of a moving index, not a trend. The count of active postings moves by tens of thousands between measurements in both directions, and some of that movement is our own coverage changing rather than the market. Two snapshots from this series are not a time series, and we do not publish growth rates off them.
The index is not the whole labor market. It covers postings pulled directly from company career pages across six applicant-tracking platforms, which skews toward employers large enough to run one. Aggregator and job-board reposts, where dead listings survive longest, are out of scope here.
One data caveat worth stating plainly. 479 active postings arrive from one feed carrying a misparsed integer where a date should be, which stamps them January 1970. Counting those as decades stale would have inflated the age signal, so they are treated as unknown-age instead. They still count toward the denominator and can never satisfy an age test. Postings genuinely dated 2009 onward are kept and do count, because they span dozens of companies and several feeds and so are what employers actually published.
How this was built.
Four-Leaf scrapes job postings from public ATS feeds for a large index of employers. This report reads every row flagged active at a single instant, 2026-09-19T18:26:39.327Z, so the published figures and the published CSV describe the same moment rather than a scan smeared across several minutes.
For each posting we compute days live, count how many active postings share its company and normalized title, and set the two signal booleans from those two numbers. Nothing else feeds the result. The cached legitimacy_tier column in our database is deliberately unused, because it is refreshed in capped daily batches and so is a mix of scores stamped weeks apart rather than a snapshot.
The CSV is the verification artifact and it is complete, one row per active posting rather than a sample. Filter it on days_live > 60 and you get 26,249. Group by company plus title_norm, keep the groups of 5 or more, and you get 1,360 roles covering 20,467 postings. Group on the raw title instead and you will land low, because thousands of titles here differ from a twin only by whitespace. That is why the normalized value ships as its own column. Every headline figure on this page is recomputable from that file without asking us for anything.
The stats JSON carries the definitions and the SQL that returns the same figures against the live index. One thing that SQL cannot do is rebuild this snapshot. is_active is overwritten in place and the table keeps no last-seen timestamp, so the set of postings active on a past date is gone once it passes. Run the query and you get a fresh snapshot, close to this one but not identical to it. The CSV is the durable record.
What is in the CSV.
| Column | Meaning |
|---|---|
company | Employer name as the ATS feed reports it. |
title | Job title as published, unmodified. |
title_norm | The title with whitespace runs collapsed, trimmed and lowercased. This is the column the repeat counts group on. |
posted_at | Timestamp the feed reports, exactly as reported. Empty only when the feed sent none. |
posted_at_implausible | True when posted_at fell below the epoch floor of 2000-01-01 and was discounted. The timestamp is still published so you can redo the call differently. |
days_live | Whole days between posted_at and the snapshot, rounded down. Empty when the age is unknown, meaning posted_at was missing or implausible. |
is_active | Always true. The file contains only postings live at the snapshot instant. |
repeat_count | How many active postings share this company and normalized title. |
live_over_60d | True when days_live exceeds 60. |
live_over_120d | True when days_live exceeds 120. |
role_repeated_5plus | True when repeat_count is 5 or more. |
any_structural_signal | True when either signal above is true. This column is the headline. |
149,008 data rows plus a header. No posting ids and no apply URLs, so the file cannot be used to replay our crawl.
Citation
The dataset is licensed under CC BY 4.0, so you can republish the rows, chart them, and build on them, as long as you credit Four-Leaf. Suggested citation:
Four-Leaf. “Ghost Job Signals, 2026 Q3.” Snapshot 2026-09-19. https://four-leaf.ai/research/ghost-job-signals-2026-q3
Questions about the method, or want a cut of the data we have not published? [email protected].