LinkedIn Job Scraper
Two builds answer to the name, and the one you pick decides whether a bad afternoon costs you an IP pool or a LinkedIn account.
A LinkedIn job scraper collects job listings as structured data, and it comes in two shapes. One reads the public job pages with no login, spends IP addresses, and runs out of results after roughly a thousand per search. The other drives a signed-in session with your own cookie, reaches further, and puts your account in the blast radius. LinkedIn publishes no API that reads listings at any tier, so every option on this page is one of those two.
On this page
What a job scraper actually reads
There is no official read route. LinkedIn’s one jobs API writes: the Job Posting API lets an approved partner publish, update and close roles for a customer, and it stopped accepting new partnerships. Nothing at any tier returns a search of listings, which is the whole reason this category exists. The full map of what LinkedIn does and does not offer is on the LinkedIn jobs API.
So a job scraper reads the same pages a person reads. The job search results and the individual job pages, rendered by LinkedIn for a browser, parsed back into rows. Everything that follows is a consequence of that: the fields are whatever the page shows, the pagination is whatever the site allows, and the blocking is whatever LinkedIn applies to a client that behaves unlike a person.
The two shapes, and what each risks
Open-source tooling in this space splits cleanly down the middle, and the two most-used libraries are a good illustration of each side because both document their own constraints.
The anonymous shape reads public job pages with no account attached. JobSpy, a multi-board scraper with 4.3k stars on GitHub, works this way and is blunt about what it costs: “LinkedIn is the most restrictive and usually rate limits around the 10th page with one ip.” Its README’s conclusion is short, that proxies are essentially mandatory for LinkedIn. A 429 in this world means the address is burnt for a while, and the fix is another address.
The session-backed shape drives a real logged-in session. py-linkedin-jobs-scraper states the requirement in three words, “The scraper needs a LinkedIn session”, and documents three ways to supply one: a signed-in Chrome profile, a pair of cookies, or the li_at session cookie pulled out of developer tools. It also names the maintenance tax that comes with the cookie route: “This cookie cannot be renewed: LinkedIn expires it after a while.”
The difference that matters is not throughput. It is what is exposed when the platform pushes back. An anonymous run that gets blocked costs you addresses and time. A session-backed run that trips detection is happening inside an account with a name on it, and account-level consequences are the subject of LinkedIn account restricted.
What comes back
The row a job scraper produces is roughly the visible job page. JobSpy’s schema for LinkedIn covers title, company, company url, job url, location, remote status, description, job type, salary, date posted, job level and company industry. The session-backed library returns a similar set with a few extras that only a signed-in view carries, including applicant count, whether the post is Easy Apply, whether it has been reposted, listed benefits and the company’s employee count.
Two practical notes on that list. Descriptions are not free: fetching the full text and the direct apply link is a second request per job, which JobSpy flags as increasing requests by O(n), and it is the single biggest driver of how fast a run gets blocked. And salary is present only when the poster filled it in, so treat it as sparse.
What no job scraper returns is a person. A listing names a company and a role, and the hiring manager behind it is a separate lookup through a different surface entirely.
What stops it
Four ceilings show up in every build, in roughly this order.
- The result cap. JobSpy documents its endpoints as “capped at around 1000 jobs on a given search”, which is a property of the search surfaces themselves. Wide queries have to be split by geography, seniority or time window to get past it.
- The page-depth limit. Rate limiting arrives around the tenth page from a single address on the anonymous route. That is shallow enough that a serious harvest is an infrastructure problem before it is a parsing problem.
- The 429. Both libraries treat it as the signal to slow down. The session-backed one implements a specific backoff worth copying: “On every 429, the delay between jobs doubles, up to min(10, slow_mo * 10) seconds”, on a default gap of 0.8 seconds between jobs.
- Cookie expiry. On the session route the
li_atcookie dies on LinkedIn’s schedule and cannot be renewed programmatically, so a scheduled pipeline has a recurring manual step in it by design.
None of those is a bug to be engineered away. They are the platform’s side of the arrangement, and a build that treats them as targets to beat is the build that draws attention.
The hosted routes
Not every team wants to own a proxy pool and a parser that breaks when a class name changes. Three hosted shapes exist, and they trade cost for upkeep in different ways.
- Actor marketplaces. Apify hosts a large set of LinkedIn actors, several of them job-specific, priced per result or per compute unit and maintained by independent authors. Which actor does what, what each costs and which ones want your cookies is on the Apify LinkedIn scraper page.
- Credit-priced data APIs. A read API returns job search, job detail and similar-jobs endpoints for a credit a call, with no account connected at any point. The vendors selling that shape are under the scraper and dataset routes of our API ranking, and its trade-offs in more general terms are on LinkedIn profile data API.
- Account-based APIs. A connected account performs the job search itself, server-side, with limits enforced before each call. The data is the signed-in view, and the pacing is somebody else’s problem to get right.
The upkeep question is the one that decides this for most teams. A self-hosted scraper is cheap until the day the markup changes, and then it is a person’s afternoon, repeatedly. A hosted route moves that cost into a line item and moves the failure into somebody else’s incident channel.
When the listing was only a proxy
A large share of job-scraping projects do not want jobs. They want to know which companies are growing, so that a sales team can reach them while a budget exists. The listing stands in for that and drops most of it on the way: a post tells you a role is open, and says nothing about who owns the budget or whether anyone has replied to the recruiter.
If hiring is the signal you are after, an account-based search with the right facets answers it without touching a listing, and it returns people rather than posts. The hiring facets are walked through on the LinkedIn jobs API, the full facet vocabulary and its five gaps on the Sales Navigator API page, and what an account-based search actually returns per row on the LinkedIn search API.
This is worth checking before any scraper gets written, because the two routes have different costs and different failure modes, and a project that harvests fifty thousand listings to derive a company list has taken the long way round.
The legal line, briefly
Job listings are public pages, which puts them in the most defensible corner of this topic, and it does not make the question go away. Three separate legal questions apply to LinkedIn data, and they have three different answers: the computer-misuse question, the contract question you agreed to when you made an account, and the data-protection question that applies once a record identifies a person. We work through all three, with the case law and the fines, on is scraping LinkedIn legal.
The short version for this page: a listing is company data and sits well clear of the data-protection problem, while the contract question still binds anyone who has ever accepted the User Agreement, logged in or not. The wider comparison of scraping against an account-based route is on LinkedIn scraping vs a safe API.
Where a build needs LinkedIn reads and actions on an account the operator owns, with per-action budgets evaluated before dispatch, that is the account-based route. On that stack, gtm-api.com reports 20,000+ accounts at under a 1% ban rate.
Frequently Asked Questions
Can you scrape LinkedIn jobs without logging in?
Yes, for the public job pages. Open-source tools such as JobSpy read LinkedIn listings with no account attached, and the practical ceiling is addresses: its README states that LinkedIn rate limits around the tenth page from a single IP and that proxies are essentially mandatory. Anonymous access also caps out at roughly a thousand results on a given search, so wide queries have to be split by geography, seniority or time window.
Is there a LinkedIn jobs API for reading listings?
No. LinkedIn’s only official jobs product is the Job Posting API, which publishes roles and never returns a search, and which is limited to approved partners that are no longer being added. Nothing at any tier returns a search of job listings. That absence is why the choice is between scraping the public pages, buying from a dataset provider, and running the search through an account you own.
What does a LinkedIn job scraper return?
Roughly what the job page shows: title, company, company URL, job URL, location, remote status, description, job type, salary where the poster filled it in, date posted, job level and company industry. A signed-in scraper adds fields that only appear to a logged-in viewer, such as applicant count, Easy Apply status, repost status, benefits and company headcount. Fetching full descriptions costs an extra request per job, which is what usually triggers blocking.
Will scraping jobs get my LinkedIn account banned?
It depends which shape you run. An anonymous scraper has no account attached, so the exposure is IP addresses and time. A session-backed scraper drives your own logged-in session with the li_at cookie, which puts the activity inside an account with your name on it and makes an account restriction one of the possible outcomes. Since public job listings are reachable without a login, spending an account on them is a poor trade.
How do I scrape LinkedIn jobs with Python?
Two library shapes exist. JobSpy scrapes several boards including LinkedIn with no login, and expects a proxy pool. py-linkedin-jobs-scraper drives a headless browser against a signed-in session supplied as a Chrome profile or a li_at cookie, and ships adaptive throttling that doubles its delay on every 429 up to a ceiling. Whichever you pick, budget for proxies, expect a result cap near a thousand per search, and plan a manual step for cookie expiry on the session route.
Sources & Further Reading
- JobSpy, repository README (the tenth-page rate limit on a single IP, the proxy requirement, the roughly 1000-job cap, the 429 guidance, the LinkedIn field schema and the O(n) description fetch) · package listing
- py-linkedin-jobs-scraper, repository README (“The scraper needs a LinkedIn session”, the three session routes, the li_at expiry note, the adaptive backoff that doubles on every 429, and the returned field list)
- LinkedIn via Microsoft Learn, Job Posting API overview (the one official jobs product, a write surface only, and closed to new partnerships)
- gtm-api.com, product · pricing (our own numbers)
- Related: LinkedIn jobs API · LinkedIn scraping vs a safe API · Is scraping LinkedIn legal · Apify LinkedIn scraper · LinkedIn search API
Reads and actions on an account you own.
Connect a LinkedIn account server-side, run searches and send invitations as typed calls, and let the server hold the per-action budget under every one. On that stack, gtm-api.com reports 20,000+ accounts at under a 1% ban rate. Free plan, then from $10 per connected account at volume.
Last updated: September 2026 · The library quotations, limits and field lists on this page were read off the projects’ own repositories and package listings on 11 September 2026 and are linked in Sources. Open-source scrapers change behaviour with the pages they parse, so check the current README before you build against one.
