# LinkedIn Jobs Scraper (`wildorigins/linkedin-jobs`) Actor

🏷️ From $0.60 / 1K | Export LinkedIn jobs with company enrichment, CV keyword matching, applicant counts and annualised salary. No login, no cookie, no proxy needed.

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

## Pricing

from $0.60 / 1,000 jobs

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

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

## What's an Apify Actor?

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

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

## How to integrate an Actor?

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

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

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

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

# README

## LinkedIn Jobs Scraper

Export LinkedIn job listings with company enrichment, CV keyword matching, applicant counts and salary parsing. No login, no session cookie, no proxy required.

### 🔍 What does LinkedIn Jobs Scraper do?

It runs a job search on LinkedIn and returns every listing it finds as a clean row of data, ready for a spreadsheet, a database or your own product.

**No login, no session cookie, no proxy required.** This reads LinkedIn's public guest endpoints, the ones that serve logged out job browsing, so there is no session to expire and no account of yours at risk.

On top of the listing itself you can add company details, filter on applicant counts and score every job against your own CV keywords.

### 🧭 Is there an official LinkedIn jobs API?

Not one you can sign up for. LinkedIn's job APIs live inside its Talent Solutions partner programme, which is limited to approved, incorporated partner companies, and LinkedIn has stopped accepting new partners for its Job Posting API. There is no public, self-serve API that returns LinkedIn job search results.

This Actor reads the public guest job pages instead, the ones LinkedIn serves to logged out visitors, and returns them as structured rows. You get the search as data, with salary parsed and annualised, plus the filtering and CV matching described below, without a partner agreement, a login or a cookie.

### 📊 What data can I extract from LinkedIn?

One row per job:

| | Field | What it holds |
|---|---|---|
| 🆔 | `jobId` | Stable id of the listing on LinkedIn |
| 🏷️ | `title` | The job title as advertised |
| 🏢 | `company` | The hiring company shown on the listing |
| 🔗 | `companyUrl` | LinkedIn page of the hiring company |
| 📍 | `location` | The location as LinkedIn displays it |
| 📅 | `postedAt` | Posting date as an ISO timestamp |
| 💰 | `listingSalary` | Salary exactly as written on the listing |
| 🔗 | `url` | Direct link to the listing |
| 💰 | `salaryFrom` | Lower bound of the advertised salary |
| 💰 | `salaryTo` | Upper bound of the advertised salary |
| 💰 | `salaryCurrency` | Currency of the salary figures |
| 💰 | `salaryPeriod` | Period the figures refer to, such as Annual or Hourly |
| 💰 | `salaryAnnualisedFrom` | Lower bound converted to an annual figure |
| 💰 | `salaryAnnualisedTo` | Upper bound converted to an annual figure |
| 💰 | `salaryRaw` | The unparsed salary text, kept for auditing |
| ✅ | `hasSalary` | Whether the listing states any pay at all |
| 💰 | `payBand` | NHS style pay band, kept as a fact rather than converted |
| 💰 | `payScheme` | The pay scheme the band belongs to |
| 📥 | `searchKeywords` | The keywords this search ran with |
| 📥 | `searchLocation` | The location this search ran with |
| 📅 | `retrievedAt` | When the row was collected |

Optional features add their own columns on top: applicant counts and job categorisation from the detail pages, `keywordMatchScore` with its matched and unmatched keyword lists when `resumeKeywords` is set, and the company fields added by `enrichCompanyData`.

### 💡 Why scrape LinkedIn jobs?

**Beating the applicant pile.** A job seeker pastes their CV keywords in, filters to under ten applicants at direct employers, and applies to the roles that scored highest before the crowd arrives.

**Hiring signals.** A recruiter watching which companies are hiring for the same role in the same city runs the search weekly and diffs the company column.

**Salary benchmarking.** An HR manager pricing a nursing or engineering opening pulls annualised pay by title and region, with NHS bands reported as bands rather than dropped.

**Market research.** An analyst tracking whether fintech firms in Leeds are still adding sales roles runs the same query monthly and charts the counts by company size.

**Agency filtering.** Someone tired of the same role appearing five times through different agencies switches on `excludeRecruitingAgencies` and sees only direct employers.

**Watching one employer.** A candidate interviewing at a company pulls everything it advertises, enriched with headcount and industry, to see which teams are growing.

### 🚀 How do I use LinkedIn Jobs Scraper?

