Duolingo Scraper - Profiles, Leaderboards, Courses & Vocabulary avatar

Duolingo Scraper - Profiles, Leaderboards, Courses & Vocabulary

Pricing

from $1.80 / 1,000 learner records

Go to Apify Store
Duolingo Scraper - Profiles, Leaderboards, Courses & Vocabulary

Duolingo Scraper - Profiles, Leaderboards, Courses & Vocabulary

Scrape public Duolingo data without login. Extract learner profiles with streaks, XP and achievements, weekly league standings, course catalogs with learner counts, and vocabulary words from lessons in clean structured data.

Pricing

from $1.80 / 1,000 learner records

Rating

0.0

(0)

Developer

Abot API

Abot API

Maintained by Community

Actor stats

0

Bookmarked

1

Total users

1

Monthly active users

7 days ago

Last modified

Categories

Share

Duolingo Scraper

Scrape Duolingo: learner profiles by username (per-course XP and crown counts, streaks, achievements, motivation, roles and more), weekly league standings, and the full course catalog with learner counts. Every surface this actor reads works with no account at all.

Why This Scraper?

  • Learner profiles by username: streak, total XP, per-course XP and crowns, join date, Super subscription flag, plus account attributes like motivation, roles and verification status. (Achievement badges are included when the source exposes them; the anonymous profile read often returns none.)
  • Weekly league standings by username, with no account needed: current tier, highest tier reached, wins, first, second and top-three finishes, weeks at the current tier, and the date of the last win.
  • Course catalog: all 300+ Duolingo courses with title, public course link, learning and source language, learner count, release phase and progress; filter by keyword, language pair or learner floor, sorted by learners or name. Optional Course Details mode also reads each returned course's page for its description line.
  • Four modes: search by username, paste profile page links, browse the course catalog, or read a course's vocabulary.
  • Incremental mode for scheduled monitoring: only NEW, UPDATED and REAPPEARED records on later runs, with EXPIRED detection on complete scans.
  • Resume a large pull from a previous run or dataset ID without paying for duplicate rows.

Data You Get

Sample shape: values are illustrative placeholders, not from a live profile.

FieldExample value
kindprofile
usernamesample_learner
nameSample Learner
userId100000001
profileUrlhttps://www.duolingo.com/profile/sample_learner
streak12
totalXp50000
learningLanguagees
fromLanguageen
hasPlustrue
locationUS
achievementsDetail[{"name": "challenger", "tier": 3, "count": 825}]
coursesCount2
courses[{"title": "Spanish", "xp": 42000, "crowns": 210}]
joinedAt1700000000
tier (league)2
numWins (league)7
learningLanguageName (course)Spanish
numLearners (course)1234567
changeTypeNEW

How to Use

Read a set of public profiles (no account needed):

{
"mode": "search",
"searchType": "profiles",
"usernames": ["sample_learner", "another_sample"]
}

Read weekly league standings for a set of learners (no account needed):

{
"mode": "search",
"searchType": "leaderboard",
"usernames": ["sample_learner", "another_sample"]
}

Read profiles from pasted links (usernames or numeric IDs):

{
"mode": "url",
"urls": ["https://www.duolingo.com/profile/sample_learner"]
}

List the course catalog (Spanish courses only here, most learners first):

{
"mode": "courses",
"courseQuery": "spanish",
"courseSort": "learners",
"maxItems": 50
}

Find only mature courses with real learners (beta and in-development pairs excluded):

{
"mode": "courses",
"courseMinLearners": 100000,
"maxItems": 100
}

Input Parameters

