# Advanced LinkedIn Jobs Scraper — No Cookies, Bulk, Company Data (`b2bsearch/linkedin-jobs-scraper`) Actor

Scrape LinkedIn jobs in bulk by title, location, company, seniority, salary, industry and posting date. Every job: full description, company card (website, size, industry, funding) and recruiter. No cookies, no 1,000-job cap, 1,000 jobs in ~30 s, free count first. $0.80 per 1,000 jobs.

- **URL**: https://apify.com/b2bsearch/linkedin-jobs-scraper.md
- **Developed by:** [B2B Enrich Search](https://apify.com/b2bsearch) (community)
- **Categories:** Jobs, Lead generation, AI
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: 5.00 out of 5 stars

## Pricing

from $0.80 / 1,000 job delivereds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

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

### What does Advanced LinkedIn Jobs Scraper do?

**Advanced LinkedIn Jobs Scraper** turns LinkedIn job postings into structured rows: the job with its full description, the employer's company card (website, industry, headcount, country, funding) and the recruiter or hiring manager when the posting names one. Search in bulk by **job title, location, company, seniority, employment type, salary, industry and posting date**, then export to JSON, CSV or Excel, or read the dataset through the API.

The rows come from our own database of **50M+ postings from the last 180 days**, not from a browser session on LinkedIn. There are no cookies, no login, no proxies, no CAPTCHAs and no 1,000-jobs-per-search ceiling, and a search that matches nothing costs nothing. **$0.80 per 1,000 jobs**, company card and recruiter card included.

#### At a glance

| | |
|---|---|
| **What you get** | One row per job: title, description (text and HTML), location, posted date, seniority, employment type, salary, applicants, benefits, required experience, the company card and the recruiter card |
| **Search by** | Job titles (boolean search allowed), locations (country, US state, city), companies, seniority, employment type, salary, industry, posting date, applicant count |
| **Price** | $0.80 per 1,000 jobs delivered. Company and recruiter cards are included; empty searches and refused inputs are free |
| **Speed** | 1,000 jobs in about 30 seconds (measured 26 October 2026) |
| **Scale** | Up to 10,000 jobs per run in one search across all your titles and locations; free Apify plan up to 100 |
| **Free preview** | `previewOnly` counts the matches and breaks them down by country, seniority, type, industry, employer, title and city before you buy |
| **Add-ons** | The employer's hiring pace on every row (free), the recruiter's full profile, the employer's decision makers |
| **Login** | No LinkedIn account, cookies or API key |
| **Runs from** | Apify Console, REST API, schedules and webhooks, n8n, Make, Zapier, and MCP for AI agents |

#### Why use a database scraper instead of a browser scraper?

- **Filters that are applied, not guessed.** Since LinkedIn moved its job search to an AI mode (August 2026), live scrapers can no longer apply seniority, employment type or industry as filters and rewrite them into search text instead. Here they are columns of a database and are applied exactly. The seniority filter works in every country, not only English-speaking ones.
- **No 1,000-job ceiling, no double billing.** Titles, locations and companies combine into one search. A job that matches two of your titles is delivered and charged once, and the walk continues to your cap, up to 10,000 jobs.
- **Faster.** 1,000 software engineer postings in the United States from the last month arrived in 29 seconds on 26 October 2026. The best-known browser-based scraper on this Store quotes about two minutes per 1,000.
- **Cheaper, with more on the row.** $0.80 per 1,000 jobs against $1.00 for that scraper (price read from its Store page on 26 October 2026), with the company card and the recruiter card included.
- **Locations as you write them.** `UK`, `United Kingdom`, `gb` and `London` all work. No geo ids, and no scraper reading `UK` as Ukraine.
- **Count first, free.** `previewOnly` tells you how many jobs match and how they split before you spend anything.
- **Your saved input runs unchanged.** `jobTitles`, `locations`, `company`, `employmentType`, `experienceLevel`, `salary`, `under10Applicants`, `postedLimit`, `industryIds` and `maxItems` keep their names and values.
- **From the job to the buyer.** `employerHiring` adds how many roles the employer has open and how fast it posts. `decisionMakers` adds its founders, C-level, VPs and directors as rows of their own.

#### Who uses LinkedIn job data, and how?

| If you are | You can | Start with |
|---|---|---|
| A recruiter or staffing agency | Watch new openings at target accounts, see which companies are scaling a team, reach the recruiter named on the posting | Titles or `company`, `postedLimit: "24h"`, a daily schedule, `employerHiring: true` |
| A job board or newsletter | Fill the site with fresh postings and push only the new ones into your database | Titles and a country, `postedLimit: "24h"`, `jobId` as the unique key |
| A B2B sales team | Turn hiring into prospect lists. A company hiring for the roles your product serves is a company spending on that problem; add its decision makers in the same run | Titles that match your ideal customer, `excludeAgencies: true`, `decisionMakers: true` |
| A market researcher | Measure demand for a role across countries and cities, benchmark the salaries employers publish, follow hiring over time | `previewOnly: true` for the breakdown, then the rows with `salaryOnly: true` |
| A competitive-intelligence team | Follow how competitors hire: which roles, where, at what level | `company` with the competitors, `postedLimit: "week"`, a weekly schedule |
| An AI agent builder | Give an agent live job data through MCP, with a free count before any paid call | The AI agents section below |

### What data do you get for each job?

| Field | Example | Filled |
|---|---|---|
| `jobId`, `url` | `4012345678`, `https://www.linkedin.com/jobs/view/4012345678/` | always |
| `title` | `Senior Software Engineer, Payments` | always |
| `postedAt`, `validUntil`, `isOpen`, `closedAt` | `2026-10-15T09:30:00Z`, `true` | always; `validUntil` when stated |
| `companyName`, `companyDomain`, `companyIndustry`, `companyEmployeeCount`, `companyCountryCode`, `companyUrl` | `Example Corp`, `example.com`, `Software Development`, `1240`, `US` | the card is on 99% of postings |
| `company` | the full card: website, industry, headcount, HQ, founded, funding, description | same |
| `locationText`, `location` | `Austin, TX`; `{ countryCode, region, locality, postalCode, lat, lon }` | always; coordinates on 57% |
| `seniorityLevel`, `employmentTypeName` | `Mid-Senior level`, `Full-time` | as stated on the posting |
| `industries` | `[{ id: 4, name: "Software Development" }]` | 99% |
| `salaryText`, `salary` | `$150K/yr - $190K/yr`; `{ min, max, currency, period, annualMin, annualMax }` | 13% overall; 50% in the Netherlands, 41% in China |
| `applicants`, `applicantRange` | `48`, `25-50 applicants` | the count on 10%, the bucket on every posting |
| `requiredExperienceMonths`, `education`, `benefits` | `60`, `Bachelor degree`, `["Medical insurance"]` | experience on 22% |
| `description`, `descriptionHtml` | the posting text, plain and as HTML | always, when `descriptionType` asks for it |
| `recruiterName`, `recruiterHeadline`, `recruiterUrl`, `recruiter` | `Jane Doe`, `Technical Recruiter at Example Corp` | on the 11% of postings that name one (28% in India, 17% in the UK, 7% in the US) |
| `recruiterProfile` | the recruiter's full career profile | with `recruiter: "profile"`, where the profile is on record |
| `employerOpenJobs`, `employerJobs7d`, `employerJobs30d`, `employerJobs90d`, `employerLastPostedAt`, `employerTopTitles30d`, `employerHiring` | `42`, `6`, `19`, `41`, `2026-10-21T08:00:00Z`, `["Software Engineer", "Account Executive"]` | with `employerHiring: true`, where the employer has a signal on record |
| `_type`, `_status`, `_input` | `job`, `found`, the filters and `dataAsOf` | always |

Unknown facts are left out, never shipped as empty strings. Every row's `_input.dataAsOf` is the date of the newest posting in the database at the time of the run.

### How to scrape LinkedIn jobs (3 steps)

1. Enter one or more **job titles** and one or more **locations** (a country by name or code, a US state, a city), or a list of **companies**.
2. Narrow with seniority, employment type, salary, industry and posting date. Set **Max jobs**, your spend cap. Press **Start**. To size the search first, tick **Preview only**: the count and the breakdown are free.
3. Export the dataset as JSON, CSV or Excel, or read it through the API. Run it on a schedule with **Posted in the last: 24 hours** for a daily feed.

A search needs a country or a company: a job title or a city alone is not a search here.

#### How do I scrape LinkedIn jobs without cookies or a login?

You do not log in anywhere. The Actor reads a database of postings, so it never touches a LinkedIn account, yours or anyone's. There is nothing to authenticate, no session to keep alive and no account to put at risk.

#### How do I get more than 1,000 LinkedIn jobs?

Set **Max jobs** to the number you want, up to 10,000, or to `0` for the full 10,000. The 1,000-per-search ceiling of browser scrapers does not exist here: one search walks the database to your cap, across all your titles and locations at once.

### How much does it cost to scrape LinkedIn jobs?

**$0.80 per 1,000 jobs delivered** ($0.0008 per job), plus the Actor start fee of $0.0002 per run.

| Row | Price |
|---|---|
| Job delivered (`_status: found`, `_type: job`) | $0.0008 |
| Company card and recruiter card on the row | $0, included |
| The employer's hiring pace on the row (`employerHiring: true`) | $0, included |
| Recruiter's full profile (`recruiter: "profile"`), per job that carries one | $0.0032 |
| Decision maker of the employer (`decisionMakers: true`), per person | $0.0032 |
| Preview and breakdown rows (`previewOnly`) | $0 |
| Search that matches nothing, refused input, company entry that is not usable | $0 |

`maxItems` is your spend cap: at the default of 100 a run costs at most $0.08 for the jobs. The decision-maker add-on is capped by `decisionMakersPerCompany` (default 3) per employer. Prices are the same on every Apify plan.

### Input

The form has five sections: **Search**, **Filters**, **People behind the job**, **Output** and **Compatibility**. An agent or an API call sends the same field names as JSON:

```json
{
  "jobTitles": ["software engineer", "data engineer"],
  "locations": ["United States", "California"],
  "company": ["https://www.linkedin.com/company/google", "stripe.com", "Netflix"],
  "maxItems": 500,
  "postedLimit": "week",
  "experienceLevel": ["mid-senior", "director"],
  "employmentType": ["full-time"],
  "industryIds": ["Software Development"],
  "salary": ["120k+"],
  "excludeAgencies": true,
  "employerHiring": true,
  "recruiter": "card",
  "descriptionType": "both"
}
```

**Search**

- `jobTitles`: words the title must contain, one query per line; a job matching any line is kept. A line may use boolean search: `AND`, `OR`, `NOT`, `"exact phrase"`, parentheses. Up to 10.
- `locations`: countries by name or two-letter code (`United States`, `Germany`, `gb`; `UK` and `USA` are read as the countries they name), US states (`California`), cities (`Berlin`, `Austin, Texas`). Any of them. A whole country next to a city elsewhere is refused rather than searched as nothing. Up to 60.
- `maxItems`: jobs for the whole run (default 100, `0` = up to 10,000). Free Apify plan: up to 100.
- `previewOnly`: the count and breakdown instead of the rows, free. `compact`: short rows for AI agents.
- `company`: page URL, page slug, website domain, numeric id or company name (matched as part of the name). Up to 100. An entry that cannot be read is a free row and is left out.
- `postedLimit`: `any`, `1h`, `24h`, `week`, `month`, `3months`, `6months`.

**Filters**

- `experienceLevel`: `internship`, `entry`, `associate`, `mid-senior`, `director`, `executive`, `not-applicable`.
- `employmentType`: `full-time`, `part-time`, `contract`, `internship`, `temporary`, `volunteer`, `other`.
- `industryIds`: industry names (`Software Development`, `Hospitals and Health Care`) or numeric ids (`4` for Software Development); an industry covers its sub-industries. Up to 30.
- `salary`: yearly ranges, one per line: `100k+`, `60k-80k`, `100000-150000`, in `salaryCurrency` (default USD). Postings without a stated salary never match. `salaryOnly` keeps every posting that states one.
- `under10Applicants`: keeps the lowest applicant bucket, fewer than 25 applicants. The data keeps applicant counts in buckets of 25.
- `excludeAgencies`: drops postings by staffing firms and job-board aggregators (by the posting's industry, and by the employer's own card: its industry and what its description says it does), and by employers with far more postings than employees (at least 200 open, or 100 posted in the last 30 days, and more than two per employee). Off by default.
- `postedLimitDate`: a date instead of the window; with both, the later one wins.

**People behind the job**

- `recruiter`: `card` (default, free), `profile` (the full career profile, $3.20 per 1,000 jobs that carry one) or `none`.
- `employerHiring`: the employer's hiring signal on each row, free: `employerOpenJobs`, `employerJobs7d`, `employerJobs30d`, `employerJobs90d`, `employerLastPostedAt`, `employerTopTitles30d`.
- `decisionMakers`, `decisionMakerRoles`, `decisionMakersPerCompany`, `decisionMakerTitleKeywords`: the leadership add-on, see below.

**Output and compatibility**

- `descriptionType`: `both` (default), `text`, `html` or `none`. Same price.
- `sortBy`: accepted for other scrapers' saved inputs; rows are always newest first.

### Output

One row per job. The flat columns lead (what a spreadsheet reads), then the job as recorded. Values below are made up:

```json
{
  "_status": "found",
  "_type": "job",
  "_input": { "filters": { "title": "software engineer", "countries": ["us"] }, "dataAsOf": "2026-10-21" },
  "jobId": 4012345678,
  "title": "Senior Software Engineer, Payments",
  "url": "https://www.linkedin.com/jobs/view/4012345678/",
  "postedAt": "2026-10-15T09:30:00Z",
  "companyName": "Example Corp",
  "companyDomain": "example.com",
  "companyIndustry": "Software Development",
  "companyEmployeeCount": 1240,
  "companyCountryCode": "US",
  "locationText": "Austin, TX",
  "countryCode": "US",
  "seniorityLevel": "Mid-Senior level",
  "employmentTypeName": "Full-time",
  "salaryText": "$150K/yr - $190K/yr",
  "applicantRange": "25-50 applicants",
  "recruiterName": "Jane Doe",
  "recruiterHeadline": "Technical Recruiter at Example Corp",
  "recruiterUrl": "https://www.linkedin.com/in/jane-doe-example",
  "employerOpenJobs": 42,
  "employerJobs7d": 6,
  "employerJobs30d": 19,
  "employerJobs90d": 41,
  "employerLastPostedAt": "2026-10-21T08:00:00Z",
  "employerTopTitles30d": ["Software Engineer", "Account Executive"],
  "isOpen": true,
  "validUntil": "2026-11-14T09:30:00Z",
  "location": { "text": "Austin, TX", "countryCode": "US", "region": "TX", "locality": "Austin" },
  "seniority": { "id": 16, "name": "Mid-Senior level" },
  "employmentType": { "id": 1, "name": "Full-time" },
  "industries": [{ "id": 4, "name": "Software Development" }],
  "salary": { "min": 150000, "max": 190000, "currency": "USD", "period": "year", "text": "$150K/yr - $190K/yr" },
  "benefits": ["Medical insurance", "401(k)"],
  "description": "About the role…",
  "descriptionHtml": "<p>About the role…</p>",
  "company": { "id": 123456, "name": "Example Corp", "domain": "example.com", "employeeCount": 1240, "industry": "Software Development", "countryCode": "US", "funding": { "hasFunding": true, "lastRoundType": "series_c" }, "hq": { "locality": "Austin, TX", "region": "TX" } },
  "recruiter": { "kind": "person", "slug": "jane-doe-example", "url": "https://www.linkedin.com/in/jane-doe-example", "fullName": "Jane Doe", "headline": "Technical Recruiter at Example Corp" },
  "employerHiring": { "openJobs": 42, "jobs7d": 6, "jobs30d": 19, "jobs90d": 41, "lastPostedAt": "2026-10-21T08:00:00Z", "seniorities30d": [{ "id": 16, "name": "Mid-Senior level", "count": 11 }], "countries30d": ["us"], "topTitles30d": ["Software Engineer", "Account Executive"], "asOf": "2026-10-22T00:00:00Z" }
}
```

The `employer…` columns are there when `employerHiring` is on. Free rows say why they are not a result: `no_results` (nothing matched), `invalid` (a company entry that could not be read), `input_problem` (a refused input, with the field named), `no_decision_makers` (an employer with nobody at the chosen roles), `error` (the service could not answer; nothing charged).

The **Output tab** has five views: *Overview*, *Company & recruiter*, *Employer hiring*, *Decision makers* and *Preview & breakdown*.

### Decision makers: from a job to the people who hire

Turn on `decisionMakers` and every employer in the result is followed by its decision makers, each as a row of its own (`_type: decision_maker`): founders and owners, C-level, VPs and directors, ranked founder + CEO first, assistants and "Office of the CEO" staff removed, each person once per run. Each row carries `fullName`, `jobTitle`, `seniority`, `headline`, `linkedinUrl`, `location`, `forJobId` and `forJobTitle`; the email that reaches the person today rides the row when one is on record and the contact tier is on.

- `decisionMakerRoles`: `founder`, `cxo`, `vp`, `director`; default all four.
- `decisionMakersPerCompany`: people per employer, in rank order (default 3, maximum 25).
- `decisionMakerTitleKeywords`: keep only titles containing any of these words (`sales`, `engineering`, `talent`). C-level acronyms (`cto`, `coo`, `cfo` …) match as whole words, so `cto` finds "Co-Founder & CTO" and not "Director".

$3.20 per 1,000 people delivered; an employer with nobody at the chosen roles is a free row. For the same list without the jobs, use [Companies Hiring Now](https://apify.com/b2bsearch/hiring-companies-finder).

### Migrating from another LinkedIn job scraper

Your saved input runs as it is: the field names and values are the same.

| Their field | Here |
|---|---|
| `jobTitles`, `locations`, `company`, `employmentType`, `experienceLevel`, `salary`, `under10Applicants`, `postedLimit`, `industryIds` | same names, same values |
| `maxItems` | **per run**, not per title and location |
| `sortBy`, `page` | accepted and ignored: newest first, the walk pages itself |
| `cookie`, `userAgent`, `proxy` | not needed |
| `workplaceType` (remote / hybrid / office), `easyApply`, `geoIds` | **not in this database**: a run that sets one stops before anything is charged and names the field |

The last line is deliberate. A remote-only run that silently came back with office jobs would be a wrong answer sold as a right one.

### Use Advanced LinkedIn Jobs Scraper with AI agents and MCP

This Actor works as a tool for AI agents. Add it to Claude, ChatGPT, Cursor or any other MCP client through the Apify MCP server:

```
https://mcp.apify.com?tools=b2bsearch/linkedin-jobs-scraper
```

The same Actor is a tool in LangChain, CrewAI and the OpenAI Agents SDK, and a step in n8n, Make and Zapier through the Apify integrations.

#### What an agent should know before calling it

- **When to pick it.** The user wants LinkedIn job postings by job title, location or company, in bulk, with the employer's details and the recruiter. For a database-style search with description keywords, employer size and expired postings, pick [LinkedIn Jobs Database API](https://apify.com/b2bsearch/linkedin-job-search-api) instead. For the list of companies hiring, pick [Companies Hiring Now](https://apify.com/b2bsearch/hiring-companies-finder).
- **Minimal input.** `jobTitles` (array) plus `locations` (array with at least one country), or `company` (array). Set `maxItems` to what the user needs; the default is 100.
- **Count first.** Send `previewOnly: true` to get the number of matches and their breakdown for free, then decide how many rows to buy.
- **Cost.** $0.0008 per `found` job row; rows with any other `_status` are free. Cap a call with `maxTotalChargeUsd` in the run options.
- **Read the rows.** `_status` is `found` for a job. A row that is not a result says why in `_status` and `_error`. The useful columns are flat: `title`, `companyName`, `companyDomain`, `locationText`, `postedAt`, `seniorityLevel`, `employmentTypeName`, `salaryText`, `applicantRange`, `recruiterName`, `recruiterUrl`, `url`.
- **Small rows.** `compact: true` keeps every plain column and cuts the description to 300 characters. `descriptionType: "none"` drops it.
- **Fast.** A small request finishes in seconds, inside one tool call.

Input an agent can send as is:

```json
{
  "jobTitles": ["software engineer"],
  "locations": ["United States"],
  "postedLimit": "week",
  "maxItems": 25,
  "compact": true
}
```

#### Example prompts

```text
Use b2bsearch/linkedin-jobs-scraper. Count the data engineer jobs posted in Germany in the last week and show the breakdown by seniority and city (preview only), then fetch the 30 newest with their salaries.
```

```text
Use b2bsearch/linkedin-jobs-scraper with company = ["stripe.com", "shopify.com", "atlassian.com"] and postedLimit = "24h". List what each company posted today and who the recruiter is.
```

```text
Use b2bsearch/linkedin-jobs-scraper: account executive jobs in the United Kingdom from the last month, excludeAgencies true, employerHiring true, 100 rows. Rank the employers by how many roles they have open.
```

```text
Use b2bsearch/linkedin-jobs-scraper: "head of talent" OR "talent acquisition lead" in the United States, last week, decisionMakers true with decisionMakerRoles ["founder", "cxo"]. Give me the job, the company and the two people to contact for each.
```

### FAQ

#### How fresh are the jobs?

The database holds the last 180 days of postings and is reloaded after each data update, not on a clock. Every row says what the data runs to: `_input.dataAsOf` is the date of the newest posting in the database at the time of your run, and the preview's note repeats it. A job posted in the last hours may not be there yet. Use `postedLimit` or `postedLimitDate` on a schedule to collect what is new since your last run.

#### Is this a scraper or an API?

Both, in the way that matters: it returns the data a scraper would, through an Actor you can call as an API, from a database we fill and refresh ourselves. Nothing runs against LinkedIn when you press Start, which is why it is fast, needs no cookies and charges nothing for a miss.

#### Can I filter remote or hybrid jobs?

Not yet: the data carries no remote / hybrid flag, so `workplaceType` is refused rather than faked. A title search for `remote` finds the postings that say so in the title: about 1 in 100 US postings of the last month (27,000 of 2.5 million on 26 October 2026), so treat it as a sample, not as the remote market.

#### What does the employer's hiring pace tell me?

`employerHiring: true` puts the employer's signal on each job row: how many postings it has open now, how many it posted in the last 7, 30 and 90 days, when it last posted and which titles it posts most. An employer with 40 open roles and 20 posted this month is scaling; one with a single posting is filling a seat. It is free and comes from the same database.

#### Are there duplicate jobs?

Each posting is one row with its own `jobId`, delivered and charged once per run even when it matches several of your titles. An employer that posts the same role in several cities has several postings, as on the source; deduplicate on `title` + `companyId` for one row per role.

#### Does every job have a salary?

No. About one posting in seven states one (half in the Netherlands, two in five in China, one in seven in the US). `salaryOnly` keeps just those; `salary` ranges compare the stated pay in the posting's own currency.

#### Does every job name a recruiter?

About one in nine, and it depends on the country: 28% in India, 17% in the United Kingdom, 7% in the United States. The card is free on every such row; `recruiter: "profile"` adds the full career profile for $3.20 per 1,000 jobs that carry one.

#### Why is `maxItems` per run and not per title?

Because titles, locations and companies combine into one search: a job matching two of your titles is delivered and charged once, and `maxItems` is a spend cap you can read. Other scrapers run one search per title and location, bill the overlap twice and stop at 1,000 per search; here the walk continues to your cap, up to 10,000.

#### How do I get all jobs of a few companies?

Put the companies in `company` (page URLs, website domains, slugs or ids) and leave `jobTitles` empty. An employer alone is a complete search.

#### Am I charged when nothing is found?

No. Empty searches, unusable company entries and refused inputs are free rows with a `_status` and an explanation. Only `found` rows are charged.

#### Does it need cookies, a login or an API key?

No. The Actor does not sign in to any website and asks for no credentials.

### Related Actors: which one should you use?

| Need | Actor |
|---|---|
| Jobs by title, location and company, in the input names of the best-known scraper | **Advanced LinkedIn Jobs Scraper** (this Actor) |
| Database-style job search: description keywords, exclusions, employer size, expired postings, decision makers | [LinkedIn Jobs Database API](https://apify.com/b2bsearch/linkedin-job-search-api) |
| The companies hiring for a role, with their hiring pace and decision makers | [Companies Hiring Now](https://apify.com/b2bsearch/hiring-companies-finder) |
| Company domain → decision makers | [Decision Maker Email Finder by Domain](https://apify.com/b2bsearch/domain-to-decision-makers) |
| Company domain → current employees | [Company Employees](https://apify.com/b2bsearch/company-employees) |
| LinkedIn URL → full profile | [Profile Lookup](https://apify.com/b2bsearch/profile-lookup) |
| LinkedIn URL → email addresses on record | [LinkedIn Email Finder](https://apify.com/b2bsearch/linkedin-email-finder) |
| Filters → list of people | [People Database Search](https://apify.com/b2bsearch/people-database-search) |
| Filters → leads with an email | [B2B Leads Finder](https://apify.com/b2bsearch/b2b-leads-finder) |
| Filters → list of companies | [Company Database Search](https://apify.com/b2bsearch/company-database-search) |

### Free Apify plan

A run started from a free Apify plan delivers the first 100 results and stops;
the run's status line says so. Any paid Apify plan lifts the cap. The rows are
the same rows a paid run returns.

### Feedback and reviews

How did it go? A rating in the **Reviews** tab of this Actor tells the next
buyer what to expect; we read every word, good or bad. Got a wrong or stale
row? Open the **Issues** tab with the run ID. Issues are public, so leave out
emails, phones and names; the run ID is enough for us to find the row. We
check every report, fix what is ours to fix and answer in the thread.

### Disclaimer and permitted use

**Independent product.** This Actor is an independent tool. It is not affiliated with, endorsed by, or sponsored by LinkedIn Corporation. LinkedIn® is a trademark of its owner. It is named here only to describe the kind of public job postings and profile links this Actor returns.

**No platform access.** This Actor does not log in to, crawl or collect pages from LinkedIn or any other service. Every answer comes from our own database of job postings and business-contact records. It never returns passwords, messages, or content from non-public accounts.

**Permitted use.** Business purposes only: job market research, recruiting, sales prospecting, CRM enrichment and building job feeds. You may not use this Actor or its output to:

- stalk, harass, threaten, dox or publicly expose any person;
- decide on anyone's eligibility for credit, employment, housing or insurance. This Actor is not a consumer reporting agency and its output is not a consumer report under the U.S. Fair Credit Reporting Act;
- send unsolicited bulk messages, or place autodialed or prerecorded calls or texts without the consent the law requires;
- profile people by health, religion, political opinion, sexual orientation, ethnicity or union membership;
- build or resell a contact database from the results.

**Your responsibilities.** You decide what to search for and what to do with the result. You are responsible for having a lawful basis for that use and for complying with the laws that apply to you, including GDPR and UK GDPR, ePrivacy and PECR, CCPA/CPRA, CAN-SPAM, and the TCPA and Do Not Call rules. Honor every opt-out you receive.

**Removal requests.** To have a person's data removed, open an issue on
this Actor with only the profile link. Nothing else is needed, and the
record is removed from every listing.

**Accuracy.** Records can be incomplete or out of date. Output is provided as is. Check before you rely on it.

# Actor input Schema

## `jobTitles` (type: `array`):

Job titles to look for, one per line; a job whose title contains any line is kept. Whole words, any case. A line may use boolean search: AND, OR, NOT, "exact phrase", parentheses (for example: (backend OR platform) engineer NOT intern). Up to 10 lines. Example values: software engineer, data engineer.

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

Where the job is: countries by name or two-letter code (United States, Germany, gb, UK), US states (California), cities (Berlin; Austin, Texas). Any of them. A search needs at least one country or a company; a city or a title alone is not enough. Up to 60.

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

How many jobs to deliver in this run, across all titles and locations together (default 100; 0 = up to 10,000). This is your spend cap: $0.80 per 1,000 jobs delivered. Free Apify plan: up to 100.

## `previewOnly` (type: `boolean`):

Returns how many jobs match and their breakdown by country, seniority, employment type, industry, top employers, titles and cities. No job rows, no charge. Run it first to size a search, then turn it off to get the jobs.

## `compact` (type: `boolean`):

Returns a short row: every plain column, long text (the description) cut to 300 characters, nested blocks left out. Made for AI agents and LLM pipelines with a context limit. Same price. Off by default.

## `company` (type: `array`):

Only jobs at these employers: company page URL, page slug, website domain (stripe.com), numeric id or a company name (matched as part of the name). A company list alone is a complete search. Up to 100.

## `postedLimit` (type: `string`):

Only jobs posted within this window, counted from the start of the run. The database keeps 180 days of postings and is reloaded after each data update; every row says what date the data runs to.

## `experienceLevel` (type: `array`):

Keep these seniority levels, as the posting states them. Any of them.

## `employmentType` (type: `array`):

Keep these employment types. Any of them.

## `industryIds` (type: `array`):

Only jobs in these industries, by name (Software Development, Hospitals and Health Care) or by numeric industry id (4 for Software Development). An industry covers its sub-industries. Up to 30.

## `salary` (type: `array`):

Yearly salary ranges, one per line: 100k+, 60k-80k or 100000-150000. Keeps postings whose stated pay reaches the lowest floor (and stays under the highest ceiling when every line has one). Postings without a stated salary never match: about one in seven states one.

## `salaryCurrency` (type: `string`):

Three-letter code the salary ranges are in (default USD). A posting is compared in its own currency.

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

Keep only postings that state a salary (about one in seven overall; half in the Netherlands, two in five in China).

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

Keep postings in the lowest applicant bucket, fewer than 25 applicants. The data keeps applicant counts in buckets of 25, so this is the closest honest answer to "under 10".

## `excludeAgencies` (type: `boolean`):

Drops postings by staffing and recruiting firms and job-board aggregators (by the posting's industry and by the employer's own card), and employers with far more postings than employees (at least 200 open, or 100 posted in 30 days, and more than two per employee). Off by default so the search reads like the source.

## `postedLimitDate` (type: `string`):

Only jobs posted on or after this date (YYYY-MM-DD). With "Posted in the last" too, the later of the two wins. Set it to your last run's date for a daily feed.

## `recruiter` (type: `string`):

"card" (default, free): name, headline and profile URL of the recruiter when the posting names one (about one posting in nine; 28% in India, 17% in the UK, 7% in the US). "profile": their full career profile, $3.20 per 1,000 jobs that carry one. "none": leave the recruiter out.

## `employerHiring` (type: `boolean`):

Adds the employer's hiring signal to each job row, free: open postings now, postings in the last 7, 30 and 90 days, the date of its latest posting and its top titles of the last 30 days (employerOpenJobs, employerJobs7d/30d/90d, employerLastPostedAt, employerTopTitles30d). Tells a company scaling a team from one that posted once.

## `decisionMakers` (type: `boolean`):

After each job, adds the founders, C-level, VPs and directors of its employer as separate rows (each employer once per run): name, title, seniority, headline, profile URL, location, and the email on record when the contact tier is on. $3.20 per 1,000 people delivered; employers with nobody on record are free.

## `decisionMakerRoles` (type: `array`):

Which levels count as a decision maker. Rows are ordered founder + CEO first, then other founders and CEOs, the rest of the C-level, VPs, directors. Default: all four.

## `decisionMakersPerCompany` (type: `integer`):

How many people to add per employer, in rank order (default 3, maximum 25). Your spend cap per employer: $0.0032 per person.

## `decisionMakerTitleKeywords` (type: `array`):

Keep only decision makers whose job title contains any of these words, for example sales, engineering, talent (3-64 characters each, up to 10).

## `descriptionType` (type: `string`):

What the description column carries: plain text and HTML (default), text only, HTML only, or none for small rows. Same price.

## `sortBy` (type: `string`):

Accepted for compatibility with other job scrapers' saved inputs; jobs are always delivered newest first.

## Actor input object example

```json
{
  "jobTitles": [
    "software engineer"
  ],
  "locations": [
    "United States"
  ],
  "maxItems": 25,
  "previewOnly": false,
  "compact": false,
  "postedLimit": "any",
  "salaryCurrency": "USD",
  "salaryOnly": false,
  "under10Applicants": false,
  "excludeAgencies": false,
  "recruiter": "card",
  "employerHiring": false,
  "decisionMakers": false,
  "decisionMakersPerCompany": 3,
  "descriptionType": "both"
}
```

# Actor output Schema

## `results` (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 = {
    "jobTitles": [
        "software engineer"
    ],
    "locations": [
        "United States"
    ],
    "maxItems": 25
};

// Run the Actor and wait for it to finish
const run = await client.actor("b2bsearch/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 = {
    "jobTitles": ["software engineer"],
    "locations": ["United States"],
    "maxItems": 25,
}

# Run the Actor and wait for it to finish
run = client.actor("b2bsearch/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 '{
  "jobTitles": [
    "software engineer"
  ],
  "locations": [
    "United States"
  ],
  "maxItems": 25
}' |
apify call b2bsearch/linkedin-jobs-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,b2bsearch/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/9AGnupyYLvDNThxIi/builds/G6gUtk3zedaAJ0cxa/openapi.json
