# LinkedIn Jobs Scraper: Job Search Results as JSON (`tollprobe/linkedin-jobs-scraper`) Actor

Scrape public LinkedIn job postings for a job title and a place as JSON: title, company, location, posted date, salary line shown and optional description. No login. Pay per offer returned, with a cost cap per run.

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

## Pricing

Pay per event

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

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

## What's an Apify Actor?

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

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

## How to integrate an Actor?

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

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

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

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

# README

LinkedIn Jobs Scraper reads the public job offers that LinkedIn shows without logging in, for a job title and a place, and returns them as JSON: title, company, location, posted date, the salary line when the card shows one, and the offer address. You pay per offer returned, and you set a cost cap per run.

This Actor is independent and not affiliated with, endorsed by or sponsored by LinkedIn.

### Scrape LinkedIn job postings as JSON

You give the Actor a job title (or keywords) and a place, the same two things you would type in the job search of the site. It reads the public result pages and saves one item per offer to the run's dataset. You can start it from the Apify Console or call it through the Apify API, and download the dataset in the formats the platform offers for datasets.

With `includeDescription` set to `true`, the Actor also opens the public page of each offer and adds its description and its criteria. It is off by default, because it adds one request per offer.

#### What you get

Always:

- `jobId`
- `title`
- `company`
- `location`
- `postedDate` (YYYY-MM-DD, or null)
- `salary` (the salary line as displayed on the card, or null; most offers show none)
- `url`

With `includeDescription`:

- `description` (plain text)
- `seniorityLevel`
- `employmentType`
- `industries`

#### What it does not return

- Anything about the person who published the offer: no name, no profile link, no job title of that person, no hiring-team block.
- Email addresses and phone numbers: those recognised in a description (international numbers with a plus sign, North American numbers, five pairs of digits) and email addresses are replaced by `[removed]`, as are links to personal profiles. A contact written in another way can remain; the pattern is a filter, not a guarantee.
- Applicant counts, company pages and any field that is not listed above.

### How to scrape LinkedIn jobs with this Actor

1. Open the Actor on Apify and go to the Input tab.
2. Type a job title in `query`, for example `data engineer`, and a place in `location`, for example `Toronto`.
3. Set `maxItems` (default 20, up to 100). Leave `includeDescription` off unless you need the description.
4. Click Start. When the run ends, open the Output tab, or read the dataset through the API.

#### Input

| Field | Meaning |
|---|---|
| `query` | Job title or keywords, required |
| `location` | City or country, required |
| `maxItems` | Maximum offers returned. Default 20, up to 100 |
| `includeDescription` | `true` opens the page of each offer to add its description and criteria. Default `false` |
| `maxCostUsd` | Cost cap per run in USD, start event included. Default 0.25, enough for 100 offers |
| `proxyConfiguration` | Optional Apify proxy settings. Unset by default |

### Why run it on Apify

The Actor runs on the Apify platform, so there is nothing to host. You can call it from the API, schedule it, and read the run status and the run summary of each run. You pay per offer returned, and nothing for an offer that is not returned.

### Pricing and cost cap

You are charged per offer, after the offer is saved to the dataset, plus a small start event for each run.

| Event | Price on every plan |
|---|---|
| Offer returned | $0.001 |
| Actor start | $0.00005 |

Examples of cost for one run:

| Offers returned | Cost |
|---|---|
| 20 (the default) | 20 × $0.001 + $0.00005 = $0.02005 |
| 100 (the maximum) | 100 × $0.001 + $0.00005 = $0.10005 |

`maxCostUsd` stops the run when the cost of the offers returned, start event included, reaches it. It is computed at the same price on every plan. Offers already returned are kept, and the run status says it stopped at the cost cap. The run also stops if the platform charge limit for the run is reached.

### Input and output example

Input:

```json
{
  "query": "data engineer",
  "location": "Toronto",
  "maxItems": 5,
  "includeDescription": false
}
```

Output, from a run of this Actor with that input (two of the five items):