1. Click **Try for free**.
2. Enter what you are searching for and where, for example `software engineer` in `London`.
3. Add any filters you want, such as date posted, workplace type or company size.
4. Cap the run with `maxResults`, then click **Start**.
5. Download the results as JSON, CSV or Excel, or pull them from the API.

### ⬇️ Input

```json
{
  "keywords": "software engineer",
  "location": "London",
  "maxResults": 100
}
```

Set at least one of `keywords` or `location`.

| Field | Type | Default | What it does |
|---|---|---|---|
| `keywords` | string | | Job title or keywords |
| `location` | string | | City, region or country |
| `datePosted` | string | `any` | Any time, past 24 hours, past week or past month |
| `workplaceType` | string | `any` | On site, remote or hybrid |
| `jobType` | string | `any` | Full time, part time, contract, temporary, internship or volunteer |
| `experienceLevel` | string | `any` | Internship through executive |
| `sortBy` | string | `relevance` | Relevance or most recent |
| `requireSalaryInfo` | boolean | `false` | Only jobs that state pay or an NHS band |
| `maxResults` | integer | `100` | Hard cap on billable results |

Many more filters are available including company size, industry, applicant count and CV keyword matching, see the input schema in the Console.

#### Company filters

| Field | Description |
|---|---|
| `companyInclude` | Only these employers. Suffixes like Ltd and PLC are ignored when matching |
| `companyExclude` | Drop these employers |
| `excludeRecruitingAgencies` | Drop agency listings and keep direct employers |
| `enrichCompanyData` | Add employee count, followers, industry, website and location |
| `companySizeMin` / `companySizeMax` | Filter by employee count |
| `companyFollowersMin` / `companyFollowersMax` | Filter by LinkedIn following |

Company pages are fetched once per company and cached within a run, so a hundred jobs at one employer costs one extra request, not a hundred.

#### Job filters

| Field | Description |
|---|---|
| `excludeJobTitles` | Drop titles containing any of these words |
| `underTenApplicants` | LinkedIn's own low competition filter, applied server side |
| `maxApplicants` | Only jobs with an exact count at or below this |
| `easyApplyOnly` | Apply without leaving LinkedIn, applied server side |
| `requireSalaryInfo` | Only jobs that state pay or an NHS band |

#### CV matching

Paste your skills into `resumeKeywords` and every job is scored on how many appear in the title and description. You get `keywordMatchScore` as a percentage plus `matchedKeywords` and `unmatchedKeywords`, so you can see exactly why a job scored what it did. Set `minKeywordScore` to drop anything below a threshold.

### ⬆️ Output

#### Table view

Results arrive as a table you can sort and filter in the Console. There is a second view, Salary detail, that lines up the parsed and annualised pay figures for comparing listings against each other.

Every run also writes a `RUN_SUMMARY` to the key value store showing how many jobs were scanned, how many were kept, and exactly which filter rejected the rest. If a run returns nothing you can see why rather than guessing.

#### JSON

A typical row:

```json
{
  "title": "Product Manager",
  "company": "Example Group",
  "location": "Reading, England, United Kingdom",
  "postedAt": "2026-08-07T00:00:00.000Z",
  "url": "https://uk.linkedin.com/jobs/view/product-manager-4448545238",
  "jobId": "4448545238",
  "companyUrl": "https://uk.linkedin.com/company/examplegroup"
}
```

Download it from the run as JSON, CSV or Excel, or read it straight from the API.

### Two things this does that others do not

**Salary you can actually compare.** Pay is published as hourly, daily or annual with no consistency. Every job gets `salaryPeriod` plus `salaryAnnualisedFrom` and `salaryAnnualisedTo`, so one column sorts correctly across the whole result set.

**UK healthcare pay bands.** NHS roles state pay as "Band 5" rather than a number, so generic scrapers report no salary at all. On a sample of 20 UK nursing jobs, numeric salary extraction found pay on 1. Adding band detection took that to 9. You get `payBand` and `payScheme` as facts. The actor deliberately does not convert bands into salary figures, because published Agenda for Change tables disagree with each other materially and inventing a number would be worse than leaving it out.

### Honest limits

**1,000 results per search.** LinkedIn's own ceiling, not a bug. Verified: offset 975 returns jobs, offset 1000 returns an error. Narrow the search with filters or date ranges to get past it.

**Applicant counts are bucketed below 25.** LinkedIn will filter to low competition jobs server side via `underTenApplicants`, but its guest view still reports "be among the first 25 applicants" rather than the exact figure. So you can select those jobs, you just cannot see the true number. The `applicantsBasis` field marks every count as `exact`, `upper-bound` or `minimum` so you always know which you are looking at.

