# LinkedIn Jobs Scraper (No Login) - Salary, Poster, Company (`superxcoder/linkedin-jobs-scraper`) Actor

Scrape LinkedIn jobs without login. Search by title, location and filters, or paste search, job or company URLs. Full description, parsed salary, applicants, apply type, job poster and company profile. Same input as bebity's scraper. $1 per 1,000 jobs, all included.

- **URL**: https://apify.com/superxcoder/linkedin-jobs-scraper.md
- **Developed by:** [Deependra Singh](https://apify.com/superxcoder) (community)
- **Categories:** Jobs, Lead generation, Automation
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.70 / 1,000 jobs

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## LinkedIn Jobs Scraper (No Login)

Scrape LinkedIn job postings into JSON, CSV or Excel without a LinkedIn account, cookies or login. Search by job title, location and filters, or paste a LinkedIn search URL, a single job URL or a company page. Every job comes with the full description, a parsed salary, applicant count, apply type, the person who posted it (name, headline, photo) and the hiring company's profile. **$1 per 1,000 jobs, everything included.**

### Why this LinkedIn jobs scraper

- **Filters that actually filter.** In 2026 LinkedIn stopped applying the workplace type, employment type, experience level and industry filters for logged-out visitors: the URL accepts them, but the results do not change. This Actor checks every job against those filters itself, so a "Remote" search returns remote jobs, not whatever LinkedIn shows.
- **Locations that mean what you typed.** Each location is matched to LinkedIn's own location list first. "UK" becomes United Kingdom (LinkedIn's lookup says Ukraine), "Czech Republic" becomes Czechia (LinkedIn's text search returns Dominican Republic). The match is printed in the log.
- **Everything in one price.** Poster headline and photo, salary source, job state, repost flag and the full company profile are included. No premium add-ons.
- **More than 1,000 jobs per search.** LinkedIn stops every search at about 1,000 results. Turn on **Split large searches by region** and a country search continues state by state (13 countries supported), with duplicates removed.
- **Only new jobs on a schedule.** Give the run a monitoring store name and each run skips the jobs earlier runs already delivered, so a daily schedule returns, and charges for, only fresh postings.
- **You only pay for jobs you keep.** Jobs dropped by your filters, duplicates across searches and failed pages are never charged.
- **Drop-in compatible with bebity/linkedin-jobs-scraper.** Same input field names (`titles`, `locations`, `rows`, `workTypes`, `contractTypes`, `experienceLevels`, `industry`, `publishedAt`, `easyApply`, `under10Applicants`, `companyName`, `companyId`, `companyUrls`, `startUrls`, `maxRowsPerUrl`, `companyProfile`) and the same output field names, so saved tasks, API calls and n8n or Make flows keep working.

### What you can do with it

- **Job hunting:** schedule a daily run for your title and city and get new postings within hours.
- **Recruiting and sales intelligence:** see which companies are hiring for what, where and at what pay, and who posted each role.
- **Labor market research:** salaries, seniority, employment types and applicant counts by market.
- **AI agents and automations:** call it from Claude, ChatGPT, n8n, Make or Zapier and get typed JSON back. Through Apify's MCP server (`https://mcp.apify.com`) it shows up as a tool with its input schema.

### How to use it

**Option 1: fill in the search.** Enter one or more job titles and locations. Each title is searched in each location (3 titles x 2 locations = 6 searches) and every job records which search found it. Add filters as needed.

**Option 2: paste LinkedIn URLs.** Search on LinkedIn, copy the URL from the address bar and paste it into **LinkedIn URLs**. All its filters are kept. You can also paste a single job (`linkedin.com/jobs/view/...`) or a company page (`linkedin.com/company/openai`) to get every open job at that company.

#### Input example

```json
{
    "titles": ["Data Analyst"],
    "locations": ["United States"],
    "workTypes": ["2"],
    "experienceLevels": ["2", "3"],
    "publishedAt": "r604800",
    "rows": 100,
    "companyProfile": true
}
```

#### Filters

| Filter | Field | Applied by |
|---|---|---|
| Date posted | `publishedAt` | LinkedIn |
| Easy Apply only | `easyApply` | LinkedIn, double-checked on each job |
| Under 10 applicants | `under10Applicants` | LinkedIn |
| Company (name, ID or page URL) | `companyName`, `companyId`, `companyUrls` | LinkedIn |
| Workplace type (on-site, remote, hybrid) | `workTypes` | this Actor, from the posting text |
| Employment type | `contractTypes` | this Actor, from the job's "Employment type" |
| Experience level | `experienceLevels` | this Actor, from the job's "Seniority level" |
| Industry (434 to pick from) | `industry` | this Actor, from the job's "Industries" |
| Apply type (Easy Apply or external) | `applyTypes` | this Actor, from the Apply button |
| Every title word in the job title | `strictTitleMatch` | this Actor |
| Exclude companies (e.g. agencies) | `excludeCompanies` | this Actor |
| Only jobs with a salary | `salaryOnly` | this Actor |
| Exclude job titles (e.g. "Senior", "Intern") | `titleExclude` | this Actor, before the job is opened |
| Exclude recruiting agencies | `excludeRecruitingAgencies` | this Actor, from the company's industry |
| Company size range | `companySizeMin`, `companySizeMax` | this Actor, from the company's declared size |
| Distance from a city | `distance` | LinkedIn |
| Skip known jobs | `skipJobIds`, `seenJobsStoreName` | this Actor |

### Output

One row per job. Example (trimmed):

```json
{
    "id": "4444390846",
    "title": "Enterprise Data Analyst",
    "companyName": "Husch Blackwell",
    "companyUrl": "https://www.linkedin.com/company/husch-blackwell",
    "companyId": "13646",
    "location": "Washington, DC",
    "locationParsed": { "city": "Washington", "region": "DC", "country": "United States", "formatted": "Washington, DC" },
    "workType": "Hybrid",
    "workTypeSource": "description",
    "contractType": "Full-time",
    "experienceLevel": "Not Applicable",
    "sector": "Law Practice",
    "salary": "$70,000 USD - $85,000 USD",
    "salaryMin": 70000,
    "salaryMax": 85000,
    "salaryCurrency": "USD",
    "salaryPeriod": "year",
    "salarySource": "Job description",
    "applicationsCount": "Over 200 applicants",
    "applicantsCount": 200,
    "postedTime": "2 weeks ago",
    "publishedAt": "2026-09-28",
    "repostedJob": false,
    "jobState": "LISTED",
    "applyType": "EXTERNAL",
    "jobUrl": "https://www.linkedin.com/jobs/view/4444390846",
    "description": "Husch Blackwell LLP is a full-service litigation and business law firm...",
    "posterFullName": "Kelli Watson, MHRM, SHRM-CP",
    "posterHeadline": "Senior Talent Acquisition Specialist at Husch Blackwell",
    "posterProfileUrl": "https://www.linkedin.com/in/kelliwatson-hr",
    "companyWebsite": "https://www.huschblackwell.com",
    "companyEmployeeCount": 1890,
    "companySize": "1,001-5,000 employees",
    "companyFollowerCount": 98000,
    "companyType": "Partnership",
    "companyFoundedYear": 1916,
    "scrapingInfo": { "page": 1, "index": 3, "inputUrl": "", "title": "Data Analyst", "location": "United States" }
}
```

The dataset has three views: **Jobs**, **Company** and **Job poster**. The run also stores `RUN-SUMMARY` (per search: LinkedIn's result count, pages read, jobs delivered, jobs filtered out and why) and `SEARCH_TOTALS` (how many jobs LinkedIn says match each search).

#### Field notes

- `workType` is read from the title, location and description, because LinkedIn no longer shows the workplace label to logged-out visitors. `workTypeSource` says where it came from; `default` means the posting says nothing, which LinkedIn treats as on-site.
- `salarySource` is `LinkedIn listing` when LinkedIn shows a pay range and `Job description` when the range was found in the text.
- `repostedJob` is inferred: logged-out pages no longer say "Reposted", but LinkedIn job IDs grow over time, so a job shown as posted more than 30 days after its ID was issued has been reposted. About a quarter of postings are.
- `benefits` holds only real benefits ("Medical insurance"); LinkedIn's badges ("Actively Hiring", "Be an early applicant") are in `jobInsights`, and `activelyHiring` is true when the badge is shown.
- Company profile fields are empty for universities and schools: LinkedIn shows school pages only to signed-in members.
- `verified` is always null: LinkedIn shows the verification badge only to signed-in members. The column is kept so files match bebity's layout.
- `publishedAt` is a date; `postedAtTimestamp` is that date at midnight UTC.
- `yearsOfExperienceMin` is the smallest number of years of experience the description asks for ("3-5 years of experience" gives 3), or null.
- `jobFunction` is always null: LinkedIn no longer shows the job function to logged-out visitors.
- `applyType` is `EASY_APPLY` or `EXTERNAL`, read from the Apply button; it is null for closed jobs and for the rare listing LinkedIn shows without any Apply button.
- `applyUrl` is the LinkedIn job page. Logged out, LinkedIn routes every Apply button through its own page and does not expose the employer's application link.

### Pricing

Pay per event: **$1.00 per 1,000 jobs** on the free plan, less on paid plans. Company profile, poster details and every field above are included. Jobs removed by your filters, duplicates and failures are free. Set a max cost on the run and the Actor stops at that budget.

### Limits

- LinkedIn shows at most about 1,000 jobs per search. Use **Split large searches by region** for country searches, or add more specific locations or titles.
- Many employers leave the seniority level as "Not Applicable". Those jobs never match an experience-level filter; the log tells you when this is why a search came back small.
- Searches with very narrow filters (for example Remote + Director in a small city) can need many pages per kept job. A search stops early only when its first 300 jobs all fail the filters, with a note in the log.
- A run stops about a minute before its **Timeout** (run options, 1 hour by default) and ends as succeeded with the jobs it has; the status message and `RUN-SUMMARY` say so. Count on about 50 jobs a minute at the default concurrency of 10, company profiles included, and raise the timeout for big runs (many titles and locations, or **Split large searches by region**).
- LinkedIn changes its pages from time to time. The Actor is tested on a schedule; open an issue if something looks wrong.

### FAQ

**Do I need a LinkedIn account?** No. The Actor reads public job pages, the same ones anyone sees without signing in. No cookies, no account risk.

**Is scraping LinkedIn jobs legal?** Job postings are published for the public. Results can still include personal data, such as the name of the person who posted a job; handle it under GDPR and similar laws, and only keep it with a legitimate reason.

**Can I run it every day and get only new jobs?** Yes. Schedule it and set **Only new jobs (monitoring store)** to any name, e.g. `my-job-alerts`. Each run skips what earlier runs with that name already delivered.

**Which location will "London" or "Paris" use?** The one LinkedIn ranks first: London, United Kingdom and Paris, France. Add a region to pick another one ("London, Ontario"). The log prints every match.

**Why do I get fewer jobs than LinkedIn shows when signed in?** Signed-in users get personalised results. Logged-out search returns a smaller list and caps at about 1,000 per search. Splitting by location usually closes most of the gap.

**I switched from bebity's scraper. Do I need to change my input?** No. The same fields work. `enrichCompany` is accepted and ignored, because everything it unlocked is always included here.

# Actor input Schema

## `titles` (type: `array`):

What to search for, e.g. "Data Analyst". Each title is its own search, crossed with every location below. Every result records the title it came from in scrapingInfo. Leave empty to search all jobs in the locations.

## `locations` (type: `array`):

Cities, regions or countries, e.g. "Berlin", "Texas", "United Kingdom". Each location is matched to LinkedIn's own location list first (so "UK" means United Kingdom, not Ukraine, and "Czech Republic" means Czechia); the match is printed in the log. A place LinkedIn does not know is searched as text, and only jobs whose location contains it are kept.

## `rows` (type: `integer`):

How many jobs to return for each title x location search, after filters. 0 = all LinkedIn shows. LinkedIn stops every search at about 1,000 jobs; turn on "Split large searches by region" to go past that. Use "Max jobs in total" or the run's cost limit to cap the whole run.

## `splitByLocation` (type: `boolean`):

LinkedIn returns at most about 1,000 jobs per search. When a country search reaches that limit, continue it state by state (or city by city for the UK): United States, Canada, India, Germany, United Kingdom, Australia, France, Netherlands, Spain, Italy, Brazil, Mexico, Poland. Jobs found twice are delivered once. Each row records its region in scrapingInfo.region.

## `publishedAt` (type: `string`):

Only jobs posted within this window. Applied by LinkedIn. Via the API any "r<seconds>" value works, e.g. "r43200" for 12 hours.

## `distance` (type: `integer`):

Search radius around a city, as on LinkedIn (e.g. 10, 25, 50). Leave empty for LinkedIn's default.

## `companyName` (type: `array`):

Only jobs at these companies, e.g. "Google". Each name is looked up on LinkedIn and the match is printed in the log.

## `companyId` (type: `array`):

Only jobs at these companies, by numeric LinkedIn ID (e.g. Google = 1441). Numbers only.

## `companyUrls` (type: `array`):

Only jobs at these companies, by LinkedIn page, e.g. https://www.linkedin.com/company/openai/. Narrows the title and location search above. To get every job of a company instead, paste its URL into "LinkedIn URLs".

## `workTypes` (type: `array`):

On-site, Remote and/or Hybrid. LinkedIn no longer applies this filter for logged-out visitors, so each posting is classified from its title, location and description (see workTypeSource in the output). Matches any selected value.

## `contractTypes` (type: `array`):

Matches the "Employment type" LinkedIn prints on each job. Checked on every job, because LinkedIn ignores this filter for logged-out visitors.

## `experienceLevels` (type: `array`):

Matches the "Seniority level" LinkedIn prints on each job. Checked on every job. Note that many employers leave it as "Not Applicable"; those jobs are dropped when this filter is set.

## `industry` (type: `array`):

Matches the "Industries" LinkedIn prints on each job (the hiring company's industry). Pick one or more.

## `easyApply` (type: `boolean`):

Only jobs you can apply to on LinkedIn (Easy Apply). Applied by LinkedIn and double-checked on each job.

## `applyTypes` (type: `array`):

Keep only Easy Apply or only external-apply jobs. Read from the job's Apply button.

## `under10Applicants` (type: `boolean`):

Only jobs with fewer than 10 applicants so far. Applied by LinkedIn.

## `strictTitleMatch` (type: `boolean`):

LinkedIn's keyword search also returns jobs that only mention your words in the description ("CTO" can return sales roles). Turn this on to keep only jobs whose title contains every word you searched.

## `titleExclude` (type: `array`):

Drop jobs whose title contains any of these words or phrases, e.g. "Senior", "Intern", "Sales Manager" (a phrase matches when every word is in the title). Checked before the job is opened, never charged.

## `excludeCompanies` (type: `array`):

Drop jobs from these companies (name or LinkedIn company slug), e.g. staffing agencies. Not charged.

## `excludeRecruitingAgencies` (type: `boolean`):

Drop jobs posted by staffing and recruiting agencies (by the company's LinkedIn industry).

## `companySizeMin` (type: `integer`):

Keep only jobs at companies with at least this many employees on LinkedIn. Turns the company profile on. Jobs at companies without a stated size are dropped.

## `companySizeMax` (type: `integer`):

Keep only jobs at companies with at most this many employees on LinkedIn, e.g. 200 for startups and small businesses.

## `salaryOnly` (type: `boolean`):

Keep only jobs that show a pay range, on LinkedIn or in the description.

## `startUrls` (type: `array`):

Paste any of: a LinkedIn job search URL copied from your browser (all its filters are kept, including the ones LinkedIn ignores logged out, which are checked on each job), a single job URL (linkedin.com/jobs/view/...), or a company page URL to get every open job at that company. When URLs are given, the search fields above are ignored; the filter fields still apply.

## `maxRowsPerUrl` (type: `integer`):

How many jobs to return from each pasted search or company URL. 0 = all LinkedIn shows (up to about 1,000).

## `maxItems` (type: `integer`):

Stop the whole run after this many jobs. 0 = no limit (the run's cost limit still applies).

## `seenJobsStoreName` (type: `string`):

For scheduled runs: name a key-value store (e.g. "my-job-monitor") and each run skips jobs that an earlier run with the same name already delivered. You only pay for new jobs. The Actor creates the store.

## `skipJobIds` (type: `array`):

Job IDs or job URLs to leave out, e.g. jobs you already have.

## `companyProfile` (type: `boolean`):

Add the hiring company's profile to each job: website, exact headcount, size bracket, followers, industry, description, tagline, headquarters, company type, founding year and specialties. Fetched once per company. Included in the price.

## `enrichCompany` (type: `boolean`):

Kept for compatibility with saved tasks. The poster's headline and photo, salary source, job state and repost flag come with every job at no extra cost.

## `maxConcurrency` (type: `integer`):

How many LinkedIn pages to load at once. Lower it if you see many blocked requests in the log.

## `proxy` (type: `object`):

Leave as is for the best results: requests go through Apify datacenter proxies and switch to residential proxies only while LinkedIn is blocking (proxy costs are included in the price). Pick a proxy group yourself only if you need to; it is then used for every request.

## Actor input object example

```json
{
  "titles": [
    "Software Engineer"
  ],
  "locations": [
    "Berlin"
  ],
  "rows": 0,
  "splitByLocation": false,
  "publishedAt": "",
  "easyApply": false,
  "under10Applicants": false,
  "strictTitleMatch": false,
  "excludeRecruitingAgencies": false,
  "salaryOnly": false,
  "maxRowsPerUrl": 0,
  "maxItems": 30,
  "companyProfile": true,
  "enrichCompany": true,
  "maxConcurrency": 10,
  "proxy": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

## `jobs` (type: `string`):

No description

## `all` (type: `string`):

No description

## `summary` (type: `string`):

No description

## `totals` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "titles": [
        "Software Engineer"
    ],
    "locations": [
        "Berlin"
    ],
    "maxItems": 30,
    "proxy": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("superxcoder/linkedin-jobs-scraper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "titles": ["Software Engineer"],
    "locations": ["Berlin"],
    "maxItems": 30,
    "proxy": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("superxcoder/linkedin-jobs-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "titles": [
    "Software Engineer"
  ],
  "locations": [
    "Berlin"
  ],
  "maxItems": 30,
  "proxy": {
    "useApifyProxy": true
  }
}' |
apify call superxcoder/linkedin-jobs-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,superxcoder/linkedin-jobs-scraper"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/Z1OcoQySvvP3LqOPG/builds/0WT53MCAEXw2WqewB/openapi.json