```json
[
  {
    "jobId": "4412402859",
    "title": "Data Engineer",
    "company": "Lakeview Loan Servicing, LLC.",
    "location": "North York, Ontario, Canada",
    "postedDate": "2026-05-15",
    "salary": null,
    "url": "https://www.linkedin.com/jobs/view/4412402859"
  },
  {
    "jobId": "4470030555",
    "title": "Data Engineer",
    "company": "Zurich Canada",
    "location": "Toronto, Ontario, Canada",
    "postedDate": "2026-10-16",
    "salary": null,
    "url": "https://www.linkedin.com/jobs/view/4470030555"
  }
]
```

The offers, dates and companies are those the site showed at the time of the run and change from one run to the next. With `includeDescription` set to `true`, an item also carries `description`, `seniorityLevel` (for example `Associate`), `employmentType` (for example `Full-time`) and `industries`.

#### Run summary

The key-value store holds `RUN_SUMMARY`: the run status, the number of offers, and counts only (requests, statuses, bytes, cards seen, why the run stopped). No page content and no offer content.

### How the Actor reads the site

One request every 5 seconds, with a declared `User-Agent` that names this Actor. No login, no cookie, no account, no challenge solving, no disguise. If the site refuses two requests in a row (for example status 429 or 403, or a sign-in page), the run stops there and keeps what it already returned: it does not retry around the refusal. The first page of results gives about 60 offers; the next pages are read by offset. A run of 100 offers without descriptions took about 40 seconds in our measurement (6 list requests); with descriptions each offer adds one more request, so it takes much longer.

### Other Actors by Tollprobe

The input fields, the output fields, the price per offer and a cost example for this Actor are also on its page at [tollprobe.com](https://tollprobe.com/actors/linkedin-jobs-scraper); all the Actors by Tollprobe are listed at [tollprobe.com/actors](https://tollprobe.com/actors).

- [Amazon Product Search Scraper](https://apify.com/tollprobe/amazon-products-scraper): public Amazon search results for a keyword as JSON, billed per product.

### FAQ

#### Is this the LinkedIn Jobs API?

No. It is an Actor that reads the public job pages and is called through the Apify API. It is not an API provided by LinkedIn.

#### Do I need a LinkedIn account or cookies?

No. The Actor does not log in and does not take a cookie.

#### Can I scrape the job description?

Yes, set `includeDescription` to `true`. The description comes as plain text, with the criteria `seniorityLevel`, `employmentType` and `industries`. Each offer then takes one more request.

#### How many job postings can I get in one run?

Up to 100 with `maxItems`. A run stops earlier if it reaches `maxCostUsd`, if the platform charge limit is reached, or if the site refuses two requests in a row.

#### Why is `salary` null on most offers?

The card showed no salary line. Most offers show none, and the Actor returns the line only as the site displays it.

#### Does it return the recruiter or the hiring team?

No. It returns nothing about the person who published the offer.

#### Is it legal to scrape LinkedIn?

Not legal advice. The site's terms apply to what you do with its content, and you are responsible for complying with them.

#### Where do I report a problem?

Use the Issues tab of this Actor on Apify.

# Actor input Schema

## `query` (type: `string`):

For example: data analyst.

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

A city or a country, for example: Toronto.

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

Maximum number of offers returned. Up to 100.

## `includeDescription` (type: `boolean`):

Opens the page of each offer to return its description and criteria. One more request per offer, so the run is slower. An offer whose page cannot be read is not returned and not charged.

## `maxCostUsd` (type: `number`):

The run stops when the cost of the offers returned, start event included, reaches this amount. Offers already returned are kept.

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

Optional. Leave unset to read from the Actor's own address.

## Actor input object example

```json
{
  "query": "data analyst",
  "location": "Toronto",
  "maxItems": 20,
  "includeDescription": false,
  "maxCostUsd": 0.25
}
```

# Actor output Schema

## `offers` (type: `string`):

One item per job offer returned: offer id, title, company, location, posted date, salary line shown, offer URL, and the description fields when includeDescription is on.

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

Counts only: status, number of items, and request statistics.

# 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 = {
    "query": "data analyst",
    "location": "Toronto"
};

// Run the Actor and wait for it to finish
const run = await client.actor("tollprobe/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 = {
    "query": "data analyst",
    "location": "Toronto",
}

# Run the Actor and wait for it to finish
run = client.actor("tollprobe/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 '{
  "query": "data analyst",
  "location": "Toronto"
}' |
apify call tollprobe/linkedin-jobs-scraper --silent --output-dataset

```

## MCP server setup

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