ParameterTypeDefaultDescription
modeselectsearchSearch, or paste profile links (URL mode).
searchTypeselectprofilesProfiles by username, or weekly league by username. Both work with no account.
usernamesarrayduolingoUsernames to read (Profiles and League search types). A name nobody matches is reported, not failed.
urlsarrayone profile linkProfile page links: /profile/
vocabSkillstringemptyOnly lessons whose skill label contains this keyword (Vocabulary mode).
courseQuerystringemptyCourse keyword filter (Course-catalog mode; matches language names and codes).
courseLearningLanguagestringemptyOnly courses for this learning language code (Course-catalog mode).
courseFromLanguagestringemptyOnly courses taught FROM this language code (Course-catalog mode).
courseMinLearnersinteger0Only courses with at least this many learners (Course-catalog mode).
courseSortselectlearnersCourse row order: most learners first, or language name.
learningLanguageselectemptyOnly keep profiles learning this language (profiles and URL mode; applied to results).
hasPlusOnlybooleanfalseOnly keep profiles with a paid Super subscription.
minTotalXpinteger0Only keep profiles with at least this total XP.
minStreakinteger0Only keep profiles with at least this active streak.
fetchAchievementsbooleantrueAdd each profile's full achievements detail (one extra read per profile).
maxItemsinteger20Stop after this many records in total. 0 = no limit.
resumeFromRunIdstringemptyContinue one interrupted run from its ID or dataset ID.
incrementalModebooleanfalseRecurring monitoring: later runs return only what changed.
stateKeystringemptyName a monitoring campaign to keep its incremental state stable.
emitUnchangedbooleanfalseAlso return (and bill) unchanged rows in incremental mode.
emitExpiredbooleanfalseAlso return (and bill) rows no longer present, after a complete scan.
proxyConfigurationproxydefault poolApify Proxy settings; the default works on every plan.

Output Example

Sample shape: values are illustrative placeholders, not from a live profile.

{
"kind": "profile",
"recordId": "100000001",
"username": "sample_learner",
"name": "Sample Learner",
"userId": 100000001,
"profileUrl": "https://www.duolingo.com/profile/sample_learner",
"bio": "Sample bio text.",
"joinedAt": 1700000000,
"streak": 12,
"streakData": {"currentStreak": {"length": 412, "startDate": "2025-07-28", "endDate": "2026-09-19"}, "longestStreak": null},
"totalXp": 50000,
"learningLanguage": "es",
"fromLanguage": "en",
"currentCourseId": "DUOLINGO_ES_EN",
"hasPlus": true,
"profileCountry": null,
"location": "Sample City",
"courses": [
{"id": "DUOLINGO_ES_EN", "title": "Spanish", "learningLanguage": "es", "fromLanguage": "en", "xp": 42000, "crowns": 210}
],
"coursesCount": 1,
"achievements": [],
"achievementsCount": 0,
"achievementsDetail": [{"name": "sample_badge", "tier": 1, "count": 5}],
"changeType": "NEW"
}

League runs emit one kind: "league_summary" row per requested username, carrying that learner's league standing: tier, highestLeague, numWins, numberOneFinishes, numberTwoFinishes, topThreeFinishes, streakInTier and lastWinContestEnd. Course-catalog runs emit kind: "course" rows: composed title ("Spanish (from English)"), the public courseUrl link, language pair (codes and names), numLearners, release maturity (releaseStatus: released / beta / in development, mapped from the source's own phase), and fromLanguageCount: how many source languages teach that course. The legacy per-course page carries no richer body, and its meta description is a site-wide template line that is not even served consistently, so no page-description fetch exists by design.

Viewer session: self-established, no login

Profiles, /u/

Scope

Profiles (with achievements detail), the per-user weekly league standing, profile links of the form /profile/

Keep usage respectful of the source's terms.

Send results into your apps (MCP connectors)

Optionally pipe results into the apps you already use through Model Context Protocol (MCP) connectors. Authorize a connector under Apify, Settings, API & Integrations, then select it in the input:

  • mcpConnectors: pick one or more connectors (Notion, Linear, Airtable, Apify).
  • notionParentPageUrl: for the Notion connector, the parent page under which one page per record is created.
  • maxNotifyListings: cap on records written per connector per run (default 50).

The connector receives a condensed, human-readable summary per record: a title plus the key fields flattened to plain text. It does not mirror the full dataset record; the complete data is always in the Apify dataset. Leaving every connector empty skips the export entirely and never changes the dataset output.