# Coursera Course Catalog & Reviews Scraper (`midnight_static/coursera-course-scraper`) Actor

Online course catalog data from Coursera. 57 fields per course: enrollment count, page views last month, up to 20 learner reviews with stars and dates, full star ratings split, module-by-module syllabus and curriculum, instructors, skills, languages. From Coursera's sitemap: 21,985 course URLs.

- **URL**: https://apify.com/midnight\_static/coursera-course-scraper.md
- **Developed by:** [Midnight Static](https://apify.com/midnight_static) (community)
- **Categories:** Education, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.75 / 1,000 courses

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

## Coursera Course Scraper

Coursera's course catalogue, with two numbers most catalogue scrapers leave out: **how many people enrolled** and **how many viewed the page last month**, plus the learner reviews. 57 fields per course, as JSON, CSV or Excel.

### Five things that are different here

**1. Enrollment and traffic, not just a catalogue listing.**
`totalEnrollmentCount` and `pageViewsInLastMonthCount` come straight off the course page. Andrew Ng's machine learning course: 1,248,467 enrolled, 1,718,986 page views in the last month. That is how big the course actually is, not just what it is called.

**2. The full rating split, not an average.**
A 4.89 from 32,913 ratings is not the same thing as a 4.89 from nine. You get the count for every star level, the separate instructor rating and its count, and Coursera's own content satisfaction score with its sample size.

**3. The syllabus, lecture by lecture.**
Module names with their total duration, how many lectures and how many assessments in each, the total course minutes, and whether any item is AI-graded. That is course structure you can compare across providers, not a marketing blurb.

**4. Reviews, with the star and the date attached.**
Up to 20 learner reviews per course: the text, the star rating, the date and the two-letter initials Coursera itself displays. An aggregate of 4.89 tells you people liked it; the reviews tell you what they liked, and the dates tell you whether the praise is current. Measured across 43 courses: 304 reviews, median 18 where a course has any. Turn them off with one switch if you only want the numbers.

**5. It walks Coursera's own sitemap.**
Coursera's `robots.txt` closes every search and query URL, and the catalogue pages only ever return the same twelve featured courses. So this Actor enumerates from the sitemap Coursera publishes itself. Measured on 18 September 2026: **21,985 course URLs** in one pass.

### What you get

**Identity**
Course id, name, slug, URL, product type, course type, status, difficulty level, launch date.

**Description**
Full description, estimated workload in the provider's own words, learning objectives and recommended background as clean text.

**Provider and instructors**
Partner names and count. Per instructor: name, title, department, how many courses they teach and how many learners they have reached. Plus the top-instructor count and the total learners reached across all of them.

**Reach and reception**
Enrollment count, page views in the last month, rating average and count, five separate star counts, instructor rating and count, content satisfaction score and its sample size.

**Reviews**
Up to 20 per course, each with star rating, review text, review date and the initials Coursera displays. Plus a review count, the average of the sampled stars and the most recent review date, so you can sort by freshness.

**Structure**
Module count, per-module duration and lecture and assessment counts, total lectures, total assessments, total material items, total minutes, and an AI-grading flag.

**Skills and languages**
Skill tags with a count, the subset flagged as tools or software, primary languages, and how many subtitle, translated and dubbed languages exist.

**Provenance**
`sourceUrl`, `fetchedAt`, `httpStatus` and `filledFieldCount` travel with every record.

### Typical uses

Sizing a topic before building a course of your own: enrollment and monthly views tell you where demand actually is. Benchmarking your catalogue against a competing provider. Finding which skills the market is teaching and at what depth. Tracking a provider's reach over time by running on a schedule.

### Example record (trimmed)

```json
{
  "name": "Advanced Portfolio Construction and Analysis with Python",
  "partnerNames": ["EDHEC Business School"],
  "difficultyLevel": "INTERMEDIATE",
  "totalEnrollmentCount": 26411,
  "pageViewsInLastMonthCount": 10958,
  "ratingAverage": 4.745,
  "ratingCount": 514,
  "fiveStarCount": 392,
  "oneStarCount": 6,
  "contentSatisfactionScore": 96.4,
  "reviewCount": 20,
  "reviewSampleAverage": 4.85,
  "latestReviewAt": "2026-04-13T00:00:00.000Z",
  "reviews": [
    { "rating": 5, "authorInitials": "AC", "reviewedAt": "2024-10-08T00:00:00.000Z",
      "comment": "great course. Nice explanation even if you are not familiar with certain mathematical concepts" }
  ],
  "moduleCount": 4,
  "lectureCount": 38,
  "totalDurationMinutes": 733,
  "containsAiGrading": false,
  "skillCount": 14,
  "subtitleLanguageCount": 12,
  "filledFieldCount": 48,
  "courseUrl": "https://www.coursera.org/learn/advanced-portfolio-construction-python"
}
```

### Input

Pick which parts of the sitemap to walk: courses, specializations, professional certificates, guided projects. Add `urlKeywords` to keep only URLs containing a word such as `python`. Add exact course URLs if you already know them.

`includeReviews` is on by default and `reviewLimit` caps how many reviews per course are kept, up to the 20 Coursera exposes.

`maxItems` caps how many records are written so a long run cannot produce an unexpected charge.

### Failures are loud

The run fails with an explanation rather than finishing quietly. If the page structure changes, you get a parse error naming the affected pages, not an empty dataset. Counters are reconciled every run: sitemap requests, URLs found, filtered out, pages fetched, courses written, duplicates, robots-blocked, HTTP failures, parse failures. The breakdown goes to `RUN_STATS`, every skipped page to `SKIPPED`.

### Limits and compliance

`robots.txt` is checked before every request and a disallowed path is never fetched. Coursera closes `/search` and every `?query=` URL, so this Actor never attempts them; when one is supplied it is reported as `ROBOTS_DISALLOW` and skipped. If `robots.txt` itself cannot be read, the run stops before making a single data request.

**Reviews carry no learner identifier.** Coursera publishes review authors as two initials only, never a full name; measured on 18 September 2026 across 120 reviews, 120 of 120 were initials. What Coursera does attach is a numeric learner id inside the review key, and that id is stable across courses, so it is the one field that would let anyone follow a person around the catalogue. **It is dropped before the record is written.** You get the rating, the text, the date and the initials as shown on the public page. Set `includeReviews` to false to leave reviews out entirely.

Instructors appear in their professional capacity, as the authors of the product: name, title, department and teaching statistics. No photos, no personal contact details.

### Pricing

**$2.50 per 1,000 courses, everything included.** No start fee, no minimum charge per run, no separate platform usage line.

Paid Apify plans pay less, automatically — there is nothing to apply for: Bronze $2.25, Silver $2.00, Gold and above $1.75 per 1,000 courses. The Free plan price is unchanged at $2.50.

You are charged per course written. Pages that produce nothing, duplicates and pages blocked by robots.txt are reported in the run statistics and cost you nothing.

### Measured performance

Run on 18 September 2026 from Apify's own network, no proxy:

| | |
|---|---|
| Course URLs found in the sitemap | 21,985 |
| Pages fetched | 43 |
| Records written | 43 |
| Duplicates | 0 |
| Parse failures | 0 |
| HTTP failures | 0 |
| Fields populated per course | 47 of 57 (median) |
| Reviews collected | 304 |
| Courses carrying reviews | 22 of 43 |
| Reviews per course, where present | 18 (median), 20 (max) |

The 21 courses that returned no reviews have a median rating count of **zero** — Coursera holds no reviews for them, so there is nothing to miss. Courses that do carry reviews average 27,927 enrollments; the ones that do not average 501. Review text runs to 199 characters, which is the length Coursera publishes on the course page.

# Actor input Schema

## `productTypes` (type: `array`):

Which parts of Coursera's own sitemap to walk. Courses alone lists thousands of URLs.

## `urlKeywords` (type: `array`):

Keep only URLs containing one of these words, one per line, for example python or marketing. Leave empty to take them in sitemap order. Coursera's robots.txt closes every search URL, so filtering happens here rather than on their search.

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

Exact course pages to scrape, one per line. Combined with the sitemap unless you turn it off.

## `useSitemap` (type: `boolean`):

Turn off to scrape only the URLs you listed above.

## `includeReviews` (type: `boolean`):

Adds up to 20 learner reviews per course: star rating, review text, review date, and the two-letter initials Coursera itself displays. No learner identifier is collected.

## `reviewLimit` (type: `integer`):

How many reviews to keep per course. Coursera exposes 20 on a course page, so 20 is the ceiling.

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

Hard cap on how many course records are written, so a long run cannot produce an unexpected charge.

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

How many course pages are fetched in parallel.

## Actor input object example

```json
{
  "productTypes": [
    "courses"
  ],
  "startUrls": [
    "https://www.coursera.org/learn/machine-learning"
  ],
  "useSitemap": true,
  "includeReviews": true,
  "reviewLimit": 20,
  "maxItems": 200,
  "concurrency": 4
}
```

# Actor output Schema

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

One record per course: enrollment, monthly page views, full rating split, up to 20 reviews, syllabus, instructors and skills.

## `runStats` (type: `string`):

Counters: sitemap requests, URLs found, pages fetched, courses written, duplicates, robots-blocked, HTTP and parse failures.

## `skipped` (type: `string`):

Every page that produced no record, with the reason. Free, never charged.

# 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 = {
    "startUrls": [
        "https://www.coursera.org/learn/machine-learning"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("midnight_static/coursera-course-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 = { "startUrls": ["https://www.coursera.org/learn/machine-learning"] }

# Run the Actor and wait for it to finish
run = client.actor("midnight_static/coursera-course-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 '{
  "startUrls": [
    "https://www.coursera.org/learn/machine-learning"
  ]
}' |
apify call midnight_static/coursera-course-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,midnight_static/coursera-course-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/D58Wp7wJ8r0cecjd2/builds/gbETbHGyMms9g69GE/openapi.json
