# LinkedIn Learning Courses Scraper (`automation-lab/linkedin-learning-course-catalog`) Actor

Search public LinkedIn Learning courses and export structured course, instructor, skills, level, duration, and catalog metadata.

- **URL**: https://apify.com/automation-lab/linkedin-learning-course-catalog.md
- **Developed by:** [Stas Persiianenko](https://apify.com/automation-lab) (community)
- **Categories:** Education
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $4.32 / 1,000 course records

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?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — 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 Learning Courses Scraper

Search public **linkedin learning courses** and export structured catalog records for skills mapping, content discovery, and course-catalog comparison.

The Actor accepts skill queries, public LinkedIn Learning search or topic pages, and individual course URLs. It returns one deduplicated row per course with title, instructor, level, duration, release date, description, skills, catalog topics, popularity signals, and the canonical course URL.

It does not access course playback, learner accounts, completion history, private recommendations, or other account-only data.

### What does LinkedIn Learning Courses Scraper do?

The Actor turns logged-out LinkedIn Learning catalog pages into integration-ready JSON, CSV, Excel, XML, RSS, or Parquet data through Apify Dataset exports.

A run can:

- search the public course catalog with one or more skill queries;
- discover courses from a public search, topic, or all-courses URL;
- extract a known public course URL directly;
- fetch each course page for richer curriculum metadata;
- deduplicate courses found by multiple inputs;
- cap output with `maxItems` for predictable scheduled jobs;
- save observation time and discovery context for later comparisons.

The default detailed mode uses public SSR HTML, not a browser or login.

### Who is it for?

**Learning and development teams** build skill-to-course maps and compare training coverage across recurring snapshots.

**Course aggregators** discover public offerings and enrich known course URLs with normalized metadata.

**Content strategists** compare instructors, levels, durations, topics, and release dates for a subject area.

**Data teams** schedule catalog refreshes and send typed rows to a warehouse, spreadsheet, or BI workflow.

**Researchers** create bounded samples of public course offerings without manually copying catalog cards.

### Why use this Actor?

- One schema covers query discovery and direct course-page enrichment.
- Detail mode adds descriptions, skills, level, topics, ratings, and outline counts.
- Multiple queries are deduplicated by canonical course URL.
- Invalid hosts and unsupported URL shapes fail closed.
- Transient requests use bounded retries rather than an unbounded crawl.
- Direct public HTTP keeps runtime and memory lower than browser automation.
- Every saved course is individually metered; duplicate and failed rows are not charged as courses.

### What data can I extract?

| Field | Meaning |
| --- | --- |
| `courseId` | Stable slug from the canonical course URL |
| `title` | Public course title |
| `courseUrl` | Canonical LinkedIn Learning course URL |
| `instructors` | Instructor names |
| `instructorUrls` | Public profile URLs linked by the course page |
| `duration` | Displayed course duration |
| `level` | Displayed skill level |
| `releasedDate` | Source release date text |
| `description` | Public course description |
| `skills` | Skills listed on the course page |
| `topics` | Catalog breadcrumb topics |
| `viewerCount` | Viewer count shown on a discovery card, when available |
| `likedByCount` | Public liked-by count, when available |
| `rating` | Public average rating, when available |
| `ratingCount` | Public rating count, when available |
| `lessonCount` | Number of exposed outline lessons |
| `imageUrl` | Public course cover image URL |
| `discoverySource` | Input page that produced the record |
| `searchQuery` | Associated query, or `null` for URL-only input |
| `scrapedAt` | ISO 8601 observation time |

Fields can be `null` when LinkedIn does not expose that value on the requested public page.

### How to scrape LinkedIn Learning courses

1. Open the Actor input in Apify Console.
2. Enter one or more skill phrases in **Search queries**.
3. Optionally add public course, topic, courses-list, or search URLs.
4. Choose the maximum number of unique courses.
5. Keep **Fetch course details** enabled for skills, descriptions, levels, topics, and rating signals.
6. Start the run.
7. Open the Dataset tab or export the results in your preferred format.

A small first run is recommended before scheduling a larger recurring catalog refresh.

### Input parameters

#### `queries`

An array of skill or topic searches, such as `python`, `data analysis`, or `artificial intelligence`.

Each query returns the public cards LinkedIn exposes to logged-out visitors. Several queries can be supplied in one run and overlapping courses are deduplicated.

#### `startUrls`

Optional public URLs under `linkedin.com/learning`:

- a course URL, for example `/learning/python-essential-training-18764650`;
- a topic URL, for example `/learning/topics/python`;
- a search URL containing its filters;
- the public `/learning/courses` page.

Other domains and unrelated LinkedIn URL types are rejected.

#### `maxItems`

Maximum unique course records to save, from 1 to 200. The default is 25.

#### `includeCourseDetails`

When `true`, the Actor opens each public course page and returns the richest supported schema. When `false`, it emits faster catalog-card snapshots; detail-only fields are empty.

#### `maxConcurrency`

Detail-page requests processed in parallel, from 1 to 10. The default of 5 balances throughput with source reliability.

### Example input: search a skill

```json
{
  "queries": ["python"],
  "maxItems": 10,
  "includeCourseDetails": true,
  "maxConcurrency": 5
}
```

### Example input: enrich one course

```json
{
  "queries": [],
  "startUrls": [
    { "url": "https://www.linkedin.com/learning/python-essential-training-18764650" }
  ],
  "maxItems": 1,
  "includeCourseDetails": true,
  "maxConcurrency": 1
}
```

### Example output

The following shortened record reflects current public output:

```json
{
  "courseId": "python-essential-training-18764650",
  "title": "Python Essential Training",
  "courseUrl": "https://www.linkedin.com/learning/python-essential-training-18764650",
  "instructors": ["Ryan Mitchell"],
  "duration": "4h 23m",
  "level": "Beginner",
  "releasedDate": "1/25/2023",
  "skills": ["Python (Programming Language)"],
  "topics": ["Technology", "Software Development", "Programming Languages"],
  "viewerCount": 647899,
  "likedByCount": 18609,
  "rating": 4.7,
  "ratingCount": 17506,
  "searchQuery": "python",
  "scrapedAt": "2026-09-02T06:10:00.000Z"
}
```

Counts and course metadata are observations and may change between runs.

### How much does it cost to extract LinkedIn Learning courses?

The Actor uses pay-per-event pricing:

- **Start:** $0.005 once per run.
- **Course record:** $0.0072 per course at the BRONZE tier.

At BRONZE, a run saving 10 courses costs about **$0.077**, 25 courses about **$0.185**, and 100 courses about **$0.725**, including one start event. Higher subscription tiers receive lower per-course prices automatically.

Only unique valid course records saved to the default dataset trigger the course event. Failed pages, rejected records, and duplicates do not trigger a course-record charge. Apify platform compute remains subject to the account's normal plan terms.

### Catalog refresh and comparison workflow

For recurring skills mapping:

1. Keep a stable set of queries in an Apify Task.
2. Enable detailed records so course metadata remains comparable.
3. Schedule the Task weekly or monthly.
4. Export each Dataset to your warehouse or trigger a webhook.
5. Compare rows by `courseId` and use `scrapedAt` as the observation timestamp.
6. Detect additions, removals from the visible sample, or changed metadata downstream.

The Actor produces observations; it does not maintain historical state or send alerts itself.

### Integrations

Dataset rows can flow to:

- Google Sheets or Microsoft Excel;
- Make, Zapier, n8n, or webhooks;
- BigQuery, Snowflake, PostgreSQL, or object storage;
- Python, JavaScript, or BI notebooks;
- an LLM or retrieval workflow for course discovery;
- a catalog diff pipeline keyed by `courseId`.

Use Apify integrations or the API rather than downloading files manually for recurring jobs.

### Run through the Apify API

Replace `YOUR_TOKEN` with an Apify API token stored securely.

#### cURL

```bash
curl -X POST \
  "https://api.apify.com/v2/acts/automation-lab~linkedin-learning-course-catalog/runs?token=YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"queries":["python"],"maxItems":10,"includeCourseDetails":true}'
```

#### JavaScript

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

const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('automation-lab/linkedin-learning-course-catalog').call({
    queries: ['python'],
    maxItems: 10,
    includeCourseDetails: true,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items);
```

#### Python

```python
import os
from apify_client import ApifyClient

client = ApifyClient(os.environ['APIFY_TOKEN'])
run = client.actor('automation-lab/linkedin-learning-course-catalog').call(
    run_input={
        'queries': ['python'],
        'maxItems': 10,
        'includeCourseDetails': True,
    }
)
items = client.dataset(run['defaultDatasetId']).list_items().items
print(items)
```

### Use with Apify MCP

Add the Actor to Claude Code:

```bash
claude mcp add --transport http apify \
  "https://mcp.apify.com?tools=automation-lab/linkedin-learning-course-catalog"
```

#### Claude Desktop, Cursor, and VS Code setup

Claude Desktop, Cursor, and VS Code can use this HTTP MCP configuration:

```json
{
  "mcpServers": {
    "apify": {
      "url": "https://mcp.apify.com?tools=automation-lab/linkedin-learning-course-catalog"
    }
  }
}
```

Example prompts:

- “Search LinkedIn Learning for data analysis courses and return title, level, duration, and skills.”
- “Extract this public LinkedIn Learning course URL and summarize its curriculum metadata.”
- “Build a 25-course AI skills dataset that I can compare with next month's run.”

### Reliability and retry behavior

The Actor verifies successful HTML responses and retries transient network failures, HTTP 408/425/429, and temporary 5xx responses up to three times.

It does not blindly retry deterministic malformed input or unsupported URLs. If discovery finds course links but every detail record fails validation, the run fails instead of reporting a misleading successful empty dataset.

No proxy or browser fallback is enabled by default. This keeps cost predictable for the currently available logged-out SSR pages.

### Limits

- A public search, topic, or all-courses page currently exposes up to about 50 logged-out course cards.
- `start` or page-number query parameters do not provide verified public pagination.
- Use several specific queries or topic URLs for broader coverage, up to 200 deduplicated rows per run.
- The Actor does not promise a complete global LinkedIn Learning inventory from one broad query.
- Some public cards omit viewer counts or other optional fields.
- Detail-free mode cannot return detail-only fields.
- Source HTML and public availability can change.
- Results use the public US-English surface; no private localization guarantee is made.

### Responsible use and legality

Use the Actor only for public data you are authorized to collect and process.

Review LinkedIn's terms, applicable database and copyright rules, and privacy laws for your use case. Do not use the Actor to circumvent login controls, access paid playback, reconstruct copyrighted course content, profile private learners, or make decisions about people without an appropriate lawful basis.

Store only the fields you need, limit retention, secure exports, and honor applicable deletion or objection requests.

### Troubleshooting

#### Why did the run return no courses?

Check spelling and try a broader real skill phrase. Also confirm that a supplied URL opens publicly in a logged-out browser. A naturally empty public result is reported without fabricated rows.

#### Why was my URL rejected?

Only `linkedin.com/learning` course, search, topic, browse, and courses-list URLs are accepted. Profile, company, job, login, and external URLs are outside this Actor's contract.

#### Why are some fields null?

LinkedIn does not expose every signal on every public page. Enable `includeCourseDetails` for the richest result, while treating nullable fields as expected source variability.

#### Why is a known course missing from a broad search?

Logged-out search pages expose a bounded ranked sample. Supply the exact course URL, a more specific query, or a relevant topic URL.

#### What should I do after a transient source error?

Inspect the run log and source status. The Actor already performs bounded retries, so repeated immediate restarts are usually less useful than retrying later with a small input.

### Related Automation Lab Actors

- [Coursera Course Scraper](https://apify.com/automation-lab/coursera-course-scraper) for another learning provider.
- [Domestika Course Catalog Scraper](https://apify.com/automation-lab/domestika-course-catalog-scraper) for creative-course catalog research.
- [Microsoft Learn Catalog Scraper](https://apify.com/automation-lab/microsoft-learn-catalog-scraper) for technical learning paths and modules.

Choose this Actor when the record of interest is specifically a public LinkedIn Learning course rather than a LinkedIn profile, company, post, or job.

### FAQ

#### Does it need a LinkedIn account or cookies?

No. The shipped workflow uses the public logged-out course catalog and public course pages.

#### Does it download videos or course lessons?

No. It extracts public catalog metadata only and does not access playback.

#### Can I monitor new or changed courses?

Yes, by scheduling stable inputs and comparing Dataset snapshots downstream. The Actor records `courseId` and `scrapedAt`, but does not store history or send alerts itself.

#### Can I export to CSV or Excel?

Yes. Use the Dataset export menu or API with CSV, XLSX, JSON, XML, RSS, or Parquet formats supported by Apify.

#### Are duplicate courses charged twice?

No. Courses are deduplicated by canonical URL before extraction and charging within a run.

#### Can I disable detail requests?

Yes. Set `includeCourseDetails` to `false` for a faster catalog-card snapshot. Fields available only on detail pages will be empty.

# Actor input Schema

## `queries` (type: `array`):

Skills or topics to search in the public LinkedIn Learning course catalog. Add several queries to build a deduplicated comparison dataset.

## `startUrls` (type: `array`):

Optional public LinkedIn Learning course, topic, courses-list, or search URLs. Course URLs are extracted directly; listing URLs are used for discovery.

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

Maximum number of unique course records saved across all queries and URLs.

## `includeCourseDetails` (type: `boolean`):

Fetch each public course page to add descriptions, skills, level, topics, instructor links, ratings, and lesson counts. Disable for a faster catalog-card snapshot.

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

Number of public course detail pages fetched in parallel. Keep the default for reliable recurring runs.

## Actor input object example

```json
{
  "queries": [
    "python",
    "data analysis"
  ],
  "startUrls": [
    {
      "url": "https://www.linkedin.com/learning/topics/python"
    }
  ],
  "maxItems": 20,
  "includeCourseDetails": true,
  "maxConcurrency": 5
}
```

# Actor output Schema

## `dataset` (type: `string`):

Dataset containing all extracted LinkedIn Learning courses.

# 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 = {
    "queries": [
        "python",
        "data analysis"
    ],
    "startUrls": [
        {
            "url": "https://www.linkedin.com/learning/topics/python"
        }
    ],
    "maxItems": 20,
    "includeCourseDetails": true,
    "maxConcurrency": 5
};

// Run the Actor and wait for it to finish
const run = await client.actor("automation-lab/linkedin-learning-course-catalog").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 = {
    "queries": [
        "python",
        "data analysis",
    ],
    "startUrls": [{ "url": "https://www.linkedin.com/learning/topics/python" }],
    "maxItems": 20,
    "includeCourseDetails": True,
    "maxConcurrency": 5,
}

# Run the Actor and wait for it to finish
run = client.actor("automation-lab/linkedin-learning-course-catalog").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 '{
  "queries": [
    "python",
    "data analysis"
  ],
  "startUrls": [
    {
      "url": "https://www.linkedin.com/learning/topics/python"
    }
  ],
  "maxItems": 20,
  "includeCourseDetails": true,
  "maxConcurrency": 5
}' |
apify call automation-lab/linkedin-learning-course-catalog --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,automation-lab/linkedin-learning-course-catalog"
        }
    }
}

```

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/aGI89DHXH6XTiEU2Q/builds/ovL9wypBMwszVpE2H/openapi.json
