# LinkedIn Jobs Scraper Pro (`yesh_boi/linkedin-jobs-scraper-pro`) Actor

Paste any LinkedIn job-search URL (with filters) and get every listing with full job details, deep company data, hiring-team profiles, salary, skills, applicant insights, and official-source contact enrichment. Deep pagination captures 100% of results.

- **URL**: https://apify.com/yesh\_boi/linkedin-jobs-scraper-pro.md
- **Developed by:** [YESHWANT SINGH](https://apify.com/yesh_boi) (community)
- **Categories:** AI, Automation, Jobs
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $25.00 / 1,000 results

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.

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

## What's an Apify Actor?

Actors are a software tools running on the Apify platform, for all kinds of web data extraction and automation use cases.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

In JavaScript/TypeScript projects, use official [JavaScript/TypeScript client](https://docs.apify.com/api/client/js/docs.md):

```bash
npm install apify-client
```

In Python projects, use official [Python client library](https://docs.apify.com/api/client/python/docs.md):

```bash
pip install apify-client
```

In shell scripts, use [Apify CLI](https://docs.apify.com/cli/docs.md):

````bash
# MacOS / Linux
curl -fsSL https://apify.com/install-cli.sh | bash
# Windows
irm https://apify.com/install-cli.ps1 | iex
```bash

In AI frameworks, you might use the [Apify MCP server](https://docs.apify.com/integrations/mcp.md).

If your project is in a different language, use the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).


# README

## LinkedIn Jobs Scraper Pro 🚀

**Paste any LinkedIn job-search URL → get every listing, with full job + company + hiring-team data and official-source contact enrichment. Nothing missed, built to be reliable.**

This Actor was built from scratch to out-feature every LinkedIn job scraper in the Apify Store. It is **HTTP-first** against LinkedIn's public guest endpoints (fast, cheap, hard to block), adds an **optional logged-in layer** for data only visible when authenticated, and finishes with **official-source contact enrichment** for recruiter email and company phone.

---

### ✨ Why this one is different

| Capability | Most scrapers | **This Actor** |
|---|---|---|
| Paste a search URL *with all your filters* | Partial | ✅ Full — `search/`, `search-results/`, `collections/` |
| Capture **100%** of results past LinkedIn's ~1,000 cap | ❌ silently truncates | ✅ **Deep pagination** auto-splits by facet |
| Company profile (website, industry, size, HQ, followers) | Sometimes | ✅ Full, **cached per company** |
| Hiring team / job poster + their LinkedIn profile | Rare | ✅ With logged-in session |
| Applicant insights & skills | Rare | ✅ With logged-in session |
| Recruiter **email** + company **phone** | ❌ or fabricated | ✅ **Official sources only**, never guessed |
| Salary parsed into min/max/currency/period | Rare | ✅ + raw text |
| Reliability (residential proxy, session pool, retries) | Varies | ✅ Tuned for LinkedIn |

---

### 🧭 What it scrapes

**Job**: id · title (role) · canonical job URL · **company apply URL** (offsite/ATS) · **careers-portal URL** · **ATS name** (Greenhouse, Lever, Workday…) · Easy-Apply flag · posted date + "time ago" · expiry · location · workplace type (on-site/remote/hybrid) · **seniority / job level** (Entry, Associate, Mid-Senior, Director, Executive…) · employment type · **job function** · **industries** · full description (text, HTML, optional Markdown) · **skills** · **applicant insights** · **salary** (raw + min/max/**location-aware currency**/period).

**Company**: name · LinkedIn URL · **official website** · tagline · description · **industry** · size · employee count · headquarters · followers · type · specialties · logo · **phone** · public inbox.

**Contacts & hiring team**: recruiter **email** + **phone** for the role (from the description, or provider enrichment) · `descriptionEmails` / `descriptionPhones` · and, when logged in, the hiring team's name · headline · **LinkedIn profile URL** plus the derived `recruiterName` / `recruiterProfileUrl`.

See the full field list in [Output](#-output).

---

### 🏁 Quick start

1. Filter jobs on LinkedIn exactly how you want them.
2. Copy the browser URL (it contains all your filters).
3. Paste it into **`searchUrls`** and run.

```json
{
  "searchUrls": [
    "https://www.linkedin.com/jobs/search-results/?keywords=Data%20Analyst&geoId=101452733&f_TPR=r86400"
  ],
  "maxItems": 0,
  "deepPagination": true,
  "scrapeCompanyDetails": true,
  "proxyConfiguration": { "useApifyProxy": true, "apifyProxyGroups": ["RESIDENTIAL"] }
}
````

Prefer not to build a URL? Use the structured fields instead:

```json
{
  "keywords": "Data Analyst",
  "country": "au",
  "datePosted": "past24h",
  "experienceLevels": ["entry", "associate"],
  "workplaceTypes": ["remote", "hybrid"]
}
```

`country: "au"` targets **all of Australia** by its LinkedIn `geoId` — more reliable than free text. Ten countries are built in; use `country: "custom"` + `geoId` for any other region.

***

### 🎛️ Input reference (highlights)

| Field | Type | Notes |
|---|---|---|
| `searchUrls` | string\[] | One or more LinkedIn search URLs. **Recommended.** |
| `keywords`, `location`, `country`, `geoId` | string | Build a search without a URL. |
| `datePosted` | enum | `any` / `past24h` / `pastWeek` / `pastMonth`. |
| `experienceLevels`, `jobTypes`, `workplaceTypes` | string\[] | Facet filters (used when building from keywords). |
| `maxItems` | int | `0` = scrape everything. |
| `deepPagination` | bool | Auto-split to break the ~1,000 cap. Default **on**. |
| `scrapeCompanyDetails` | bool | Fetch each company page (cached). Default on. |
| `useLoginSession` | bool | Unlock hiring team + applicant insights. See below. |
| `liAtCookie`, `jsessionidCookie` | secret | Your LinkedIn session cookies. |
| `enrichContacts` | bool | Fill recruiter email + company phone. |
| `contactProvider` | enum | `none` / `hunter` / `apollo`. |
| `hunterApiKey`, `apolloApiKey` | secret | Provider keys (bring your own). |
| `proxyConfiguration` | proxy | **RESIDENTIAL** strongly recommended. |
| `maxConcurrency`, `minDelayMs`, `maxRequestRetries` | int | Reliability tuning. |

***

### 🧩 Deep pagination — capturing *everything*

LinkedIn's guest search returns **at most ~1,000 results per query** (40 pages × 25) and gives no total count. Naïve scrapers just stop there and silently miss the rest.

When `deepPagination` is on and a search hits the cap, this Actor automatically **partitions** the query by the next unused discrete facet — first by the six **experience levels**, then by **job type**, then by **workplace type**. Every job belongs to exactly one bucket, results are **deduplicated by job id**, and the union is complete. If a fully-faceted slice still exceeds 1,000, the Actor logs a clear warning telling you to add one more filter (e.g. a tighter date window or a sub-region).

***

### 🔐 Logged-in session (optional) — hiring team, applicants, skills

Some fields are **only** visible to a signed-in LinkedIn user: the *"Meet the hiring team"* poster and their profile, applicant insights, and skill data. Provide session cookies to unlock them:

1. Log in to LinkedIn in Chrome (use a **dedicated account** — see the warning below).
2. Open **DevTools → Application → Cookies → `https://www.linkedin.com`**.
3. Copy the value of **`li_at`** into `liAtCookie`.
4. Copy the value of **`JSESSIONID`** (looks like `"ajax:1234567890"`) into `jsessionidCookie` — keep the quotes.
5. Set `useLoginSession: true`.

Both are stored as encrypted secrets. On startup the Actor launches a **real headless Chrome**, applies your cookies, and logs `🔓 LinkedIn login OK — signed in as <you>` or a clear `🔒 login FAILED` — so you instantly know whether the session works (stale cookies are the #1 cause of empty hiring-team fields). For each job it then opens the logged-in job page and reads the **"Meet the hiring team"** card (name + LinkedIn profile URL) and the **true external apply URL** that the public page hides behind a login wall.

Because logged-in mode drives a browser it's slower and needs more memory — **run it with 2 GB+ RAM and a Residential proxy** for best results. (`linkedinHiringTeamQueryId` is an optional advanced fallback for the API path and can be left blank.)

> 💡 **You often don't even need login for the recruiter's contact.** Many postings — especially recruitment-agency listings — put the recruiter's email and phone *in the description text*. The Actor extracts those automatically into `recruiterEmail` / `recruiterPhone` (source `job-description`). See Contact enrichment below.

> ⚠️ **Account risk.** Scraping while authenticated can violate LinkedIn's [User Agreement](https://www.linkedin.com/legal/user-agreement) and may get the account restricted. Use a throwaway account you can afford to lose, and keep concurrency/`minDelayMs` gentle. Leave `useLoginSession` off for the safe, public-only crawl.

***

### 📧 Contact enrichment — email & phone, **official sources only**

Recruiter email and company phone are **not published in LinkedIn's structured data**, so this Actor sources them the legitimate way, in priority order, and **leaves the field empty if nothing genuine is found** (no guessed patterns, no grey-market lists):

1. **The job description itself** *(first-party, always legitimate, no key)* — recruiters frequently write their email/phone into the posting ("send your resume to …", "call me on …"). The Actor pulls these into `recruiterEmail` / `recruiterPhone` (source `job-description`) plus full `descriptionEmails` / `descriptionPhones` arrays. This is the most reliable source of a **role-specific** contact.
2. **The company's own website** *(first-party, always legitimate)* — reads `tel:` / `mailto:` links and `schema.org` `Organization` data (`telephone`, `email`, `contactPoint`) that the company itself published. No key required; enabled by `scrapeCompanyWebsiteForContacts`.
3. **A reputable provider you opt into** *(bring your own key)*:
   - **Hunter.io** — Email Finder returns a recruiter's professional email + confidence score from their name and the company domain. [Docs](https://hunter.io/api-documentation).
   - **Apollo.io** — organization enrichment for a verified business **phone**, and people-match for email. [Docs](https://docs.apollo.io/).

Set `enrichContacts: true`, pick `contactProvider`, and paste the matching API key. Enriched fields carry a `recruiterEmailSource` / confidence so you always know where a value came from.

***

### 📤 Output

One dataset item per job (undefined fields are omitted):

```jsonc
{
  "jobId": "4436678938",
  "title": "Senior Data Analyst",
  "jobUrl": "https://www.linkedin.com/jobs/view/4436678938/",
  "applyUrl": "https://boards.greenhouse.io/acme/jobs/12345",
  "externalApplyUrl": "https://boards.greenhouse.io/acme/jobs/12345",
  "companyApplyUrl": "https://boards.greenhouse.io/acme/jobs/12345",
  "companyCareersUrl": "https://boards.greenhouse.io/",
  "atsName": "Greenhouse",
  "applyIsOffsite": true,
  "easyApply": false,
  "postedDate": "2026-07-12",
  "postedTimeAgo": "1 day ago",
  "expiresAt": "2026-08-12",
  "location": "Sydney, New South Wales, Australia",
  "workplaceType": "Hybrid",
  "seniorityLevel": "Mid-Senior level",
  "employmentType": "Full-time",
  "jobFunction": "Analyst",
  "industries": "IT Services and IT Consulting",
  "descriptionText": "…",
  "descriptionHtml": "…",
  "skills": ["SQL", "Python", "Power BI"],
  "applicantsCount": 200,
  "applicantInsights": "Over 200 applicants",
  "salaryText": "A$90,000 – A$110,000/yr",
  "salaryMin": 90000, "salaryMax": 110000, "salaryCurrency": "AUD", "salaryPeriod": "year",
  "companyName": "Acme Corp",
  "companyLinkedinUrl": "https://www.linkedin.com/company/acme-corp/",
  "companyWebsite": "https://www.acme-corp.com",
  "companyIndustry": "Information Technology",
  "companySize": "1,001-5,000 employees",
  "companyHeadquarters": "Sydney, NSW",
  "companyFollowers": 48213,
  "companyPhone": "+61255501234",
  "hiringTeam": [
    { "name": "Jane Recruiter", "headline": "Talent Acquisition @ Acme", "profileUrl": "https://www.linkedin.com/in/jane-recruiter/" }
  ],
  "recruiterName": "Jane Recruiter",
  "recruiterProfileUrl": "https://www.linkedin.com/in/jane-recruiter/",
  "recruiterEmail": "jane@acme-corp.com",
  "recruiterEmailSource": "hunter.io",
  "recruiterEmailConfidence": 94,
  "recruiterPhone": "61283195529",
  "recruiterPhoneSource": "job-description",
  "descriptionEmails": ["jane@acme-corp.com"],
  "descriptionPhones": ["61283195529"],
  "searchUrl": "https://www.linkedin.com/jobs/search-results/?keywords=Data%20Analyst&f_TPR=r86400",
  "dataSource": "guest+loggedIn",
  "scrapedAt": "2026-07-13T02:20:00.000Z"
}
```

A `SUMMARY` record (searches started, jobs enqueued/pushed, companies resolved) is written to the default key-value store at the end of every run.

***

### 🛠️ Deploy it (you push it yourself)

You need the **[Apify CLI](https://docs.apify.com/cli/)** and a free Apify account.

```bash
npm install -g apify-cli      # 1. install the CLI
apify login                   # 2. authenticate (paste your API token)

cd linkedin-jobs-scraper      # 3. from this project folder
apify push                    # 4. build & upload to your Apify account
```

`apify push` builds the Docker image on the platform and creates the Actor under your account. Open it in the [Apify Console](https://console.apify.com/actors), run it from the auto-generated input form, and check the **Dataset** tab for results.

Run it locally first if you like:

```bash
npm install
npm run build
apify run -p        # runs with your input.json against Apify proxy
```

**Publish to the Store** (to make it famous 🌟): in the Console open your Actor → **Publication** → set a title, description, categories, and pricing → **Publish**. Full guide: <https://docs.apify.com/platform/actors/publishing>.

> **Memory for logged-in mode.** The Actor ships on the Playwright + Chromium base image, so the logged-in browser layer works out of the box — no Dockerfile changes needed. When you enable the logged-in session, run it with **2 GB+ memory** (choose it in the Console before Start). The public-only crawl runs fine on 512 MB–1 GB.

***

### 📁 Project layout

```
.actor/
  actor.json            # Actor metadata
  input_schema.json     # 30 typed inputs (4 secrets)
  dataset_schema.json   # dataset views (overview + contacts)
  Dockerfile            # HTTP-first build (Playwright note inside)
src/
  main.ts               # entrypoint: input → config → crawler
  router.ts             # SEARCH + JOB handlers, pagination, deep-split
  constants.ts          # endpoints, filter maps, geoIds
  types.ts              # typed input / records
  parsers/              # urlParser, guestList, guestDetail, companyParser
  linkedin/voyager.ts   # logged-in enrichment (best-effort)
  enrichment/           # companySite, hunter, apollo, orchestrator
  utils/text.ts         # cleaning, salary parsing, helpers
smoke-test.mjs          # offline test of the parsing logic (npm run build first)
```

Run the offline tests any time: `npm run build && node smoke-test.mjs`.

***

### 🧯 Reliability & troubleshooting

- **Blocking / few results** → use **RESIDENTIAL** proxy, lower `maxConcurrency`, raise `minDelayMs`. Datacenter IPs get blocked fast.
- **Missing hiring team / applicants** → those need `useLoginSession` with valid, fresh cookies.
- **Empty email/phone** → nothing legitimate was published or matched; that is by design.
- **Stopped at ~1,000** → make sure `deepPagination` is on, or add a tighter filter.
- The crawler auto-retires blocked sessions and retries with a new proxy up to `maxRequestRetries`.

***

### ⚖️ Compliance

This Actor is intended for lawful use on publicly available data. Scraping LinkedIn — especially while logged in — may conflict with LinkedIn's Terms of Service. You are responsible for how you use it, for respecting rate limits and robots directives, and for handling any personal data (recruiter names, profiles, enriched emails/phones) in line with applicable laws such as GDPR/CCPA. Enrichment uses official, opt-in provider APIs and first-party company data only.

# Actor input Schema

## `searchUrls` (type: `array`):

Paste any LinkedIn job-search URL — keep all your filters in it. Works with /jobs/search/, /jobs/search-results/ and /jobs/collections/ URLs. This is the recommended way to run the Actor: filter on LinkedIn, then paste the URL here. You can add several URLs and they will all be scraped.

## `keywords` (type: `string`):

Job title / keywords to search for. Used only when no search URL is supplied above.

## `location` (type: `string`):

Location name as you would type it on LinkedIn, e.g. "Australia", "Sydney, New South Wales, Australia", "Remote". Used when building a search from keywords.

## `country` (type: `string`):

Optional convenience selector that targets an entire country by its LinkedIn geoId — more reliable than free text. Overrides the free-text location when set. Choose "Custom geoId" to enter your own below.

## `geoId` (type: `string`):

LinkedIn geoId for precise region targeting. Find it in a LinkedIn search URL after filtering by a place. Example: Australia = 101452733. Used when Region/Country is set to "Custom geoId".

## `datePosted` (type: `string`):

Restrict to recently posted jobs. Applied only when building a search from keywords; if your pasted URL already has an f\_TPR filter, that is respected.

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

Filter by seniority. Applied when building a search from keywords.

## `jobTypes` (type: `array`):

Filter by employment type. Applied when building a search from keywords.

## `workplaceTypes` (type: `array`):

Filter by on-site / remote / hybrid. Applied when building a search from keywords.

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

Result ordering when building a search from keywords.

## `easyApplyOnly` (type: `boolean`):

Only include jobs with LinkedIn Easy Apply. Applied when building a search from keywords.

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

Hard cap on the number of jobs scraped across all search URLs. Set 0 to scrape every result (recommended with Deep pagination on). Prefilled to 10 for a fast first run — raise or set 0 for full runs.

## `deepPagination` (type: `boolean`):

LinkedIn's guest search returns at most ~1000 results per query. When on, the Actor automatically slices any search that hits the cap into smaller posting-date windows so no listing is missed. This is the feature that lets it scrape *everything*.

## `scrapeJobDetails` (type: `boolean`):

Fetch each job's detail page for description, seniority, employment type, job function, industries, applicant count, salary and skills. Turn off for a fast listing-only crawl.

## `scrapeCompanyDetails` (type: `boolean`):

Also fetch each company's page for official website, industry, size, headquarters, followers and specialties. Companies are cached so each is fetched only once.

## `includeDescriptionHtml` (type: `boolean`):

Include the raw HTML of the job description in addition to clean text.

## `includeDescriptionMarkdown` (type: `boolean`):

Also include a Markdown version of the job description.

## `useLoginSession` (type: `boolean`):

When on, the Actor uses your LinkedIn session cookies to fetch data that is ONLY visible when logged in: the hiring team / job poster and their profile, applicant insights, and skill-match details. ⚠️ Scraping while authenticated can violate LinkedIn's Terms of Service and carries account risk — use a dedicated account. Leave off for the safe, public-only crawl.

## `liAtCookie` (type: `string`):

Value of the `li_at` cookie from a logged-in LinkedIn session. Stored as a secret. Only used when 'Use logged-in session' is on.

## `jsessionidCookie` (type: `string`):

Value of the `JSESSIONID` cookie (used as the CSRF token for LinkedIn's internal API). Include the surrounding quotes exactly as shown in the cookie. Stored as a secret.

## `linkedinHiringTeamQueryId` (type: `string`):

Optional but recommended for hiring-team data. LinkedIn's internal GraphQL query id for the 'Meet the hiring team' card (looks like `voyagerJobsDashJobPostingDetailSections.<hash>`). Because LinkedIn rotates it, you paste a current value: open a job while logged in, DevTools → Network → filter `graphql`, and copy the `queryId` from the hiring-team request. See the README → Hiring team.

## `enrichContacts` (type: `boolean`):

Fill recruiter email and company phone from OFFICIAL sources only: the company's own website first (published tel:/mailto:/schema.org data), then a reputable provider you supply a key for. If nothing legitimate is found for a job, the fields are left empty — no guessed or scraped-list data.

## `scrapeCompanyWebsiteForContacts` (type: `boolean`):

First-party, fully legitimate: read tel:, mailto: and schema.org Organization data published on the company's own website. No third-party key needed.

## `contactProvider` (type: `string`):

Reputable enrichment provider used as a fallback when the company website has nothing. Requires the matching API key below.

## `hunterApiKey` (type: `string`):

API key from hunter.io. Used to find a recruiter's professional email from their name + the company domain. Stored as a secret.

## `apolloApiKey` (type: `string`):

API key from apollo.io. Used to enrich company phone and, optionally, recruiter email. Stored as a secret.

## `renderJs` (type: `boolean`):

Use a real browser (Playwright) for pages that need JS rendering. Requires deploying with the Playwright base image (see Dockerfile comment). Off by default because the guest endpoints are static HTML.

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

Maximum number of parallel requests. Lower it if you see blocking; raise it for speed on residential proxies.

## `minDelayMs` (type: `integer`):

Politeness delay applied per session to look human and avoid rate limits.

## `maxRequestRetries` (type: `integer`):

How many times to retry a blocked or failed request (with a fresh proxy/session) before giving up on it.

## `proxyConfiguration` (type: `object`):

Residential proxies are strongly recommended for LinkedIn. Datacenter proxies get blocked quickly.

## Actor input object example

```json
{
  "searchUrls": [
    "https://www.linkedin.com/jobs/search/?keywords=Data%20Analyst&geoId=101452733&f_TPR=r86400"
  ],
  "keywords": "Data Analyst",
  "location": "Australia",
  "country": "",
  "datePosted": "any",
  "sortBy": "date",
  "easyApplyOnly": false,
  "maxItems": 10,
  "deepPagination": true,
  "scrapeJobDetails": true,
  "scrapeCompanyDetails": true,
  "includeDescriptionHtml": true,
  "includeDescriptionMarkdown": false,
  "useLoginSession": false,
  "enrichContacts": false,
  "scrapeCompanyWebsiteForContacts": true,
  "contactProvider": "none",
  "renderJs": false,
  "maxConcurrency": 8,
  "minDelayMs": 400,
  "maxRequestRetries": 8,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}
```

# 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 = {
    "searchUrls": [
        "https://www.linkedin.com/jobs/search-results/?keywords=Data%20Analyst&f_TPR=r86400"
    ],
    "maxItems": 10
};

// Run the Actor and wait for it to finish
const run = await client.actor("yesh_boi/linkedin-jobs-scraper-pro").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 = {
    "searchUrls": ["https://www.linkedin.com/jobs/search-results/?keywords=Data%20Analyst&f_TPR=r86400"],
    "maxItems": 10,
}

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

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

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

```

## CLI example

```bash
echo '{
  "searchUrls": [
    "https://www.linkedin.com/jobs/search-results/?keywords=Data%20Analyst&f_TPR=r86400"
  ],
  "maxItems": 10
}' |
apify call yesh_boi/linkedin-jobs-scraper-pro --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=yesh_boi/linkedin-jobs-scraper-pro",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

```json
{
    "openapi": "3.0.1",
    "info": {
        "title": "LinkedIn Jobs Scraper Pro",
        "description": "Paste any LinkedIn job-search URL (with filters) and get every listing with full job details, deep company data, hiring-team profiles, salary, skills, applicant insights, and official-source contact enrichment. Deep pagination captures 100% of results.",
        "version": "1.0",
        "x-build-id": "fGC4UK8LXgvcdQTj9"
    },
    "servers": [
        {
            "url": "https://api.apify.com/v2"
        }
    ],
    "paths": {
        "/acts/yesh_boi~linkedin-jobs-scraper-pro/run-sync-get-dataset-items": {
            "post": {
                "operationId": "run-sync-get-dataset-items-yesh_boi-linkedin-jobs-scraper-pro",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for its completion, and returns Actor's dataset items in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        },
        "/acts/yesh_boi~linkedin-jobs-scraper-pro/runs": {
            "post": {
                "operationId": "runs-sync-yesh_boi-linkedin-jobs-scraper-pro",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor and returns information about the initiated run in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/runsResponseSchema"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/acts/yesh_boi~linkedin-jobs-scraper-pro/run-sync": {
            "post": {
                "operationId": "run-sync-yesh_boi-linkedin-jobs-scraper-pro",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for completion, and returns the OUTPUT from Key-value store in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        }
    },
    "components": {
        "schemas": {
            "inputSchema": {
                "type": "object",
                "properties": {
                    "searchUrls": {
                        "title": "🔗 LinkedIn job-search URLs",
                        "type": "array",
                        "description": "Paste any LinkedIn job-search URL — keep all your filters in it. Works with /jobs/search/, /jobs/search-results/ and /jobs/collections/ URLs. This is the recommended way to run the Actor: filter on LinkedIn, then paste the URL here. You can add several URLs and they will all be scraped.",
                        "items": {
                            "type": "string"
                        }
                    },
                    "keywords": {
                        "title": "🔎 Keywords",
                        "type": "string",
                        "description": "Job title / keywords to search for. Used only when no search URL is supplied above."
                    },
                    "location": {
                        "title": "📍 Location (free text)",
                        "type": "string",
                        "description": "Location name as you would type it on LinkedIn, e.g. \"Australia\", \"Sydney, New South Wales, Australia\", \"Remote\". Used when building a search from keywords."
                    },
                    "country": {
                        "title": "🌏 Region / Country (precise)",
                        "enum": [
                            "",
                            "au",
                            "us",
                            "gb",
                            "in",
                            "ca",
                            "de",
                            "fr",
                            "nl",
                            "sg",
                            "ae",
                            "custom"
                        ],
                        "type": "string",
                        "description": "Optional convenience selector that targets an entire country by its LinkedIn geoId — more reliable than free text. Overrides the free-text location when set. Choose \"Custom geoId\" to enter your own below.",
                        "default": ""
                    },
                    "geoId": {
                        "title": "🌐 Custom geoId",
                        "type": "string",
                        "description": "LinkedIn geoId for precise region targeting. Find it in a LinkedIn search URL after filtering by a place. Example: Australia = 101452733. Used when Region/Country is set to \"Custom geoId\"."
                    },
                    "datePosted": {
                        "title": "🗓️ Date posted",
                        "enum": [
                            "any",
                            "past24h",
                            "pastWeek",
                            "pastMonth"
                        ],
                        "type": "string",
                        "description": "Restrict to recently posted jobs. Applied only when building a search from keywords; if your pasted URL already has an f_TPR filter, that is respected.",
                        "default": "any"
                    },
                    "experienceLevels": {
                        "title": "🎯 Experience levels",
                        "type": "array",
                        "description": "Filter by seniority. Applied when building a search from keywords.",
                        "items": {
                            "type": "string",
                            "enum": [
                                "internship",
                                "entry",
                                "associate",
                                "midSenior",
                                "director",
                                "executive"
                            ],
                            "enumTitles": [
                                "Internship",
                                "Entry level",
                                "Associate",
                                "Mid-Senior level",
                                "Director",
                                "Executive"
                            ]
                        }
                    },
                    "jobTypes": {
                        "title": "💼 Job types",
                        "type": "array",
                        "description": "Filter by employment type. Applied when building a search from keywords.",
                        "items": {
                            "type": "string",
                            "enum": [
                                "fullTime",
                                "partTime",
                                "contract",
                                "temporary",
                                "internship",
                                "volunteer",
                                "other"
                            ],
                            "enumTitles": [
                                "Full-time",
                                "Part-time",
                                "Contract",
                                "Temporary",
                                "Internship",
                                "Volunteer",
                                "Other"
                            ]
                        }
                    },
                    "workplaceTypes": {
                        "title": "🏢 Workplace types",
                        "type": "array",
                        "description": "Filter by on-site / remote / hybrid. Applied when building a search from keywords.",
                        "items": {
                            "type": "string",
                            "enum": [
                                "onSite",
                                "remote",
                                "hybrid"
                            ],
                            "enumTitles": [
                                "On-site",
                                "Remote",
                                "Hybrid"
                            ]
                        }
                    },
                    "sortBy": {
                        "title": "↕️ Sort by",
                        "enum": [
                            "date",
                            "relevance"
                        ],
                        "type": "string",
                        "description": "Result ordering when building a search from keywords.",
                        "default": "date"
                    },
                    "easyApplyOnly": {
                        "title": "⚡ Easy Apply only",
                        "type": "boolean",
                        "description": "Only include jobs with LinkedIn Easy Apply. Applied when building a search from keywords.",
                        "default": false
                    },
                    "maxItems": {
                        "title": "🔢 Max jobs (0 = all)",
                        "minimum": 0,
                        "type": "integer",
                        "description": "Hard cap on the number of jobs scraped across all search URLs. Set 0 to scrape every result (recommended with Deep pagination on). Prefilled to 10 for a fast first run — raise or set 0 for full runs.",
                        "default": 0
                    },
                    "deepPagination": {
                        "title": "🧭 Deep pagination (capture 100%)",
                        "type": "boolean",
                        "description": "LinkedIn's guest search returns at most ~1000 results per query. When on, the Actor automatically slices any search that hits the cap into smaller posting-date windows so no listing is missed. This is the feature that lets it scrape *everything*.",
                        "default": true
                    },
                    "scrapeJobDetails": {
                        "title": "📄 Scrape full job details",
                        "type": "boolean",
                        "description": "Fetch each job's detail page for description, seniority, employment type, job function, industries, applicant count, salary and skills. Turn off for a fast listing-only crawl.",
                        "default": true
                    },
                    "scrapeCompanyDetails": {
                        "title": "🏦 Scrape company details",
                        "type": "boolean",
                        "description": "Also fetch each company's page for official website, industry, size, headquarters, followers and specialties. Companies are cached so each is fetched only once.",
                        "default": true
                    },
                    "includeDescriptionHtml": {
                        "title": "🧩 Include description HTML",
                        "type": "boolean",
                        "description": "Include the raw HTML of the job description in addition to clean text.",
                        "default": true
                    },
                    "includeDescriptionMarkdown": {
                        "title": "📝 Include description Markdown",
                        "type": "boolean",
                        "description": "Also include a Markdown version of the job description.",
                        "default": false
                    },
                    "useLoginSession": {
                        "title": "🔐 Use logged-in session (richer data)",
                        "type": "boolean",
                        "description": "When on, the Actor uses your LinkedIn session cookies to fetch data that is ONLY visible when logged in: the hiring team / job poster and their profile, applicant insights, and skill-match details. ⚠️ Scraping while authenticated can violate LinkedIn's Terms of Service and carries account risk — use a dedicated account. Leave off for the safe, public-only crawl.",
                        "default": false
                    },
                    "liAtCookie": {
                        "title": "li_at cookie",
                        "type": "string",
                        "description": "Value of the `li_at` cookie from a logged-in LinkedIn session. Stored as a secret. Only used when 'Use logged-in session' is on."
                    },
                    "jsessionidCookie": {
                        "title": "JSESSIONID cookie",
                        "type": "string",
                        "description": "Value of the `JSESSIONID` cookie (used as the CSRF token for LinkedIn's internal API). Include the surrounding quotes exactly as shown in the cookie. Stored as a secret."
                    },
                    "linkedinHiringTeamQueryId": {
                        "title": "Hiring-team GraphQL queryId (advanced)",
                        "type": "string",
                        "description": "Optional but recommended for hiring-team data. LinkedIn's internal GraphQL query id for the 'Meet the hiring team' card (looks like `voyagerJobsDashJobPostingDetailSections.<hash>`). Because LinkedIn rotates it, you paste a current value: open a job while logged in, DevTools → Network → filter `graphql`, and copy the `queryId` from the hiring-team request. See the README → Hiring team."
                    },
                    "enrichContacts": {
                        "title": "📧 Enrich recruiter email & company phone",
                        "type": "boolean",
                        "description": "Fill recruiter email and company phone from OFFICIAL sources only: the company's own website first (published tel:/mailto:/schema.org data), then a reputable provider you supply a key for. If nothing legitimate is found for a job, the fields are left empty — no guessed or scraped-list data.",
                        "default": false
                    },
                    "scrapeCompanyWebsiteForContacts": {
                        "title": "Pull contacts from the company's official website",
                        "type": "boolean",
                        "description": "First-party, fully legitimate: read tel:, mailto: and schema.org Organization data published on the company's own website. No third-party key needed.",
                        "default": true
                    },
                    "contactProvider": {
                        "title": "Email/phone provider",
                        "enum": [
                            "none",
                            "hunter",
                            "apollo"
                        ],
                        "type": "string",
                        "description": "Reputable enrichment provider used as a fallback when the company website has nothing. Requires the matching API key below.",
                        "default": "none"
                    },
                    "hunterApiKey": {
                        "title": "Hunter.io API key",
                        "type": "string",
                        "description": "API key from hunter.io. Used to find a recruiter's professional email from their name + the company domain. Stored as a secret."
                    },
                    "apolloApiKey": {
                        "title": "Apollo.io API key",
                        "type": "string",
                        "description": "API key from apollo.io. Used to enrich company phone and, optionally, recruiter email. Stored as a secret."
                    },
                    "renderJs": {
                        "title": "🖥️ JavaScript-render fallback",
                        "type": "boolean",
                        "description": "Use a real browser (Playwright) for pages that need JS rendering. Requires deploying with the Playwright base image (see Dockerfile comment). Off by default because the guest endpoints are static HTML.",
                        "default": false
                    },
                    "maxConcurrency": {
                        "title": "Max concurrency",
                        "minimum": 1,
                        "maximum": 50,
                        "type": "integer",
                        "description": "Maximum number of parallel requests. Lower it if you see blocking; raise it for speed on residential proxies.",
                        "default": 8
                    },
                    "minDelayMs": {
                        "title": "Min delay between requests (ms)",
                        "minimum": 0,
                        "maximum": 10000,
                        "type": "integer",
                        "description": "Politeness delay applied per session to look human and avoid rate limits.",
                        "default": 400
                    },
                    "maxRequestRetries": {
                        "title": "Max request retries",
                        "minimum": 0,
                        "maximum": 30,
                        "type": "integer",
                        "description": "How many times to retry a blocked or failed request (with a fresh proxy/session) before giving up on it.",
                        "default": 8
                    },
                    "proxyConfiguration": {
                        "title": "Proxy configuration",
                        "type": "object",
                        "description": "Residential proxies are strongly recommended for LinkedIn. Datacenter proxies get blocked quickly.",
                        "default": {
                            "useApifyProxy": true,
                            "apifyProxyGroups": [
                                "RESIDENTIAL"
                            ]
                        }
                    }
                }
            },
            "runsResponseSchema": {
                "type": "object",
                "properties": {
                    "data": {
                        "type": "object",
                        "properties": {
                            "id": {
                                "type": "string"
                            },
                            "actId": {
                                "type": "string"
                            },
                            "userId": {
                                "type": "string"
                            },
                            "startedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "finishedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "status": {
                                "type": "string",
                                "example": "READY"
                            },
                            "meta": {
                                "type": "object",
                                "properties": {
                                    "origin": {
                                        "type": "string",
                                        "example": "API"
                                    },
                                    "userAgent": {
                                        "type": "string"
                                    }
                                }
                            },
                            "stats": {
                                "type": "object",
                                "properties": {
                                    "inputBodyLen": {
                                        "type": "integer",
                                        "example": 2000
                                    },
                                    "rebootCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "restartCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "resurrectCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "computeUnits": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "options": {
                                "type": "object",
                                "properties": {
                                    "build": {
                                        "type": "string",
                                        "example": "latest"
                                    },
                                    "timeoutSecs": {
                                        "type": "integer",
                                        "example": 300
                                    },
                                    "memoryMbytes": {
                                        "type": "integer",
                                        "example": 1024
                                    },
                                    "diskMbytes": {
                                        "type": "integer",
                                        "example": 2048
                                    }
                                }
                            },
                            "buildId": {
                                "type": "string"
                            },
                            "defaultKeyValueStoreId": {
                                "type": "string"
                            },
                            "defaultDatasetId": {
                                "type": "string"
                            },
                            "defaultRequestQueueId": {
                                "type": "string"
                            },
                            "buildNumber": {
                                "type": "string",
                                "example": "1.0.0"
                            },
                            "containerUrl": {
                                "type": "string"
                            },
                            "usage": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "integer",
                                        "example": 1
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "usageTotalUsd": {
                                "type": "number",
                                "example": 0.00005
                            },
                            "usageUsd": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "number",
                                        "example": 0.00005
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
```
