LinkedIn Jobs Scraper — No Login, by Keyword & Location
LinkedIn Jobs Scraper is a NexGenData tool that returns job listings with title, company, salary and posting date from public LinkedIn job pages.
LinkedIn jobs scraper for public LinkedIn job listings — no login or cookies: search by keyword, location and job type and get title, company, salary, seniority and posting date as JSON/CSV.
Watch mode: save a job search once and get only the newly posted roles on every later run.
Sample run: the default returns 50 jobs so you can verify schema and quality before scaling up. Raise maxResults (up to 1000) for full runs.
Pull public LinkedIn job listings without logging in, without cookies, and without an account — just a keyword and a location. Each run returns title, company, location, salary range (when posted), seniority, posting date, applicant count, and a direct apply link. Recruiters source roles, sales teams read hiring signals, and analysts benchmark compensation — all from a clean CSV/JSON feed.
It reads only LinkedIn's public, guest-visible job data, so there is no session, no 2FA, and nothing to authenticate. You supply a search; it returns the matching postings.
🤖 For AI agents & automation
An agent can read hiring-intent from public LinkedIn listings — track which companies are hiring for which roles, by location and recency, for sales, recruiting, or market research.
Sample agent prompt:
Find 'data engineer' jobs in Austin posted in the last week and return company, title, employment type and salary.
Output fields (one item per result):
job_title — role title
company_name — hiring company
location — job location
posted_date — date posted
job_url — listing URL
job_id — LinkedIn job id
salary — salary if shown
job_type — the job-type filter you asked for (every delivered row matches it), or "Not specified"
employment_type — the employment type LinkedIn shows on the job page (Full-time, Contract, Internship…)
seniority_level, industries, applicants — from the job page
description — full job description
What you get per job
job_title, company_name, location, posted_date, job_url, job_id
salary (where LinkedIn shows it on the listing)
employment_type, seniority_level, industries, applicants (read from each job's page)
- Full job description (when
fetchDescriptions is on)
Use cases
- Recruiters — daily lists of companies hiring for a target role.
- Sales / RevOps — hiring-intent signals (a company hiring data engineers is buying a data stack).
- Comp benchmarking — aggregate salary ranges by role × geo.
- Market intel — track competitor hiring velocity over time.
- Niche job boards — power a board with fresh public LinkedIn postings.
Sample input
{
"keywords": "data engineer",
"location": "San Francisco",
"jobType": "full-time",
"maxItems": 200,
"fetchDescriptions": true
}
Several searches in one run (one run instead of one run per search): put each keyword/location pair in searches. maxResults applies per search, a posting matched by two searches is delivered once, and the run's status message gives the per-search count.
{
"searches": [
{ "keywords": "data engineer", "location": "San Francisco" },
{ "keywords": "data engineer", "location": "New York" },
{ "keywords": "analytics engineer", "location": "Remote" }
],
"maxResults": 25,
"fetchDescriptions": true
}
Sample output
{
"title": "Senior Data Engineer",
"company": "Globex",
"location": "San Francisco, CA",
"remote": false,
"salary": "$170k-$210k",
"seniority": "Senior",
"employment_type": "full-time",
"posted_date": "2026-06-22",
"applicant_count": 47,
"apply_url": "https://www.linkedin.com/jobs/view/3912...",
"easy_apply": true
}
🔌 Optional company web enrichment (paid add-on)
Set enrich_companies: true to resolve each hiring company's website and append its domain, homepage title and description (enriched_* fields). Opt-in, default off; base listing output unchanged.
Watch mode — get only what's new
Set watchMode: true to turn a one-off search into a saved monitor. Instead of returning every job posting that matches your job search, a watch run returns only the job postings that are new since your last run.
- First run seeds a baseline: it records what currently matches and returns 0 records — nothing to alert on yet.
- Every later run returns only the newly-appearing job postings (each tagged
change_type: "new_filing"), plus any whose status changed.
- Point a Schedule at it (daily/hourly) and you have a standing "New job posting match" feed.
Inputs
| field | meaning |
|---|
watchMode | true to enable watch mode (default false = a normal one-off search) |
watchLabel | optional name; two watches with the same query but different labels keep separate memory |
Worked example — watch senior data engineer roles in the US:
{
"watchMode": true,
"watchLabel": "senior data engineer roles in the US"
}
Run 1 returns 0 records and reports "baseline established: monitoring N job posting(s)". Schedule it daily; from run 2 on, you receive only the new job postings.
Run it on a schedule
Daily is the right cadence for LinkedIn jobs. New roles are posted every day and older ones close, so a daily run with postedWithin set to past_24_hours keeps a role or hiring-signal list current. Each run mostly brings new postings rather than changes to old ones. Turn on watchMode for a scheduled feed: the first run records a baseline and returns 0 records, and every later run returns only the postings that are new since the last run (tagged change_type: "new_filing"), plus any whose status changed. Send each run's results onward with a webhook, Google Sheets, Zapier, Make or the Bytewells API.
FAQ
Why use this instead of LinkedIn's own job search?
LinkedIn's job search is free in a browser, but it shows one page at a time and has no export. This actor returns postings as clean CSV or JSON with title, company, location, salary where shown, seniority, industries, applicant count and apply link. It runs several keyword and location searches in one run, removes duplicates, and checks your job type against each job's own page, because LinkedIn's logged-out search ignores job-type filters. It needs no login, no cookies and no API key, and you pay per listing.
What does a typical run cost?
One flat monthly price, with a free trial; there is no per-row charge. Bytewells bills compute separately from your prepaid credit, by memory and run time.
Can I scrape LinkedIn jobs without logging in?
Yes. The actor reads only LinkedIn's public, guest-visible job data, so there is no account, session cookie or 2FA. You supply a keyword (required) and optionally a location, job type and posting window.
Does LinkedIn job data include salaries?
Only where LinkedIn shows a salary on the posting, which is typically a subset of jobs depending on role and location. When no salary is posted, the field is empty.
How do I find companies that are hiring for a specific role?
Search the role your product or service maps to, for example data engineer, and set postedWithin to keep only fresh postings. The companies posting that role are investing in that area, which you can use as a hiring-intent signal. Turn on enrich_companies to add each company's domain, homepage title and description.
How many jobs can one run return?
Up to 1,000 per search, set with maxResults (default 50). With several searches in searches, the limit applies per search, and a posting matched by two searches is delivered once. Full descriptions are included only when fetchDescriptions is on.
How it is billed on Bytewells
One flat monthly price for this actor, with a free trial to start. There is no charge per row, per result or per run from us.
Compute is billed separately by Bytewells from your prepaid credit, by memory and run time (per second). If a run uses the platform's proxy, Bytewells adds that traffic to the same run's compute bill.
Run it from the API
Bytewells speaks the same API as Apify, so curl or apify-client work with the Bytewells address and your Bytewells API key.
curl -X POST "https://bytewells.com/api/v2/acts/nexgendata~linkedin-jobs-scraper/runs" \
-H "Authorization: Bearer YOUR_BYTEWELLS_API_KEY" \
-H "Content-Type: application/json" \
-d '{"keywords": "python developer", "searches": [], "maxResults": 50, "enrich_companies": false}'
# when the run has finished, read its rows:
curl -H "Authorization: Bearer YOUR_BYTEWELLS_API_KEY" \
"https://bytewells.com/api/v2/actor-runs/RUN_ID/dataset/items"
from apify_client import ApifyClient
client = ApifyClient("YOUR_BYTEWELLS_API_KEY", api_url="https://bytewells.com/api")
run = client.actor("nexgendata/linkedin-jobs-scraper").call(run_input={'keywords': 'python developer', 'searches': [], 'maxResults': 50, 'enrich_companies': False})
for row in client.dataset(run["defaultDatasetId"]).iterate_items():
print(row)
Contact
Questions, a source that changed, or a field you need: [email protected]