# Duolingo Scraper (`maximedupre/duolingo`) Actor

Get public Duolingo language courses, learner profiles, weekly league standings, and vocabulary as structured rows. Pick a result type, enter your targets, and run it. No Duolingo sign-in needed.

- **URL**: https://apify.com/maximedupre/duolingo.md
- **Developed by:** [Maxime Dupré](https://apify.com/maximedupre) (community)
- **Categories:** Education, Social media, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.60 / 1,000 language 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

### 🦉 Collect public Duolingo courses, learners, and vocabulary as structured data

This Actor helps language teachers, course designers, researchers, and developers collect public Duolingo data without signing in. It returns structured rows about Duolingo languages. Rows cover courses, learner profiles, weekly league standings, and vocabulary. You can compare courses, review learner streaks and XP, or build word lists for a language pair, without copying each page by hand.

**Use it to:**

- Compare Duolingo language courses by learner count, source language, and release phase.
- Filter learner profiles by total XP, streak, or Super status, starting from usernames or profile links.
- Track weekly league standings for a list of learners, including tier, wins, and placement history.
- Build a Duolingo Spanish vocabulary list with translations, skills, and units for a language pair, such as Spanish taught in English.
- Recheck a monitored lookup and receive only the new or changed records.

#### 🧩 Four kinds of public Duolingo data

- **Courses:** language courses with their teaching and learning languages, learner counts, and release phase.
- **Learner profiles:** public names, streaks, total XP, languages, and current course. Enrolled courses and achievements are included when the source shows them.
- **League summaries:** each learner's weekly league tier, wins, podium finishes, and placement history.
- **Vocabulary:** words and phrases for one language pair, with translations, skills, and units.

Each row is public data that was read and checked successfully. Failed, private, invalid, or unreadable targets go to the warning log, not the dataset.

#### ▶️ Run a Duolingo data collection

1. Open this Actor in Apify Console, or call it through the Apify API.
2. Choose one result type, then fill in only the fields for that type.
3. Start the run. When it finishes, open the Output tab to see the rows.

#### ⚙️ Input

Choose one result type first. Fields for other result types are ignored.

**Input fields**

| Field | Type | What it does |
|---|---|---|
| Result type (`resultType`) | string | Choose one result type for the run: Courses, Learner profiles, Learner league summaries, or Vocabulary. The form starts with Courses. |
| Course name or code (`courseNameOrCode`) | string | For Courses, narrow the results by a course name or code. |
| Minimum learners (`minLearners`) | integer | For Courses, keep only courses with at least this many learners. |
| Course order (`courseSort`) | string | For Courses, sort by most learners first (default) or by language name, A to Z. |
| Find profiles by (`findProfilesBy`) | string | For Learner profiles, choose whether the Learners field holds usernames (default) or profile links. |
| Minimum XP (`minXp`) | integer | For Learner profiles, keep learners with at least this much total XP. |
| Minimum streak (`minStreak`) | integer | For Learner profiles, keep learners with a current streak of at least this many days. |
| Super status (`subscription`) | string | For Learner profiles, keep learners with Super (`super`) or without Super (`notSuper`). Leave blank to include either. |
| Learners (`learners`) | array of strings | Required for Learner profiles and Learner league summaries. For league summaries, enter usernames only. For profiles, enter usernames or profile links, as chosen in Find profiles by. Use one kind of value per run. Profile links can use a username or a numeric user ID. |
| Source language code (`sourceLanguage`) | string | For Courses, filter by the language used to teach the course, such as `en` for English. Required for Vocabulary. |
| Learning language code (`learningLanguage`) | string | For Courses, filter by the language being learned, such as `es` for Spanish. For Learner profiles, filter by their learning language. Required for Vocabulary. |
| Return unchanged records (`changeOutput`) | string | Choose changes only (default) or all items and changes. Change tracking runs automatically. A record counts as missing only after a full successful scan. |
| Monitoring campaign (`monitoringCampaign`) | string | Leave blank to keep history separate for this lookup. Use the same name in another run to share its history. |
| Maximum results (`maxItems`) | integer | Stop after this many results. Leave blank to collect all available results until the source has no more. The form starts at 10. |
| Continue collection from (`resumeFrom`) | string | Enter an earlier Apify run ID or dataset ID to continue its saved progress. Keep the same result type and lookup settings. Leave blank to start a new collection. |

**Example input**

This is the default input. It returns up to 10 course rows.

```json
{
  "resultType": "courses",
  "courseSort": "popularity",
  "findProfilesBy": "usernames",
  "changeOutput": "changes",
  "maxItems": 10
}
```

#### 🧾 Output

Each run saves its rows to the run's default dataset. Open the Output tab in Apify Console to see an overview table, or download the rows as JSON, CSV, or Excel. Every row has `resultType`, `changeType`, `firstObservedAt`, and `latestObservedAt` fields. The tables below list the fields for each result type.

**Course rows**

| Field | Type | What it does |
|---|---|---|
| `resultType` | string | Always `courses` for this row type. |
| `changeType` | string | How this read compares with saved history: `new`, `updated`, `reappeared` (seen again after it was counted as missing), or `unchanged`. |
| `firstObservedAt` | string | Date and time this course was first read and checked successfully in its history. |
| `latestObservedAt` | string | Date and time this course was most recently read and checked successfully. |
| `courseId` | string | Duolingo course ID, used to match course records. |
| `sourceLanguage` | string | Code for the language used to teach the course, such as `en`. |
| `sourceLanguageName` | string | Name of the teaching language, such as English. |
| `learningLanguage` | string | Code for the language being learned, such as `es`. |
| `learningLanguageName` | string | Name of the language being learned, such as Spanish. |
| `title` | string | Course title reported by Duolingo. |
| `learners` | integer | Number of learners reported for the course. |
| `releasePhase` | string | Release phase reported by Duolingo. |
| `developmentProgress` | number | Course development progress, on the scale Duolingo reports. |
| `courseUrl` | string | Public link to the course. |

**Example course row**

```json
{
  "resultType": "courses",
  "courseId": "DUOLINGO_ES_EN",
  "sourceLanguage": "en",
  "sourceLanguageName": "English",
  "learningLanguage": "es",
  "learningLanguageName": "Spanish",
  "title": "Spanish",
  "learners": 41971541,
  "releasePhase": "3",
  "developmentProgress": 200,
  "courseUrl": "https://www.duolingo.com/course/es/en",
  "changeType": "new",
  "firstObservedAt": "2026-10-10T00:34:31.308Z",
  "latestObservedAt": "2026-10-10T00:34:31.308Z"
}
```

**Learner profile rows**

| Field | Type | What it does |
|---|---|---|
| `resultType` | string | Always `profiles` for this row type. |
| `changeType` | string | How this read compares with saved history: `new`, `updated`, `reappeared` (seen again after it was counted as missing), or `unchanged`. |
| `firstObservedAt` | string | Date and time this profile was first read and checked successfully in its history. |
| `latestObservedAt` | string | Date and time this profile was most recently read and checked successfully. |
| `username` | string | Public username. |
| `displayName` | string | Public name shown on the profile. |
| `joinedAt` | string | Date and time the account joined. |
| `location` | string | Public location, when the learner shares one. |
| `avatarUrl` | string | Public link to the profile picture. |
| `sourceLanguage` | string | Code for the source language on the profile, such as `en`. |
| `learningLanguage` | string | Code for the language the learner is studying, such as `fr`. |
| `currentCourseId` | string | ID of the course the learner is on now. Use it to match course rows. |
| `streak` | integer | Current streak length, in days. |
| `currentStreak` | object | Current streak period, when Duolingo reports one. |
| `currentStreak.startDate` | string | Start date of the current streak. |
| `currentStreak.endDate` | string | End date of the current streak. |
| `longestStreak` | object | Longest streak period, when Duolingo reports one. |
| `longestStreak.startDate` | string | Start date of the longest streak. |
| `longestStreak.endDate` | string | End date of the longest streak. |
| `totalXp` | integer | Total XP reported for the learner. |
| `hasSuper` | boolean | Whether the public source reports an active Super subscription. |
| `roles` | array of strings | Public account roles reported by Duolingo. |
| `courses` | array of objects | Enrolled courses shown on the profile. |
| `courses.courseId` | string | Course ID, used to match course rows. |
| `courses.sourceLanguage` | string | Code for the teaching language of the course. |
| `courses.sourceLanguageName` | string | Name of the teaching language, when Duolingo reports it. |
| `courses.learningLanguage` | string | Code for the language being learned. |
| `courses.learningLanguageName` | string | Name of the language being learned, when Duolingo reports it. |
| `courses.title` | string | Course title reported by Duolingo. |
| `courses.xp` | integer | XP earned in that course. |
| `courses.crowns` | integer | Crown progress in that course. |
| `achievements` | array of objects | Achievements shown on the profile, when the source exposes them. |
| `achievements.name` | string | Achievement name. |
| `achievements.description` | string | What the achievement is for. |
| `achievements.tier` | integer | Achievement tier. |
| `achievements.count` | integer | Count reported for the achievement. |
| `achievements.badgeUrl` | string | Link to the achievement badge image. |

**Example learner profile row**

```json
{
  "resultType": "profiles",
  "username": "tsannifer",
  "displayName": "Jennifer",
  "joinedAt": "2016-06-10T01:58:31.000Z",
  "avatarUrl": "https://d3gq3s1iyyx31w.cloudfront.net/static/render/bg/BackgroundColor-3/Body-1/ClothingColor-9/Expression-23/EyeColor-1/FacialHair-0/FacialHairColor-1/Glasses-2/GlassesColor-8/Headwear-0/HeadwearColor-1/MainHair-1/MainHairColor-14/Nose%20Piercing-0/Piercings-0/SkinTone-2/Wrinkles-2",
  "sourceLanguage": "en",
  "learningLanguage": "fr",
  "currentCourseId": "DUOLINGO_FR_EN",
  "streak": 0,
  "longestStreak": {
    "startDate": "2023-06-28",
    "endDate": "2026-05-08"
  },
  "totalXp": 173178,
  "hasSuper": false,
  "roles": [
    "users"
  ],
  "courses": [
    {
      "courseId": "DUOLINGO_FR_EN",
      "sourceLanguage": "en",
      "sourceLanguageName": "English",
      "learningLanguage": "fr",
      "learningLanguageName": "French",
      "title": "French",
      "xp": 138622,
      "crowns": 0
    },
    {
      "courseId": "DUOLINGO_RU_EN",
      "sourceLanguage": "en",
      "sourceLanguageName": "English",
      "learningLanguage": "ru",
      "learningLanguageName": "Russian",
      "title": "Russian",
      "xp": 370,
      "crowns": 0
    },
    {
      "courseId": "DUOLINGO_ES_EN",
      "sourceLanguage": "en",
      "sourceLanguageName": "English",
      "learningLanguage": "es",
      "learningLanguageName": "Spanish",
      "title": "Spanish",
      "xp": 12806,
      "crowns": 0
    },
    {
      "courseId": "DUOLINGO_DE_EN",
      "sourceLanguage": "en",
      "sourceLanguageName": "English",
      "learningLanguage": "de",
      "learningLanguageName": "German",
      "title": "German",
      "xp": 3610,
      "crowns": 0
    },
    {
      "courseId": "DUOLINGO_IT_EN",
      "sourceLanguage": "en",
      "sourceLanguageName": "English",
      "learningLanguage": "it",
      "learningLanguageName": "Italian",
      "title": "Italian",
      "xp": 1548,
      "crowns": 0
    },
    {
      "courseId": "DUOLINGO_EL_EN",
      "sourceLanguage": "en",
      "sourceLanguageName": "English",
      "learningLanguage": "el",
      "learningLanguageName": "Greek",
      "title": "Greek",
      "xp": 12179,
      "crowns": 0
    },
    {
      "courseId": "DUOLINGO_GA_EN",
      "sourceLanguage": "en",
      "sourceLanguageName": "English",
      "learningLanguage": "ga",
      "learningLanguageName": "Irish",
      "title": "Irish",
      "xp": 436,
      "crowns": 0
    },
    {
      "courseId": "DUOLINGO_SV_EN",
      "sourceLanguage": "en",
      "sourceLanguageName": "English",
      "learningLanguage": "sv",
      "learningLanguageName": "Swedish",
      "title": "Swedish",
      "xp": 80,
      "crowns": 0
    },
    {
      "courseId": "DUOLINGO_RO_EN",
      "sourceLanguage": "en",
      "sourceLanguageName": "English",
      "learningLanguage": "ro",
      "learningLanguageName": "Romanian",
      "title": "Romanian",
      "xp": 583,
      "crowns": 0
    },
    {
      "courseId": "DUOLINGO_UK_EN",
      "sourceLanguage": "en",
      "sourceLanguageName": "English",
      "learningLanguage": "uk",
      "learningLanguageName": "Ukrainian",
      "title": "Ukrainian",
      "xp": 109,
      "crowns": 0
    },
    {
      "courseId": "DUOLINGO_DA_EN",
      "sourceLanguage": "en",
      "sourceLanguageName": "English",
      "learningLanguage": "da",
      "learningLanguageName": "Danish",
      "title": "Danish",
      "xp": 555,
      "crowns": 0
    },
    {
      "courseId": "DUOLINGO_NO-BO_EN",
      "sourceLanguage": "en",
      "learningLanguage": "no-BO",
      "title": "Norwegian (Bokmål)",
      "xp": 219,
      "crowns": 0
    },
    {
      "courseId": "DUOLINGO_TR_EN",
      "sourceLanguage": "en",
      "sourceLanguageName": "English",
      "learningLanguage": "tr",
      "learningLanguageName": "Turkish",
      "title": "Turkish",
      "xp": 36,
      "crowns": 0
    },
    {
      "courseId": "DUOLINGO_ZH-CN_EN",
      "sourceLanguage": "en",
      "learningLanguage": "zh",
      "title": "Chinese (Simplified)",
      "xp": 1055,
      "crowns": 0
    },
    {
      "courseId": "DUOLINGO_LA_EN",
      "sourceLanguage": "en",
      "sourceLanguageName": "English",
      "learningLanguage": "la",
      "learningLanguageName": "Latin",
      "title": "Latin",
      "xp": 180,
      "crowns": 0
    },
    {
      "courseId": "DUOLINGO_AR_EN",
      "sourceLanguage": "en",
      "sourceLanguageName": "English",
      "learningLanguage": "ar",
      "learningLanguageName": "Arabic",
      "title": "Arabic",
      "xp": 302,
      "crowns": 0
    },
    {
      "courseId": "DUOLINGO_NL-NL_EN",
      "sourceLanguage": "en",
      "learningLanguage": "nl-NL",
      "title": "Dutch",
      "xp": 60,
      "crowns": 0
    },
    {
      "courseId": "DUOLINGO_HI_EN",
      "sourceLanguage": "en",
      "sourceLanguageName": "English",
      "learningLanguage": "hi",
      "learningLanguageName": "Hindi",
      "title": "Hindi",
      "xp": 28,
      "crowns": 0
    }
  ],
  "achievements": [
    {
      "name": "Photogenic",
      "description": "Add a profile picture",
      "tier": 1,
      "count": 1,
      "badgeUrl": "https://d35aaqx5ub95lt.cloudfront.net/images/achievements/39ac3b013faeeba3d0afb91f7230a43f.svg"
    }
  ],
  "changeType": "new",
  "firstObservedAt": "2026-10-10T00:41:06.286Z",
  "latestObservedAt": "2026-10-10T00:41:06.286Z"
}
```

**League summary rows**

| Field | Type | What it does |
|---|---|---|
| `resultType` | string | Always `leagues` for this row type. |
| `changeType` | string | How this read compares with saved history: `new`, `updated`, `reappeared` (seen again after it was counted as missing), or `unchanged`. |
| `firstObservedAt` | string | Date and time this summary was first read and checked successfully in its history. |
| `latestObservedAt` | string | Date and time this summary was most recently read and checked successfully. |
| `username` | string | Public username of the learner. |
| `leagueTier` | string | Current league tier reported by Duolingo. |
| `highestLeague` | string | Highest league tier the learner has reached. |
| `wins` | integer | League wins reported by Duolingo. |
| `podiumFinishes` | integer | Podium finishes reported by Duolingo. |
| `firstPlaceFinishes` | integer | First-place finishes reported by Duolingo. |
| `secondPlaceFinishes` | integer | Second-place finishes reported by Duolingo. |
| `weeksInTier` | integer | Weeks spent in the current tier. |
| `lastWinDate` | string | Date of the most recent league win. |

**Example league summary row**

```json
{
  "resultType": "leagues",
  "username": "Luis",
  "leagueTier": "9",
  "highestLeague": "9",
  "wins": 7,
  "firstPlaceFinishes": 3,
  "secondPlaceFinishes": 5,
  "podiumFinishes": 18,
  "weeksInTier": 2,
  "lastWinDate": "2026-08-10T03:00:00Z",
  "changeType": "new",
  "firstObservedAt": "2026-10-10T00:34:51.107Z",
  "latestObservedAt": "2026-10-10T00:34:51.107Z"
}
```

**Vocabulary rows**

| Field | Type | What it does |
|---|---|---|
| `resultType` | string | Always `vocabulary` for this row type. |
| `changeType` | string | How this read compares with saved history: `new`, `updated`, `reappeared` (seen again after it was counted as missing), or `unchanged`. |
| `firstObservedAt` | string | Date and time this entry was first read and checked successfully in its history. |
| `latestObservedAt` | string | Date and time this entry was most recently read and checked successfully. |
| `sourceLanguage` | string | Code for the language used to teach the entry, such as `en`. |
| `learningLanguage` | string | Code for the language being learned, such as `es`. |
| `text` | string | Word or phrase in the language being learned. |
| `translations` | array of strings | Translations into the teaching language. |
| `skill` | string | Skill name attached to the word or phrase. |
| `unit` | string | Unit name attached to the word or phrase. |
| `pathPosition` | object | Position of the entry in the learning path, as reported by the source. |
| `pathPosition.unitIndex` | integer | Index of the unit in the learning path. |
| `audioUrls` | array of strings | Direct links to pronunciation audio from the source. |

**Example vocabulary row**

```json
{
  "resultType": "vocabulary",
  "sourceLanguage": "en",
  "learningLanguage": "es",
  "text": "Un vaso de agua, por favor.",
  "translations": [
    "A glass of water, please."
  ],
  "skill": "Cafe",
  "unit": "Order at a café",
  "pathPosition": {
    "unitIndex": 0
  },
  "audioUrls": [
    "https://d1vq87e9lcf771.cloudfront.net/miguel/439c80f8627350ea006f0537b759da1a"
  ],
  "changeType": "new",
  "firstObservedAt": "2026-10-10T00:34:49.599Z",
  "latestObservedAt": "2026-10-10T00:34:49.599Z"
}
```

#### 💳 Pricing

This Actor uses pay-per-event pricing. You pay for each row below that is read successfully and saved to your dataset:

- **Language course:** each course read successfully and saved to your dataset.
- **Learner profile:** each learner profile read successfully and saved, including any public course details.
- **League summary:** each learner's league summary read successfully and saved.
- **Vocabulary word:** each word or phrase read successfully and saved.

Prices can differ by Apify plan tier, so check the Store page for current prices.

#### 🔌 Integrations

https://www.youtube.com/watch?v=bNACk1_S_6w\&list=PLObrtcm1Kw6MUrlLNDbK9QRg8VDJg0gOW\&index=4

- Call this Actor through the Apify API from scripts, apps, or internal tools.
- Export the dataset as JSON, CSV, or Excel from Apify Console.
- Schedule repeat runs with the same settings to follow changes over time.

#### ❓ FAQ

##### Can I get only Duolingo English courses?

Yes. For Courses, set Learning language code to `en` to see courses for learning English. Use Source language code to filter by the language a course is taught in, such as `en` for English.

##### Can I look up learners by display name?

No. Learner lookups need public usernames or public profile links.

##### Does it return followers, following lists, or user reviews?

No. It does not return follower or following lists, learner cohort rankings, or Duolingo user reviews.

##### Does it return course descriptions?

No. Course rows include public catalog details, such as titles, language codes, learner counts, and release phase. Course page descriptions are not included. Vocabulary rows return the available words and phrases from one selected course, with translations, skills, and units.

##### Does a failed lookup mean a learner or course was deleted?

No. A failed lookup does not prove that anything was removed. A record counts as missing only after a full, successful scan.

##### Can I continue a large run that stopped early?

Yes. Enter the earlier run ID or dataset ID in Continue collection from. Keep the same result type and lookup settings, and the run picks up from its saved progress.

##### How do I track changes over time?

Run the Actor again with the same lookup settings. Each row's `changeType` field shows how it compares with saved history. Use the same Monitoring campaign name in each run to share history. Leave it blank to keep each lookup's history separate.

##### How many results can one run return?

There is no fixed maximum. Leave Maximum results blank to collect all available results until the source has no more. Set a number to stop earlier.

##### What am I charged for?

You are charged for each successful row saved to the dataset, as listed in Pricing.

##### Is this the official Duolingo app or website?

No. This is an independent Actor that reads public Duolingo data. You do not need a Duolingo account or sign-in to run it.

### 📝 Changelog

**v0.0** (11-10-2026)

- Initial release.

### 🆘 Support

For issues, questions, or feature requests, [file a ticket](https://console.apify.com/actors/maximedupre~duolingo/issues) and I'll fix or implement it in less than 24h 🫡

### 🔗 Related Actors

- [Duolingo Scraper](https://apify.com/crawlerbros/duolingo-scraper): Get a Duolingo course catalog with learner counts, progress phases, and source languages.
- [Duolingo Scraper - Profiles, Leaderboards, Courses & Vocabulary](https://apify.com/abotapi/duolingo-learner-scraper): Get learner profiles with streaks, XP, and achievements, plus weekly league standings.
- [Duolingo Language Data Scraper](https://apify.com/parseforge/duolingo-language-data-scraper): Pull words with translations, skills, and units for one language pair to build study lists.
- [Tatoeba Sentence Corpus Scraper](https://apify.com/parseforge/tatoeba-sentence-corpus-scraper): Collect example sentences and their translations for a language pair.
- [Free Dictionary Scraper](https://apify.com/gio21/free-dictionary-scraper): Look up definitions, phonetics, audio pronunciation, and examples for a word list, with multi-language support.

**Made with ❤️ by Maxime Dupré**

# Actor input Schema

## `resultType` (type: `string`):

Choose one result type for this run. Fields in another result type's options section are ignored.

## `courseNameOrCode` (type: `string`):

For Courses, enter a course name or code to narrow the results.

## `minLearners` (type: `integer`):

For Courses, keep courses with at least this many learners.

## `courseSort` (type: `string`):

For Courses, choose how to order the results.

## `findProfilesBy` (type: `string`):

For Learner profiles, choose what you will enter in Learners: usernames or profile links. Other result types ignore this field.

## `minXp` (type: `integer`):

For Learner profiles, keep learners with at least this much total XP.

## `minStreak` (type: `integer`):

For Learner profiles, keep learners with a current streak of at least this many days.

## `subscription` (type: `string`):

For Learner profiles, choose a public Super status. Leave blank to include either status.

## `learners` (type: `array`):

Required for Learner profiles and Learner league summaries. For profiles, enter one or more values in the format you picked in Find profiles by. For league summaries, enter usernames only. Use one kind of value per run. Profile links can use a username or a numeric user ID.

## `sourceLanguage` (type: `string`):

For Courses, filter by the language used to teach the course. Required for Vocabulary. For example, en means English.

## `learningLanguage` (type: `string`):

For Courses, filter by the language being learned. For Learner profiles, filter by their learning language. Required for Vocabulary. For example, es means Spanish.

## `changeOutput` (type: `string`):

Change tracking runs automatically. Choose whether results include records that are unchanged from saved history. A record counts as missing only after a full successful scan.

## `monitoringCampaign` (type: `string`):

Leave blank to keep history separate for this lookup. Use the same name in another run to share its history.

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

Stop after this many results. Leave blank to collect all available results until the source has no more.

## `resumeFrom` (type: `string`):

Enter an earlier Apify run ID or dataset ID to continue its saved progress. Keep the same result type and lookup settings. Leave blank to start a new collection.

## Actor input object example

```json
{
  "resultType": "courses",
  "courseSort": "popularity",
  "findProfilesBy": "usernames",
  "changeOutput": "changes",
  "maxItems": 10
}
```

# Actor output Schema

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

View the collected Duolingo results.

# 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 = {
    "resultType": "courses",
    "maxItems": 10
};

// Run the Actor and wait for it to finish
const run = await client.actor("maximedupre/duolingo").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 = {
    "resultType": "courses",
    "maxItems": 10,
}

# Run the Actor and wait for it to finish
run = client.actor("maximedupre/duolingo").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 '{
  "resultType": "courses",
  "maxItems": 10
}' |
apify call maximedupre/duolingo --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,maximedupre/duolingo"
        }
    }
}
```

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/cydnIKtIpnXHzO5QJ/builds/h0KVcGQnV1dadNhGd/openapi.json
