If you scrape LinkedIn jobs without logging in, you probably use the guest search endpoint (/jobs-guest/jobs/api/seeMoreJobPostings/search). It takes the same query parameters the website does: f_JT for job type, f_E for experience level, f_WT for remote, sortBy=DD for newest first.
Since around 11 September 2026 it accepts all of them — and returns exactly the same result set no matter what you send.
The measurement
Query: keywords=data analyst, location=United States. Same request, five variants, first 50 job IDs compared with the unfiltered search (re-run 13 Sep 2026):
| variant | overlap with unfiltered |
|---|---|
f_JT=C (contract) |
50 / 50 |
f_E=2 (entry level) |
50 / 50 |
f_WT=2 (remote) |
50 / 50 |
sortBy=DD |
50 / 50 |
f_TPR=r86400 (past 24h) |
4 / 50 ← still works |
Only f_TPR (posted within), f_AL (Easy Apply) and f_EA change the response. No error, no warning: the server just serves the default page. A scraper that trusts the parameters keeps "succeeding" with wrong data, which is the worst kind of failure.
Why "filter after the fact" isn't enough
The obvious fix is to open each job's detail page (which still has the criteria block: seniority, employment type) and drop what doesn't match. I shipped that first. Then a routine parameter audit found the next bug: with maxJobs: 20 and jobTypes: ["contract"] the run returned 0 rows. Not because there are no contract data-analyst jobs, but because only 20 cards were scanned before filtering, and 0 of those 20 were contracts. In my sample it took 49 detail pages to find the first one.
So the loop is now: page → detail-check → keep matches → keep paging until maxJobs matches or a scan cap is hit (default 5× maxJobs, min 50, max 500). Non-matching jobs cost nothing. A "remote data analyst, 8 jobs" run scans ~30 postings; "contract" scans a lot more and tells you so.
Takeaways for any scraper
- Parameters that are accepted are not parameters that are honoured. Test the effect (compare IDs), not the status code.
- If a filter has to be enforced client-side, the scan budget has to be decoupled from the result budget, or rare filters return nothing.
- Put the measurement in a gate you run every day. Mine is a JSON spec: input variant → expected effect (
all rows have employmentType ~ /contract/,≥8 rows,first 5 ids differ from base). It caught the 0-row bug the day after I introduced it.
The scraper (salary parsing, applicant counts, monitor mode, company hiring signals, and now honest filters) is on the Apify Store: https://apify.com/tactful_anvil/linkedin-jobs-full-details-scraper — if it saves you an afternoon, a bookmark or a review there helps more than you'd think.
Top comments (0)