**Salary is often absent.** LinkedIn does not require employers to publish pay, and most do not. `requireSalaryInfo` filters to those that do.

### Empty results are not a failure

If nothing passes your filters the run finishes successfully with an empty
dataset, and `RUN_SUMMARY` holds the rejection breakdown, so you can see which
filter ate everything.

An earlier version threw an error here. That recorded a failed run against the
actor's success rate for what was a filter choice, and it discarded the
diagnostic you needed.

### When a filter cannot be evaluated

If a detail page fails to load, the job's function, industry and applicant count
are unknown. By default those jobs are **kept**, so a network problem is never
mistaken for a filter decision. `detailFailures` in `RUN_SUMMARY` tells you how
often it happened.

Set `strictFilters` to reject them instead.

### Speed

`concurrency` controls how many detail and company pages are read at once,
default 6, maximum 10. It does not change how many requests are made, only how
long you wait. Ten detail pages take under a second at the default, against
roughly eight seconds one at a time.

**Set the run timeout to suit the size of the ask.** This Actor's default is 3600 seconds, which is comfortably more than the largest run in the table above (150 jobs with details on takes about 2 minutes). You are charged per delivered result rather than per minute, so a generous timeout costs you nothing and a tight one risks losing the run's work. Lower it only if you want a hard ceiling on how long a scheduled run may sit.

### Salary

Salary is read from the listing's own pay field, or from a labelled pay section
on the detail page. It is never extracted from the body of a job description: a
project budget or a revenue figure in the copy would otherwise become the
salary and then feed `salaryMin` filtering. Where there is no pay information,
`hasSalary` is false rather than a number being guessed.

### ⏱️ How long does a run take?

Measured on real runs, so you know what normal looks like and can tell it apart from a run that has stalled.

| Jobs returned | Typical run time |
|---|---|
| 10 | about 5 seconds |
| 60 | about 11 seconds |
| 100 | 18 to 25 seconds |
| 400 | about 55 seconds |

Between an eighth and a quarter of a second a job on the listing pass, which is what the table above is measuring. Two options change that completely, because both add a request per job on top of the listing. `fetchDetails` opens each job's own page for the full description and the applicant count: 150 jobs with it on took about 105 seconds against roughly 25 seconds without. `enrichCompanyData` looks up each distinct employer, and 80 jobs with both switched on took about 128 seconds. Turn them on when you need those fields and expect a run measured in minutes rather than seconds. The first few seconds of any run are the container starting rather than the work.

A run is never silently stuck. Jobs are logged as each listing page is read, and written to the dataset as they are parsed rather than held back to the end, so a run that hits its time limit still leaves everything it had already collected. A search that matches nothing ends successfully with an empty dataset and the reason in its status message.

### 💰 How much does it cost?

Three events, because the cost of a run is not proportional to how many jobs
survive your filters.

| Event | Charged when |
|---|---|
| `job-detail-fetched` | A job detail page is read |
| `company-enriched` | A company page is read, once per company |
| `job-result` | A job passes every filter and is delivered |

Deep filtering asks this actor to read a thousand jobs to find fourteen. Under a
single per-result charge, the better your filters the worse the economics, which
is the wrong incentive on the one feature that sets this apart. Splitting the
events means you pay for the reading you asked for and the results you got, and
both are visible before you run.

Cheap filters, company name, title exclusions and agency exclusion, are applied
before any detail request, so they never cost you a fetch.

#### What a run actually costs

The listing shows the result price. That is the cheap end, and deep filtering
costs more, so here are both plainly:

| Run | Cost |
|---|---|
| 100 jobs, no detail filters | about $0.10 |
| 1,000 scanned, 14 delivered after salary, applicant and keyword filters | about $2.01 |

The second run reads a thousand detail pages to find fourteen jobs. That reading
is the work you asked for and it is charged at `job-detail-fetched`, not hidden
in the result price. Filters that need no detail page, company name, title
exclusions and agency exclusion, are applied first and cost nothing.

If you want the cheap end, leave the detail filters off.

### 🔌 Integrations

