# LinkedIn Learning Course & Learning Path Finder (`nimbryxo-studio/linkedin-learning-course-and-learning-path-finder`) Actor

Search the LinkedIn Learning catalog by keyword for courses, learning paths or videos, with instructor, duration, difficulty, topics and skills taught. No login required.

- **URL**: https://apify.com/nimbryxo-studio/linkedin-learning-course-and-learning-path-finder.md
- **Developed by:** [Nimbryxo-studio](https://apify.com/nimbryxo-studio) (community)
- **Categories:** Developer tools, Videos, Other
- **Features:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.49 / 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.
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 Learning Course & Learning Path Finder

Read the public LinkedIn Learning catalog without an account, a subscription seat or a cookie. Search by keyword with LinkedIn's own filters, read specific course / learning-path / chapter URLs, or browse every learning path, and get structured records with instructor, duration, level, rating, learner count, skills, syllabus, reviews and certificate data.

### What this actor does

- **Searches the public catalog** by keyword, with one query per line for batch runs.
- **Uses LinkedIn's real filters**: skill level, content type, time to complete, software label and sort order (best match, most viewed, newest).
- **Collects past the 50-card guest page** by re-running the query across LinkedIn's own filter slices and merging unique records.
- **Reads detail pages** for the full record: description, skills taught, syllabus sections, chapters, free-preview lessons, rating, rating count, learner count, provider, category path, reviews and certificate.
- **Accepts URLs**: individual courses, learning paths, video chapters, topic pages and filtered search URLs.
- **Browses all learning paths** from the public all-paths page.
- **Skips LinkedIn's no-results fallback.** When nothing matches, LinkedIn renders a page of unrelated popular courses; the actor detects that page and returns zero records with an explanation instead of fabricated relevance.
- **Empty fields are omitted** instead of writing nulls or sentinels.

### Modes

| Mode | What it needs | What it returns |
|---|---|---|
| `search` (default) | `searchQuery` | Courses, learning paths or video chapters matching the query and filters |
| `byUrls` | `urls` | One record per course, learning path, chapter or search URL |
| `learningPaths` | nothing | Up to 50 public learning paths, optionally enriched |

### Input

| Field | Type | Default | Description |
|---|---|---|---|
| `mode` | select | `search` | `search`, `byUrls` or `learningPaths` |
| `searchQuery` | text | - | Keyword or skill. One query per line for several searches; each record is labelled with the query that found it. Required in search mode. |
| `urls` | URL list | - | Course, learning-path, chapter/lesson, topic or filtered search URLs. Required in URL mode. |
| `maxResults` | integer | `50` | Records per query (or per URL list). `1`-`500`. |
| `difficultyLevel` | select | all levels | `BEGINNER`, `INTERMEDIATE`, `ADVANCED` |
| `assetType` | select | `COURSE` | `COURSE`, `LEARNING_PATH`, `VIDEO`, or `ALL` |
| `sortBy` | select | `RELEVANCE` | `RELEVANCE`, `POPULARITY` (view count), `RECENCY` |
| `duration` | select | any length | Under 10 min, 10-30 min, 30-60 min, 1-2 h, 2-3 h, 3+ h |
| `software` | text | - | One LinkedIn software label, e.g. `Python`, `Microsoft Excel`, `SQL`, `ChatGPT` |
| `topic` | text | - | Extra words appended to every query to narrow it |
| `includeDetails` | boolean | `true` | Open each record's public page for the full detail surface. Turn off for a faster catalog snapshot. |
| `proxyConfiguration` | proxy | direct | Optional override. The actor connects directly by default and only escalates to a datacenter proxy if LinkedIn blocks a request. |

#### Example: beginner Python courses, most viewed

```json
{
  "mode": "search",
  "searchQuery": "Python machine learning",
  "difficultyLevel": "BEGINNER",
  "assetType": "COURSE",
  "sortBy": "POPULARITY",
  "maxResults": 100
}
```

#### Example: several skills in one run, card-only

```json
{
  "mode": "search",
  "searchQuery": "python\nleadership\nexcel",
  "assetType": "ALL",
  "maxResults": 50,
  "includeDetails": false
}
```

#### Example: read specific URLs

```json
{
  "mode": "byUrls",
  "urls": [
    {"url": "https://www.linkedin.com/learning/python-essential-training-18764650"},
    {"url": "https://www.linkedin.com/learning/paths/getting-started-with-python"},
    {"url": "https://www.linkedin.com/learning/python-quick-start-22667553/starting-with-python"}
  ]
}
```

### Output per record

Every record carries `sourceUrl`, `scrapedAt` and `recordType` (`course`, `learningPath` or `video`).

| Field | Notes |
|---|---|
| `title`, `description` | Course, path or chapter name and overview |
| `id`, `courseId` | Stable slug id; `courseId` is the numeric slug suffix when present |
| `courseUrl`, `sourceUrl`, `pathUrl`, `videoUrl` | Canonical public URLs |
| `thumbnailUrl` | Cover image (public, no Referer needed) |
| `assetType`, `recordType` | `COURSE`, `LEARNING_PATH`, `VIDEO` |
| `language` | Content language from the course page |
| `difficultyLevel` | `BEGINNER`, `INTERMEDIATE`, `ADVANCED` |
| `durationSeconds`, `durationFormatted`, `duration` | Total length in seconds, e.g. `1h 54m`, and rounded minutes |
| `instructorName`, `instructorUrl`, `instructor` | First instructor (legacy fields) |
| `instructorNames`, `instructors` | All listed instructors with profile and image URLs |
| `viewerCount` | LinkedIn's historical enrollment count |
| `ratingValue`, `ratingCount` | Aggregate rating and number of ratings |
| `skillsTaught` | Skills LinkedIn lists under "Skills you'll gain" |
| `topics` | Catalog subject tags |
| `categoryPath` | Breadcrumb path such as `Technology > Software Development > Programming Languages` |
| `syllabus` | Section titles, durations and descriptions |
| `chapters` | Chapter/lesson titles, URLs, durations, descriptions and free-preview flags |
| `lessonCount`, `freeLessonCount` | Counts from the public chapter list |
| `reviews` | Up to 3 public reviews with rating, text, author name, job title, profile URL and date |
| `certificate`, `certificateName` | Completion certificate when LinkedIn publishes it |
| `providerName`, `accessModel` | Course provider and access model (e.g. subscription) |
| `learningPathCourses`, `courseCount` | Ordered member courses for learning paths |
| `parentCourseTitle`, `parentCourseUrl` | Parent course for video chapters |
| `releaseDate` | ISO release date |
| `query` | The search query that produced the record |
| `popularityScore`, `learnerCount` | Legacy aliases: rating value and rating count |

Fields that LinkedIn does not publish for a record are omitted. The actor never writes `null`, `N/A`, `-1` or an empty string.

### Use cases

- **Curriculum planning** - compare what already exists for a skill before building internal training.
- **Skills-gap research** - export the skills and catalog categories across many queries.
- **Learning & development catalogs** - build a searchable catalog with duration, level, provider and rating.
- **Instructor discovery** - find who teaches in a subject and which courses they lead.
- **Free-preview planning** - rank courses by how many lessons LinkedIn exposes for free.
- **Catalog monitoring** - schedule the same queries and compare snapshots by `id` and `scrapedAt`.

### Limitations

- **50 cards per query and filter slice.** LinkedIn's guest catalog has no next-page control. The actor works around this by re-running the query across LinkedIn's own level, length and sort slices. On a broad course query this reached **about 97% of the total LinkedIn reported** (375 of 385 unique courses in testing); the run status says when a shortfall remains. Requesting more than the source exposes never invents rows.
- **No content-language filter.** LinkedIn's guest search has no language parameter; changing the locale only changes the page language. Each record still reports the course `language`.
- **Three reviews.** Course pages publish three review samples; the actor returns those, not the full review history.
- **No instructor course-list page.** Instructor profile pages do not expose a public course list without a login, so there is no instructor mode.
- **Video records are chapters.** `assetType=VIDEO` returns individual lessons/chapters with a link to their parent course, which is what LinkedIn's guest catalog exposes.
- **Detail pages are extra requests.** `includeDetails=true` reads one page per record. Use `false` for large scheduled snapshots or set a smaller `maxResults`.
- **Instructor profile links.** The profile URLs come from LinkedIn's public course data and open in a browser. LinkedIn sometimes answers a bare command-line request for `/in/...` with its `999` bot check, so an automated HEAD check can fail even though the link is valid.

### FAQ

**Do I need a LinkedIn account, a Learning subscription or cookies?**
No. Every request reads a public page that a signed-out visitor can open. There is no cookie input and no login field.

**Why did I get fewer records than `maxResults`?**
Either LinkedIn's catalog has fewer matches for that query and filter, or the guest page stopped exposing more. The run status reports the shortfall; split the topic into narrower queries for wider coverage.

**Why do some records have fewer fields?**
Not every public course page publishes every field. Skills, syllabus, reviews, certificate data and learner counts are optional on LinkedIn's side and are omitted when absent.

**What is the difference between a course and a learning path?**
A course is one multi-lesson unit. A learning path is a curated sequence of courses; its record includes the ordered member courses.

**What is the difference between a video and a course?**
A video record is a single chapter or lesson inside a course, with `parentCourseTitle` and `parentCourseUrl` linking back to the course.

**What is `popularityScore`?**
It is the legacy name for LinkedIn's aggregate rating value. New integrations should use `ratingValue`; `learnerCount` is the legacy name for `ratingCount`, not the enrollment count. The enrollment count is `viewerCount`.

**Does `software` accept more than one value?**
LinkedIn applies only the first software label per request. Use one label per run; a second label in the same field is ignored by the source.

**I used to set `language`. Is my saved task broken?**
No. The old `language` field is still accepted for saved tasks, but it never changed LinkedIn's results. The run status explains that it is no longer a filter, and every enriched record reports the course's real `language`.

**Is this affiliated with LinkedIn?**
No. It reads LinkedIn Learning's public catalog pages and is not operated or endorsed by LinkedIn.

### Local development

```bash
python -m pytest tests/ -q
apify run --purge --input-file input.json
```

The actor uses `httpx` only; no browser, no login, no cookie.

# Actor input Schema

## `mode` (type: `string`):

What to collect. 'Search the catalog' needs searchQuery. 'Read specific URLs' needs urls. 'Browse learning paths' reads the public all-paths page.

## `searchQuery` (type: `string`):

Keyword or skill to search for, e.g. 'Python machine learning' or 'project management'. Put one query per line to run several searches in one run; each line is labelled in the output.

## `urls` (type: `array`):

Course, learning-path, chapter/lesson, topic or filtered search URLs. Used only in 'Read specific LinkedIn Learning URLs' mode.

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

Maximum records per query (or per URL list in URL mode). LinkedIn's guest catalog exposes at most 50 cards per filter slice; the actor re-runs the query across its own filters to collect more.

## `difficultyLevel` (type: `string`):

Filter by skill level. 'All levels' returns every level.

## `assetType` (type: `string`):

Kind of learning content. 'All types' mixes courses, learning paths and videos.

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

Order of the results: LinkedIn's best match, view count, or newest release.

## `duration` (type: `string`):

Filter by total course length. 'Any length' returns every length.

## `software` (type: `string`):

Keep only content that teaches this LinkedIn Learning software label, e.g. 'Python', 'Microsoft Excel', 'SQL' or 'ChatGPT'. LinkedIn applies only one software label per request.

## `topic` (type: `string`):

Optional words appended to every search query to narrow it, e.g. 'AWS' or 'for beginners'.

## `includeDetails` (type: `boolean`):

Open each record's public page for description, skills, syllabus, chapters, free-preview flags, reviews, rating, learner count, provider and certificate. Turn off for a faster, cheaper catalog snapshot.

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

Optional. The actor connects directly by default and only escalates to a datacenter proxy if LinkedIn blocks a request. Change this only if you need a specific proxy setup.

## Actor input object example

```json
{
  "mode": "search",
  "searchQuery": "Python machine learning",
  "urls": [],
  "maxResults": 3,
  "difficultyLevel": "",
  "assetType": "COURSE",
  "sortBy": "RELEVANCE",
  "duration": "",
  "includeDetails": false,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

## `records` (type: `string`):

Dataset containing every course, learning-path and video record collected by this 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 = {
    "mode": "search",
    "searchQuery": "Python machine learning",
    "urls": [],
    "maxResults": 3,
    "difficultyLevel": "",
    "assetType": "COURSE",
    "sortBy": "RELEVANCE",
    "duration": "",
    "includeDetails": false,
    "proxyConfiguration": {
        "useApifyProxy": false
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("nimbryxo-studio/linkedin-learning-course-and-learning-path-finder").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 = {
    "mode": "search",
    "searchQuery": "Python machine learning",
    "urls": [],
    "maxResults": 3,
    "difficultyLevel": "",
    "assetType": "COURSE",
    "sortBy": "RELEVANCE",
    "duration": "",
    "includeDetails": False,
    "proxyConfiguration": { "useApifyProxy": False },
}

# Run the Actor and wait for it to finish
run = client.actor("nimbryxo-studio/linkedin-learning-course-and-learning-path-finder").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 '{
  "mode": "search",
  "searchQuery": "Python machine learning",
  "urls": [],
  "maxResults": 3,
  "difficultyLevel": "",
  "assetType": "COURSE",
  "sortBy": "RELEVANCE",
  "duration": "",
  "includeDetails": false,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}' |
apify call nimbryxo-studio/linkedin-learning-course-and-learning-path-finder --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,nimbryxo-studio/linkedin-learning-course-and-learning-path-finder"
        }
    }
}
```

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/HkEpjBr9pdGuzJZQF/builds/iQnLqjCqc174ezrSc/openapi.json
