---
title: "How to Scrape LinkedIn Jobs (2026 Guide) | Scrape.ly"
description: "Scrape LinkedIn jobs without code or with the API: the fields you get, a real 10-job run, cost per job, limits and what LinkedIn's terms say."
url: https://scrape.ly/blog/scrape-linkedin-jobs
markdown: https://scrape.ly/blog/scrape-linkedin-jobs.md
---

[Tutorials](https://scrape.ly/blog/category/tutorials.md)

# How to Scrape LinkedIn Jobs: Titles, Companies and Full Descriptions

Scrape public LinkedIn job listings by keyword and location, with no login: titles, companies, posting dates, seniority, descriptions and company details. Run it without code or from the API, with a real 10-job run and its cost.

[Openrole Actor publisher on the Scrape.ly Store](https://scrape.ly/blog/author/openrole.md)

- Published

  Oct 22, 2026

- Reading time

  8 min read

![Pixel-art robot with a grey terminal head showing an orange >\_ pulls a job card off a LinkedIn job board, marked with the LinkedIn logo, and drops it into a crate](https://scrape.ly/media/scrape-linkedin-jobs.png?w=1080)

You can scrape LinkedIn jobs from LinkedIn's public job search, the version anyone sees without logging in. You get titles, companies, locations, posting dates, seniority, full descriptions and company details. The fastest way is to run a ready-made Actor from the Scrape.ly Store with a keyword and a location, then export the results as CSV, JSON or Excel.

This guide covers public job listings only. It doesn't cover member profiles, and nothing here needs a LinkedIn account. You'll see the fields you get, how to run a search with no code, and how to do it from curl, Python, JavaScript and an AI agent. It also covers cost, limits and LinkedIn's terms. The examples use a real run from 22 October 2026.

## What data you can get from LinkedIn job listings

Each job is one row in the dataset. A field is present only when the public page has it, so some rows carry fewer fields than others.

| Field                                                           | What it holds                                                       |
| --------------------------------------------------------------- | ------------------------------------------------------------------- |
| `id`, `link`                                                    | LinkedIn job ID and the job page URL                                |
| `title`                                                         | Job title as posted                                                 |
| `companyName`                                                   | Employer name                                                       |
| `companyLinkedinUrl`                                            | The employer's LinkedIn company page                                |
| `location`                                                      | Location text, e.g. "Berlin, Berlin, Germany"                       |
| `postedAt`                                                      | Posting date (`YYYY-MM-DD`)                                         |
| `employmentType`                                                | Full-time, part-time and so on                                      |
| `seniorityLevel`                                                | Seniority from the job's criteria block, including "Not Applicable" |
| `industries`                                                    | Industry from the criteria block                                    |
| `applicantsCount`                                               | Applicant count, only when the page says exactly "N applicants"     |
| `salaryInfo`, `salaryInsights`                                  | Pay range amounts and LinkedIn's pay note, when the job lists them  |
| `descriptionText`, `descriptionHtml`                            | The full public job description                                     |
| `applyMethod`                                                   | `Offsite` or `Easy Apply`                                           |
| `companyDescription`, `companyWebsite`, `companyEmployeesCount` | From the company page, when `scrapeCompany` is on                   |

What you don't get: the recruiter's name, title, photo and profile URL aren't on the logged-out job page, so the Actor doesn't return them. The apply link sits behind sign-in, so no apply URL is stored either.

## How to scrape LinkedIn jobs without code

1. Open the [LinkedIn Jobs Scraper](https://scrape.ly/store/openrole/linkedin-jobs-scraper.md) in the Scrape.ly Store and sign in.
2. Pick the preset [Data engineer jobs in Berlin this week, 10 jobs](https://scrape.ly/store/openrole/linkedin-jobs-scraper/presets/keyword-location-filters.md). It fills in the input below.
3. Check the input. This is the exact JSON we ran:

```json
{
  "keywords": "data engineer",
  "location": "Berlin",
  "distance": 0,
  "datePosted": "pastWeek",
  "limitPerSource": 10
}
```

4. Click **Start**. Our run finished in about 46 seconds.
5. Open the dataset and export it as JSON, CSV or Excel. The **Jobs** view shows title, company, location, posting date, employment type, applicant count and link.

The input fields that matter most:

- **`keywords`** **and&#x20;**__`location`__: what you'd type into LinkedIn's job search. `distance` sets the radius in miles around the location; leave it empty for LinkedIn's default.
- **`datePosted`**: `anyTime` (the default), `past24Hours`, `pastWeek` or `pastMonth`. A daily run with `past24Hours` collects what was posted since yesterday.
- **`limitPerSource`**: the maximum number of jobs per search. The default is 20, and LinkedIn returns at most about 1,000 per search.
- **`urls`**: paste one or more public LinkedIn job search URLs instead. When `urls` is set, the keyword and filter fields are ignored. Build the search in a private window so you get the public version.
- **`scrapeCompany`**: on by default. It loads each employer's company page for the about text, website and headcount. That means extra requests per job, so runs take longer.

Scrape.ly Store LinkedIn Jobs Scraper Public LinkedIn job listings by keyword, location or search URL: titles, companies, dates, seniority, full descriptions and company details. $0.0015 per job.

[Try it free](https://scrape.ly/store/openrole/linkedin-jobs-scraper.md) [Browse the Store](https://scrape.ly/store.md)

## How to scrape LinkedIn jobs with the API

Get your API token from **Settings → API & Integrations** in the dashboard and export it as `SCRAPELY_TOKEN`. The [Actors API docs](https://scrape.ly/docs/api-actors.md) list every endpoint.

**curl.** This search took about 46 seconds, which is longer than the 45-second limit of the synchronous endpoint. So start the run, wait for it, then read the items:

```shell
# 1. start the run; the response's data.id is the run ID
curl -X POST "https://api.scrape.ly/v2/actors/openrole~linkedin-jobs-scraper/runs"
  -H "Authorization: Bearer $SCRAPELY_TOKEN"
  -H "Content-Type: application/json"
  -d '{"keywords": "data engineer", "location": "Berlin", "distance": 0, "datePosted": "pastWeek", "limitPerSource": 10}'
# 2. wait (up to 60 s per call; repeat until status is SUCCEEDED)
curl "https://api.scrape.ly/v2/actor-runs/RUN_ID?waitForFinish=60"
  -H "Authorization: Bearer $SCRAPELY_TOKEN"
# 3. read the items (CSV here)
curl "https://api.scrape.ly/v2/actor-runs/RUN_ID/dataset/items?format=csv"
  -H "Authorization: Bearer $SCRAPELY_TOKEN"
```

**Python.** Install the client with `pip install scrapely-client-python`. `call()` waits for the run however long it takes, so the 46 seconds don't matter here. See the [Python client docs](https://scrape.ly/docs/client-python.md).

```python
import os
from scrapely_client import ScrapelyClient

client = ScrapelyClient(os.environ["SCRAPELY_TOKEN"], api_url="https://api.scrape.ly")

run = client.actor("openrole/linkedin-jobs-scraper").call(
    run_input={"keywords": "data engineer", "location": "Berlin", "distance": 0, "datePosted": "pastWeek", "limitPerSource": 10}
)
print(run.status)  # SUCCEEDED, FAILED, ABORTED or TIMED-OUT
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item["title"], item["companyName"], item.get("applicantsCount"))
```

**JavaScript.** Install with `npm install scrapely-client`. See the [JavaScript client docs](https://scrape.ly/docs/client-js.md).

```javascript
import { ScrapelyClient } from 'scrapely-client';

const client = new ScrapelyClient({ token: process.env.SCRAPELY_TOKEN, baseUrl: 'https://api.scrape.ly' });

const run = await client.actor('openrole/linkedin-jobs-scraper').call({
  keywords: 'data engineer',
  location: 'Berlin',
  distance: 0,
  datePosted: 'pastWeek',
  limitPerSource: 10,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items.length, items[0]);
```

**AI agents (MCP).** Scrape.ly runs a hosted MCP server at `https://api.scrape.ly/mcp`. In Cursor, add it to `~/.cursor/mcp.json`:

```json
{
  "mcpServers": {
    "scrapely": {
      "url": "https://api.scrape.ly/mcp",
      "headers": { "Authorization": "Bearer YOUR_TOKEN" }
    }
  }
}
```

In Claude Code, run `claude mcp add --transport http scrapely https://api.scrape.ly/mcp`, then `/mcp` to sign in. The agent's `call-actor` tool waits up to 45 seconds, so for this search it starts the run and then polls `get-actor-run` before it reads the results with `get-actor-output`. Try a prompt like: "Use scrapely to run openrole/linkedin-jobs-scraper for data engineer jobs in Berlin posted in the past week, 10 jobs, and give me a CSV of title, company, posting date and applicant count." The [MCP docs](https://scrape.ly/docs/mcp.md) cover OAuth and other clients.

## A real run: data engineer jobs in Berlin this week

We ran the preset on 22 October 2026. It saved 10 jobs in 45.961 seconds with 512 MB of memory, and the run was charged $0.0181. Here are five of the rows (the job link column is left out for width):

| title                                                         | companyName               | location                | postedAt   | employmentType | seniorityLevel   | applicantsCount |
| ------------------------------------------------------------- | ------------------------- | ----------------------- | ---------- | -------------- | ---------------- | --------------- |
| Domain Data Engineer - Power Grid & System Operations (w/m/d) | 50Hertz Transmission GmbH | Berlin, Berlin, Germany | 2026-10-22 | Full-time      | Mid-Senior level | 56              |
| Data Engineer - Digital Health Excellence Center (w/m/d)      | EY                        | Berlin, Berlin, Germany | 2026-10-19 | Full-time      | Mid-Senior level | —               |
| Data Engineer (all genders) in Berlin - Jobbird.com           | Jobster                   | Berlin, Berlin, Germany | 2026-10-22 | Full-time      | Not Applicable   | —               |
| Data Engineer (f/m/x)                                         | Netconomy                 | Berlin, Berlin, Germany | 2026-10-21 | Full-time      | Mid-Senior level | 50              |
| Senior Data Engineer - Shop Analytics (all genders)           | ABOUT YOU                 | Berlin, Berlin, Germany | 2026-10-16 | Full-time      | Mid-Senior level | 73              |

The rows hold company data only. We shortened long text in our sample, but there were no names to mask.

The console shows where the time went:

```plaintext
20261022T15:22:59.535Z [scrapely.proxy_configuration] INFO  Platform proxy access check succeeded.
20261022T15:23:00.765Z [scrapely.actor] INFO  Fetching search offset 0 (0 kept)
20261022T15:23:01.551Z [scrapely.actor] INFO  Search offset 0 kept 10 new cards (10 total)
20261022T15:23:01.551Z [scrapely.actor] INFO  Search list ended with 10 jobs: reached limit
20261022T15:23:42.582Z [scrapely.actor] INFO  Stored 10 jobs with descriptions
20261022T15:23:43.183Z [Scrapely] Container state: status=exited exitCode=0 oomKilled=false
```

The search itself took under a second. Most of the run, about 41 seconds, went into opening each job page and each company page, because `scrapeCompany` was on. All posting dates fall between 16 and 22 October, inside the `pastWeek` window. Two jobs have no `applicantsCount`, since the Actor only fills it when LinkedIn prints an exact "N applicants" caption.

## What it costs

The Actor charges one event: **$0.0015 per job** saved to the dataset. Company details from `scrapeCompany` are included at no extra charge. Platform usage (compute, proxy traffic and storage) is billed on top.

For this run, 10 jobs × $0.0015 = $0.015 in job fees. The run was charged $0.0181 in total because platform usage added $0.00153 in compute, $0.00150 in residential proxy traffic and $0.00005 in dataset writes.

At the same price, 1,000 jobs cost $1.50 in event fees, plus platform usage. If you set a maximum cost per run, the run saves as many jobs as that budget pays for, then stops cleanly.

## Limits and common problems

- **The synchronous endpoint times out.** A 10-job search with company pages took 46 seconds. `run-sync-get-dataset-items` waits only 45 seconds, then answers `408` while the run keeps going. Don't call it again, because that starts a second run. Use the async flow or a client.
- **About 1,000 jobs per search.** LinkedIn stops there. To go further, turn on `splitByLocation` and set `splitCountry`. The search then repeats across major cities in that country and drops duplicate job IDs.
- **Old search URLs lose filters.** LinkedIn's AI job search dropped classic filters such as experience, job type, workplace, salary and sort. With `autoConvertToAiSearch` on (the default), those codes become keywords. Industry, function and title codes (`f_I`, `f_F`, `f_T`) aren't converted, so put those ideas in `keywords` yourself.
- **Fields come and go.** Salary, applicant count and headcount appear only when the public page shows them. A headcount range such as "10,001+" isn't turned into a number.
- **Memory for big runs.** The README sizes a single search for 1 GB, though our 10-job run used 512 MB. Raise memory when you pass many URLs or split by city. A URL that isn't a LinkedIn job search is skipped and listed under `failedInputs` in the run's `OUTPUT` record.

## Is it legal to scrape LinkedIn jobs?

Job listings on LinkedIn's public job search are visible without an account, and this Actor reads only those logged-out pages. That doesn't make every use fine. LinkedIn's [User Agreement](https://www.linkedin.com/legal/user-agreement) lists scraping among its "Don'ts" in section 8.2: you may not use software, scripts, robots or crawlers "to scrape or copy the Services." So scraping LinkedIn conflicts with its terms, whatever the data. Listings are mostly company data, but descriptions sometimes name a contact person. Any personal data you store falls under privacy laws such as the GDPR and CCPA, so keep only what you need and don't build profiles of people. This is general information, not legal advice. If the data feeds a product or a sales process, ask a lawyer about your use case and jurisdiction.

## Alternatives

- **LinkedIn's official Job Posting API.** It lets approved partners, such as applicant tracking systems and job distributors, _post_ jobs to LinkedIn. It isn't for reading listings, and LinkedIn [says it isn't accepting new partnerships](https://learn.microsoft.com/en-us/linkedin/talent/job-postings/api/overview). It's only the better choice if you already have that partnership.
- [**JobSpy**](https://github.com/speedyapply/JobSpy) **.** An open-source Python library that searches LinkedIn, Indeed, Glassdoor, ZipRecruiter and other boards into one dataframe. Pick it if you want several job boards in one script and can run your own proxies. Its README notes that LinkedIn answers bursts of requests with a 429 error.
- [**linkedin-jobs-scraper**](https://pypi.org/project/linkedin-jobs-scraper/) **.** An MIT-licensed Python package that drives a headless browser with Selenium. The author marks it for personal or educational use.
- **Build it yourself.** A headless browser or plain HTTP requests against the public job pages work, as tutorials like [Scrapfly's](https://scrapfly.io/blog/posts/how-to-scrape-linkedin) show. You'll own the parsing, retries and blocking, and you'll fix it when LinkedIn changes its markup. That's a fair trade for a one-off project.

## FAQ

### Can you scrape LinkedIn jobs without an account?

Yes. The public job search and job pages open without logging in, and the Actor uses only those. You don't need cookies or a LinkedIn login. Recruiter details and apply links sit behind sign-in, so you won't get them.

### How do I scrape LinkedIn job postings to CSV?

Run the Actor, open the dataset and choose CSV in the export menu. From the API, add `?format=csv` to the dataset items URL, as in step 3 of the curl example. The [datasets API docs](https://scrape.ly/docs/api-datasets.md) list the other formats.

### How many LinkedIn jobs can I scrape per search?

LinkedIn returns at most about 1,000 jobs per search. Set `limitPerSource` up to that. For larger markets, split the search by city with `splitByLocation` and `splitCountry`, or pass several search URLs.

### How much does LinkedIn job scraping cost?

$0.0015 per job, so $1.50 per 1,000 jobs in event fees, plus platform usage. Our 10-job run was charged $0.0181 in total, company details included.

Scrape.ly Store Scrape LinkedIn jobs in minutes Run a keyword and location search on public LinkedIn job listings and export titles, companies, dates and full descriptions to CSV, JSON or Excel.

[Try it free](https://scrape.ly/store/openrole/linkedin-jobs-scraper.md) [Browse the Store](https://scrape.ly/store.md)

About the author

[Openrole](https://scrape.ly/blog/author/openrole.md) Actor publisher on the Scrape.ly Store

Every open role, wherever it is posted: job boards, company ATS pages and freelance markets in dozens of countries. For recruiters, labour-market analysts and job aggregators. A Scrapely studio.

- [Website](https://scrape.ly/users/openrole.md)

Keep reading

## More in Tutorials

- [![Pixel-art robot with an orange >\_ on its terminal head pulls a pink reel card with a play button out of a phone showing the Instagram logo and a grid of posts, and loads it into a crate of colored tiles](https://scrape.ly/media/scrape-instagram.png?w=750)](https://scrape.ly/blog/scrape-instagram.md)
  ### [How to Scrape Instagram Reels, Posts and Comments Without a Login](https://scrape.ly/blog/scrape-instagram.md)
  [You can scrape Instagram's public profiles, posts, reels and comments without logging in: captions, likes, plays, timestamps and owner details. Here is how, without code or with the API, using a real run on National Geographic's reels.](https://scrape.ly/blog/scrape-instagram.md)
- [![Pixel-art robot with an orange >\_ on its terminal head pulls a stats card with a bar chart out of a phone showing the TikTok logo and loads it into a crate](https://scrape.ly/media/tiktok-scraping.png?w=750)](https://scrape.ly/blog/tiktok-scraping.md)
  ### [TikTok Scraping: Get Videos, Plays, Likes and Comments as JSON](https://scrape.ly/blog/tiktok-scraping.md)
  [TikTok scraping gets you captions, play, like, share and comment counts, music and hashtags from public profiles, hashtags and keyword searches. Here is how to do it without code or with the API, using a real keyword-search run.](https://scrape.ly/blog/tiktok-scraping.md)
- [![Pixel-art robot with an orange >\_ on its terminal head pulls a video card with the YouTube play-button logo out of a large monitor showing the YouTube logo and loads it into a crate](https://scrape.ly/media/youtube-scraping.png?w=750)](https://scrape.ly/blog/youtube-scraping.md)
  ### [YouTube Scraping: Get Video Stats, Channel Data and Subtitles](https://scrape.ly/blog/youtube-scraping.md)
  [YouTube scraping gets you titles, views, likes, comment counts, durations and channel stats from public channels, playlists and searches. Here is how to do it without code or with the API, using a real run on the NASA JPL channel.](https://scrape.ly/blog/youtube-scraping.md)

## The internet is full of data. Start owning yours.

Scrape.ly gives you the infrastructure, the tooling, and the team to extract, process, and operationalize web data at any scale. Setup takes minutes. The advantage lasts forever.

[Get Started Free](https://my.scrape.ly/register) [Chat with Support](mailto:support@scrape.ly?subject=Question%20about%20Scrape.ly\&body=Hi%2C%20I%20have%20a%20question%20about%20Scrape.ly.)

No credit card. No setup calls.