Send results straight to Google Sheets, Slack, Airtable, Zapier, Make or your own webhook using [Apify integrations](https://docs.apify.com/platform/integrations). You can also trigger a run whenever something happens in another tool.

AI agents can run this Actor too, through the [Apify MCP server](https://mcp.apify.com). An agent connected to mcp.apify.com can discover it, read its input schema and start a run under the identifier `spookyweb/linkedin-jobs`, then read the finished dataset. That means an assistant asked to shortlist low competition product manager roles in London can call this Actor with the right filters and answer from live listings.

### 🔗 Using LinkedIn Jobs Scraper with the Apify API

```bash
curl -X POST "https://api.apify.com/v2/acts/spookyweb~linkedin-jobs/run-sync-get-dataset-items?token=YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"keywords": "software engineer", "location": "London", "maxResults": 100}'
```

Or with the Apify client:

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

const client = new ApifyClient({ token: 'YOUR_TOKEN' });
const run = await client.actor('spookyweb/linkedin-jobs').call({
  keywords: 'software engineer',
  location: 'London',
  maxResults: 100,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
```

Full detail is in the [Apify API reference](https://docs.apify.com/api/v2), and every run is also callable from the [Python](https://docs.apify.com/api/client/python) and [JavaScript](https://docs.apify.com/api/client/js) clients.

### ❓ FAQ

#### Do I need a LinkedIn account or a session cookie?

No, and that is the point. This reads the public guest job pages, so there is no login step, no cookie to paste and no session of yours to expire or get flagged. You also do not need a proxy.

#### How many jobs can I get from one search?

Up to 1,000, which is LinkedIn's own ceiling rather than a limit set here. Past that the guest endpoint returns a 400, which the run treats as a normal end and finishes successfully with what it has. To cover more of a market, split the search by location, by job title or by date range.

#### What is CV keyword matching?

Put your skills into `resumeKeywords` and every job is scored on how many of them appear in its title and description. The row carries `keywordMatchScore` as a percentage plus `matchedKeywords` and `unmatchedKeywords`, so the score is auditable. `minKeywordScore` drops anything below a threshold.

#### Does it get applicant counts?

Yes, in `applicants`, with `applicantsBasis` saying whether the figure is `exact`, an `upper-bound` or a `minimum`. Below 25 applicants LinkedIn's guest view reports a bucket rather than a number, so you can still select those jobs with `underTenApplicants`, you just cannot see the true count.

#### What does enrichCompanyData add?

Employee count, followers, industry, website, description and location for the hiring company. Company pages are read once per company and cached within the run, so a hundred jobs at one employer costs one extra request rather than a hundred.

#### Why is a job kept when a lookup fails?

Because a network problem is not a filter decision. If a detail page will not load, the job's function, industry and applicant count are unknown, and dropping it would quietly turn an outage into a filtered result. Those jobs are kept by default and counted in `detailFailures`. Set `strictFilters` to reject them instead.

### ⚖️ Is it legal to scrape LinkedIn?

This reads LinkedIn's public guest job pages, the ones anyone can see without an account. It never logs in, it never uses a cookie, and it never touches anything behind authentication. Job adverts are business information published by employers to be seen.

Scraping public data is lawful in the UK, the EU and the US, and the hiQ v LinkedIn line of cases went to public data specifically. If you go on to combine this data with personal data, that is on you to handle under GDPR. Apify's [ethical scraping guide](https://blog.apify.com/is-web-scraping-legal/) covers the wider picture.

### 👍 Your feedback

Found a bug, or want a field that is not here yet? Open an issue on the Actor's Issues tab. Requests that make the data more useful get built, and problems get fixed quickly.

### 🔎 You might also like

| Actor | What it does |
|---|---|
| [Indeed Jobs Scraper](https://apify.com/spookyweb/indeed-jobs) | Indeed listings with parsed salary figures, past the 15 job sign in wall |
| [Reed Jobs Scraper](https://apify.com/spookyweb/uk-jobs-reed) | UK jobs from Reed with applicant counts and annualised salaries |
| [Totaljobs Scraper](https://apify.com/spookyweb/uk-jobs-totaljobs) | UK jobs from Totaljobs with duplicate agency repostings removed |

# Actor input Schema

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

Job title or keywords, for example software engineer.

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

City, region or country, for example London.

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

How recently the job was posted. Use day, week or month to restrict to that window, or any to return everything LinkedIn still lists.

## `workplaceType` (type: `string`):

Where the work is done, as LinkedIn classifies it: onsite, remote or hybrid. Use any to include all three.

## `jobType` (type: `string`):

The contract basis of the role, for example full-time, contract or internship. Use any to include every basis LinkedIn lists.

## `experienceLevel` (type: `string`):

Seniority band as LinkedIn defines it, from entry and associate up to director and executive. Use any to include every level.

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

Result ordering. relevance uses LinkedIn own ranking, date returns the most recently posted first, which is what you want when monitoring for new listings.

## `maxResults` (type: `integer`):

Hard cap. LinkedIn serves at most 1000 per search. Each job is one billable result.

## `companyInclude` (type: `array`):

Keep only jobs from these employers. Company suffixes and case are ignored.

## `companyExclude` (type: `array`):

Employer names to drop from the results, matched on the company name as LinkedIn displays it. Useful for filtering out staffing agencies or your own listings.

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

Drop listings posted by agencies rather than the employer. Useful because LinkedIn carries far more direct employer posts than agency ones.

## `enrichCompanyData` (type: `boolean`):

Fetch each company page for employee count, followers, industry, website and location. Costs one extra request per company, cached within a run.

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

Only companies with at least this many employees. Enables company enrichment.

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

Only companies with at most this many employees.

## `companyFollowersMin` (type: `integer`):

Only companies with at least this many followers.

## `companyFollowersMax` (type: `integer`):

Only companies with at most this many followers.

## `excludeJobTitles` (type: `array`):

Drop jobs whose title contains any of these words.

## `maxApplicants` (type: `integer`):

Only jobs with an exact applicant count at or below this. Jobs showing "first 25" are excluded because their real count is unknown.

## `requireSalaryInfo` (type: `boolean`):

Only jobs that disclose a salary or an NHS pay band. Enables detail fetching.

## `resumeKeywords` (type: `array`):

Paste your skills. Each job is scored on how many appear in the title and description, returned as keywordMatchScore with matched and unmatched lists. Enables detail fetching.

## `minKeywordScore` (type: `integer`):

Drop jobs scoring below this percentage against your keywords.

## `fetchDetails` (type: `boolean`):

Fetch every job detail page for seniority, employment type, function, industries, description, applicant count and apply type. Turned on automatically when a filter needs it.

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

Optional. The guest endpoints need no proxy, but one helps at high volume from a single IP.

## `underTenApplicants` (type: `boolean`):

Uses LinkedIn own low-competition filter, applied server side so it costs no extra requests. Note LinkedIn does not reveal exact counts below 25, so results show "first 25" rather than the true figure.

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

Only jobs you can apply to without leaving LinkedIn. Applied server side by LinkedIn.

## `jobFunctionInclude` (type: `array`):

Keep only jobs whose LinkedIn job function matches, for example Engineering or Finance.

## `jobIndustryInclude` (type: `array`):

Keep only jobs in these industries, named as LinkedIn labels them. Leave empty to accept every industry.

## `jobIndustryExclude` (type: `array`):

Drop jobs in these industries, named as LinkedIn labels them. Applied after the include list, so an industry named in both is excluded.

## `salaryMin` (type: `integer`):

Minimum annual equivalent pay. Hourly and daily rates are annualised first so they are compared fairly.

## `salaryMax` (type: `integer`):

Upper bound on pay, as an annual equivalent in the currency the job advertises. Jobs that publish no salary are kept, because most listings omit it.

## `concurrency` (type: `integer`):

How many detail and company pages to fetch at once. Does not change how many requests are made, only how long the run takes.

## `strictFilters` (type: `boolean`):

By default a job is kept when a lookup fails, so a network problem is not mistaken for a filter decision. Turn this on to reject those instead.

## Actor input object example

```json
{
  "keywords": "software engineer",
  "location": "London",
  "datePosted": "any",
  "workplaceType": "any",
  "jobType": "any",
  "experienceLevel": "any",
  "sortBy": "relevance",
  "maxResults": 100,
  "excludeRecruitingAgencies": false,
  "enrichCompanyData": false,
  "requireSalaryInfo": false,
  "fetchDetails": false,
  "underTenApplicants": false,
  "easyApplyOnly": false,
  "concurrency": 6,
  "strictFilters": false
}
```

# Actor output Schema

## `results` (type: `string`):

One row per item: job listings with company and salary data.

## `runSummary` (type: `string`):

Counts and stop reason for the run.

# 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 = {
    "keywords": "software engineer",
    "location": "London"
};

// Run the Actor and wait for it to finish
const run = await client.actor("wildorigins/linkedin-jobs").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 = {
    "keywords": "software engineer",
    "location": "London",
}

# Run the Actor and wait for it to finish
run = client.actor("wildorigins/linkedin-jobs").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 '{
  "keywords": "software engineer",
  "location": "London"
}' |
apify call wildorigins/linkedin-jobs --silent --output-dataset

```

## MCP server setup

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

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/cCeqnn6O0RY7xuywm/builds/YuhPmTMpzajQ4iv1o/openapi.json
