# Egghead Courses Scraper (`crawlerbros/egghead-scraper`) Actor

Scrape egghead.io's programming course catalog. Search or browse by content type, look up a course's full syllabus by slug, or list every lesson taught by a specific instructor.

- **URL**: https://apify.com/crawlerbros/egghead-scraper.md
- **Developed by:** [Crawler Bros](https://apify.com/crawlerbros) (community)
- **Categories:** Automation, Developer tools, Videos
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $3.00 / 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/platform/actors/running/actors-in-store#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

## Egghead Scraper

Scrape [egghead.io](https://egghead.io) — a curated catalog of concise, expert-taught programming courses, lessons, articles, podcasts, talks, and tips covering React, TypeScript, Node, AWS, testing, and modern web development. Search or browse the full catalog, pull a course's complete syllabus, or list every lesson from a specific instructor. No login, no API key, no cookies required.

### What this actor does

- **Three modes:** `search` (full-text search / catalog browse), `byCourseSlugs` (full course syllabus lookup), `byInstructor` (all lessons by one teacher)
- **8 content types:** courses, lessons, articles, podcasts, talks, tips, case studies, guides
- **Filters:** tag/topic, instructor name, free-only content, sort order
- **Full syllabi:** course lookups include the ordered lesson list with per-lesson duration and free/pro status
- **Instructor profiles:** bio, Twitter, website, and avatar attached to every lesson in `byInstructor` mode
- **Empty fields are omitted** — every record only contains data egghead actually returned

### Output fields

#### Search / browse mode (`mode=search`)

- `contentType` — `course`, `lesson`, `article`, `podcast`, `talk`, `tip`, `caseStudy`, or `guide`
- `contentId`
- `title`, `summary`, `description`
- `slug`, `sourceUrl`
- `imageUrl`
- `instructorName`, `instructorSlug`, `instructorUrl`, `instructorAvatarUrl`
- `contributors`
- `primaryTag`, `primaryTagImageUrl`, `tags[]`
- `freeForever`
- `publishedAt`, `updatedAt`
- `recordType`, `scrapedAt`

#### Course lookup mode (`mode=byCourseSlugs`)

- `courseId`, `title`, `slug`, `sourceUrl`
- `tagline`, `summary`, `description`
- `durationSeconds`, `freeForever`, `published`, `courseState` (`published` / `retired`), `accessState` (`free` / `pro`)
- `imageUrl`
- `ratingOutOf5`, `ratingCount`
- `instructorName`, `instructorSlug`, `instructorUrl`, `instructorAvatarUrl`, `instructorTwitter`, `instructorWebsite`
- `primaryTag`, `tags[]`, `frameworks[]`, `libraries[]`, `tools[]`, `languages[]`, `platforms[]`, `topics[]`, `skillsets[]`, `skillLevels[]`
- `publishedAt`, `updatedAt`
- `lessons[]` — ordered syllabus: `{ title, slug, sourceUrl, itemType, itemState, durationSeconds, freeForever, isProContent, mediaUrl }` (`itemType`/`mediaUrl` are only present for non-lesson syllabus entries, e.g. an embedded `podcast` episode — most syllabus items are plain lessons and omit them)
- `lessonCount`
- `recordType`, `scrapedAt`

#### Instructor lessons mode (`mode=byInstructor`)

- `lessonId`, `title`, `slug`, `sourceUrl`, `summary`, `transcript`
- `durationSeconds`, `freeForever`, `isProContent`, `lessonState` (`published` / `retired`), `playsCount`
- `thumbnailUrl`, `hlsUrl`, `dashUrl` (video stream URLs — omitted for login-gated Pro lessons)
- `publishedAt`
- `primaryTag`, `frameworks[]`, `libraries[]`, `tools[]`
- `instructorName`, `instructorSlug`, `instructorUrl`, `instructorBio`, `instructorTwitter`, `instructorWebsite`, `instructorAvatarUrl`
- `recordType`, `scrapedAt`

### Input

| Field | Type | Default | Description |
|---|---|---|---|
| `mode` | string | `search` | `search` / `byCourseSlugs` / `byInstructor` |
| `searchQuery` | string | `react` | Free-text query (mode=search) |
| `contentType` | string | `course` | One of the 8 content types (mode=search) |
| `tag` | string | – | Filter by tag/topic, e.g. `typescript` |
| `instructorName` | string | – | Exact instructor full name (mode=search) |
| `sortBy` | string | `relevance` | `relevance` / `newest` / `oldest` (mode=search) |
| `freeOnly` | boolean | `false` | Only include free (non-Pro) content — works in every mode (see FAQ) |
| `courseSlugs` | array | – | Course slugs or URLs (mode=byCourseSlugs) |
| `instructorSlug` | string | – | Instructor slug or URL (mode=byInstructor) |
| `maxItems` | integer | `30` | Hard cap on emitted records (1–500) |

#### Example: search for React courses

```json
{
  "mode": "search",
  "searchQuery": "react",
  "contentType": "course",
  "sortBy": "newest",
  "maxItems": 30
}
```

#### Example: full syllabus for one or more courses

```json
{
  "mode": "byCourseSlugs",
  "courseSlugs": ["the-beginner-s-guide-to-react"]
}
```

#### Example: every lesson by an instructor

```json
{
  "mode": "byInstructor",
  "instructorSlug": "kent-c-dodds",
  "freeOnly": true,
  "maxItems": 100
}
```

### Use cases

- **Curriculum research** — discover the newest courses on a framework before recommending a learning path
- **Content aggregation** — build a technology-focused learning feed (React, AWS, testing, etc.)
- **Instructor research** — audit a teacher's full catalog and free vs. Pro split
- **EdTech competitive analysis** — track course counts, durations, and tags across topics
- **Link auditing** — verify course/lesson URLs before publishing a resource list

### FAQ

**Do I need an egghead account or API key?** No. Every mode uses egghead's own public, unauthenticated endpoints — the same ones the egghead.io website itself calls from your browser.

**What's the difference between "search" and "browse"?** Leave `searchQuery` blank (or set it to `*`) to browse the full catalog for a `contentType` sorted however you like, without a text query.

**Why do some lessons show `isProContent: true` but no `hlsUrl`/`dashUrl`?** Egghead's paid ("Pro") lessons keep their metadata (title, summary, duration, tags) public, but the playable video stream is login-gated, so `hlsUrl`/`dashUrl` are omitted for Pro lessons. `transcript` is a separate field — egghead publishes it for most lessons regardless of Pro status, but not all (some newer Pro lessons omit it too); when present it's always included. Set `freeOnly: true` (mode=byInstructor) to skip Pro content entirely.

**Does `freeOnly` work in `search` mode?** Yes. Egghead's public search index (the same one powering the website's own search box) is filterable by access tier server-side even though it doesn't return a free/Pro field on individual hits — so `freeOnly: true` in `search` mode drops Pro-only results, but the emitted records won't carry an `isProContent`/`freeForever` field (the upstream index doesn't expose the value itself, only the ability to filter by it). The per-instructor lessons endpoint (`mode=byInstructor`) does return the flag directly, so records from that mode do carry `freeForever`/`isProContent`.

**What does a course slug look like?** Most newer egghead course slugs end in a short hash, e.g. `up-and-running-with-tanstack-router-and-react~mhvqt`; older courses use a plain slug with no hash, e.g. `the-beginner-s-guide-to-react`. You can paste the full course URL instead — the actor extracts the slug automatically either way.

**How is `contentType: caseStudy` different from `article`?** Both are text content, but egghead tags in-depth "how I learned to code" narratives as case studies and shorter dev-news pieces as articles.

**How fresh is the data?** Live — every request hits egghead's production API directly, so results reflect the catalog at the moment the actor runs.

# Actor input Schema

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

What to fetch.

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

Free-text query matched against title, description, instructor name, and contributors. Leave blank (or use `*`) to browse the catalog without a text query.

## `contentType` (type: `string`):

Restrict search/browse results to a single content type.

## `tag` (type: `string`):

Restrict to items tagged with this topic/technology, e.g. `react`, `typescript`, `aws`, `node`.

## `instructorName` (type: `string`):

Exact instructor full name, e.g. `Kent C. Dodds`, `John Lindquist`.

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

How to order search/browse results.

## `freeOnly` (type: `boolean`):

Only include free (non-Pro) content. For mode=byInstructor this checks each lesson's own "free forever" flag. For mode=search this is applied server-side against egghead's access-tier index (the free/Pro flag itself isn't returned on individual search hits, so it filters results but doesn't add a field to the record).

## `courseSlugs` (type: `array`):

Egghead course slugs (some end in a short hash, some don't), e.g. `the-beginner-s-guide-to-react`, `up-and-running-with-tanstack-router-and-react~mhvqt`, or full course URLs.

## `instructorSlug` (type: `string`):

Egghead instructor slug, e.g. `kent-c-dodds`, `john-lindquist`, or full instructor URL.

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

Hard cap on emitted records.

## Actor input object example

```json
{
  "mode": "search",
  "searchQuery": "react",
  "contentType": "course",
  "sortBy": "relevance",
  "freeOnly": false,
  "courseSlugs": [],
  "maxItems": 30
}
```

# Actor output Schema

## `content` (type: `string`):

Dataset containing all scraped egghead.io items.

# 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": "react",
    "contentType": "course",
    "sortBy": "relevance",
    "freeOnly": false,
    "courseSlugs": [],
    "maxItems": 30
};

// Run the Actor and wait for it to finish
const run = await client.actor("crawlerbros/egghead-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 = {
    "mode": "search",
    "searchQuery": "react",
    "contentType": "course",
    "sortBy": "relevance",
    "freeOnly": False,
    "courseSlugs": [],
    "maxItems": 30,
}

# Run the Actor and wait for it to finish
run = client.actor("crawlerbros/egghead-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 '{
  "mode": "search",
  "searchQuery": "react",
  "contentType": "course",
  "sortBy": "relevance",
  "freeOnly": false,
  "courseSlugs": [],
  "maxItems": 30
}' |
apify call crawlerbros/egghead-scraper --silent --output-dataset

```

## MCP server setup

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