Duolingo Scraper icon

Duolingo Scraper

Scrape public Duolingo data without login. Extract learner profiles with streaks, XP and achievements, weekly league standings, course catalogs with…

Start using this actor on Apify ↗Need a custom scraper?Contact us ↗

Duolingo Scraper at a glance

Duolingo Scraper is a ready-to-run Apify actor from abotapi for collecting web data. Its documented output includes kind, recordid, username, name and userid, which you can export as JSON, CSV or Excel or read through the Apify API. It costs $2.50 per 1,000 results on the Apify Free plan and from $1.80 on paid plans, with smaller per-run or optional feature charges listed on Apify.

Runs on
Apify cloud: console, API, schedules and integrations
Data type
Web data
Pricing
$2.50 per 1,000 results (Free plan), from $1.80 on paid plans
Output
Documented below · JSON, CSV, Excel
Inputs
25 options

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.

What Duolingo Scraper can do

  • Four ways to read the platform. Search by username (profiles or weekly league), paste profile links, browse the full course catalog, or read a course's vocabulary lesson by lesson.
  • No account needed anywhere. Profiles, leagues and the course catalog read anonymously; achievements detail and the vocabulary walk use a viewer session the actor mints for itself. You never paste a login or a token.
  • Deep learner profiles. Streak, total XP, per-course XP and crowns, join date, Super subscription flag, location, motivation, roles and full achievement badge detail.
  • The whole course catalog. 300+ language pairs with learner counts, release maturity and progress, filterable by keyword, language pair or learner floor, sorted by popularity or name.
  • Vocabulary reading. Walk a course's active lessons and collect word and translation pairs with their skill, unit and level context.
  • Built for schedules. Incremental mode returns only new and changed learners on recurring runs, and a refused run fails loudly instead of returning an empty dataset.

Choose what to collect

These inputs let you configure Duolingo Scraper - Profiles, Leaderboards, Courses & Vocabulary. Open the actor on Apify to enter your values and review the current options.

Supported input fields
InputWhat it controls
modestring · requiredHow to find data: pick a search type below, paste profile links, list the whole course catalog, or read a course's vocabulary.

Choices: search, url, courses, vocabulary

searchTypestringWhat Search mode reads (Search mode only). Both types work with no account.

Choices: profiles, leaderboard

usernamesarrayDuolingo usernames to read, one record each (Profiles and League search types). A username that matches nobody is reported, not failed. Public profile data only: whatever the profile owner's privacy settings expose.
urlsarrayProfile links (URL mode): a profile page, with or without www or a trailing /courses, a /u/ link, or a users API link like https://www.duolingo.com/2017-06-30/users?username=duolingo. Each distinct learner returns one record.
courseQuerystringOnly keep courses whose language names or codes contain this keyword, like spanish or ja (Course-catalog mode only; applied to results).
courseLearningLanguagestringCourse pair selector (Course-catalog and Vocabulary modes): the learning language code, like es or ja.
courseFromLanguagestringCourse pair selector (Course-catalog and Vocabulary modes): the source language code the course is taught from, like en.
Show 18 more inputs
More input fields
InputWhat it controls
courseMinLearnersintegerOnly keep courses with at least this many learners (0 = no floor; Course-catalog mode only; applied to results).
courseSortstringOrder of the course rows (Course-catalog mode only): learners = most learners first, name = course language name A to Z.

Choices: learners, name

vocabSkillstringOnly read lessons whose skill label contains this keyword, like cafe (Vocabulary mode only; applied per lesson).
learningLanguagestringOnly return profiles learning this language, as a course code like es, fr, de, it, ja (empty = all). The list is the source's own live course catalog codes. Applies to returned profiles in Profiles search type and URL mode; the filter is applied to results, not server-side.

Choices: , ar, ca, cs, cy, da, de, dn, el, en, eo, es, fi, fr, ga, gd, gn, he, hi, ht, hu, hv, hw, id, it, ja, kl, ko, la, nb, nv, pl, pt, ro, ru, sv, sw, tr, uk, vi, yi, zc, zs, zu, zw

hasPlusOnlybooleanOnly return profiles with a paid Super subscription. Applied to results in Profiles search type and URL mode.
minTotalXpintegerOnly return profiles with at least this much total XP (0 = no floor). Applied to results in Profiles search type and URL mode.
minStreakintegerOnly return profiles with an active streak of at least this many days (0 = no floor). Applied to results in Profiles search type and URL mode.
fetchAchievementsbooleanOn by default. Adds each profile's full achievements detail (badge name, tier, count) from the achievements service: one extra read per profile, with per-profile progress in the log. Applies to Profiles search type and URL mode.
maxItemsintegerStop after this many records in total. 0 means the hard cap of 100000 records. This is the only limit that stops a run early by default.
resumeFromRunIdstringPaste a previous run ID or dataset ID to continue a large pull without returning records already collected there.
incrementalModebooleanTurn this on for daily or recurring monitoring. The first run returns every matching record as NEW. Later runs normally return only NEW, UPDATED and REAPPEARED records. Turn on Emit unchanged or Emit expired only when you also want those rows returned (and billed). State is kept separately for each search, filter and URL setup; use State key to name or deliberately share a monitoring campaign. Compared: every field of a record, including streak, total XP, course list, achievements and the catalog course phase and progress. Excluded (still returned, never counted as a change): live activity flags, the streak detail object, per-course XP, the catalog learner counter and the scrape timestamp, because the source recomputes them constantly. Course and achievement order never counts as a change. Use Ignore fields to exclude more.
stateKeystringOptional. Name this monitoring campaign to keep its state stable, or deliberately share state across differently configured runs. Leave empty to let the actor derive a key automatically from the search and filter settings.
emitUnchangedbooleanOff by default. Turn on to also return records that have not changed since the last run, marked UNCHANGED. This returns, and bills, extra rows you already have.
emitExpiredbooleanOff by default. Turn on to also return records that were present in a previous run but are no longer found, marked EXPIRED. Only produced once a run has fully scanned the tracked scope; a run that could not read every requested item skips EXPIRED detection rather than guessing.
ignoreFieldsForChangesarrayOptional. Field names that never count as a change in incremental mode, for example crowns or achievementsCount. Use a dotted path for a field inside a list, such as courses.crowns. Ignored fields are still returned in the output; they only stop a record being marked UPDATED and are left out of changedFields. Takes effect immediately on existing state, with no one-off wave of UPDATED rows.
mcpConnectorsarrayOptionally send results into the apps you already use, via Model Context Protocol (MCP) connectors. Authorize one under Apify, Settings, API & Integrations, then select it here. Notion gets a rich page-per-item export; other connectors get a best-effort write or digest. Leave empty to skip; never changes the dataset output.
notionParentPageUrlstringURL or id of the Notion page under which item pages are created. Required to enable the Notion export; ignored by other connectors.
maxNotifyListingsintegerCap on items written to each connector per run. Does not affect the dataset.

Fields in the output

The documented dataset includes the fields below. Availability can depend on the source page and selected mode.

kindkind
recordIdrecordId
usernameusername
namename
userIduserId
profileUrlprofileUrl
streakstreak
totalXptotalXp
learningLanguagelearningLanguage
fromLanguagefromLanguage
hasPlushasPlus
locationlocation
Show 47 more fields
coursesCountcoursesCount
achievementsCountachievementsCount
achievementsDetailachievementsDetail
tiertier
courseIdcourseId
learningLanguageNamelearningLanguageName
fromLanguageNamefromLanguageName
titletitle
courseUrlcourseUrl
urlurl
scrapedAtscrapedAt
numLearnersnumLearners
phasephase
wordword
translationtranslation
skillNameskillName
changeTypechangeType
changedFieldschangedFields
firstSeenAtfirstSeenAt
lastSeenAtlastSeenAt
currentCourseIdcurrentCourseId
coursescourses
achievementsachievements
joinedAtjoinedAt
biobio
profileCountryprofileCountry
hasRecentActivity15hasRecentActivity15
highestLeaguehighestLeague
numWinsnumWins
numberOneFinishesnumberOneFinishes
numberTwoFinishesnumberTwoFinishes
topThreeFinishestopThreeFinishes
streakInTierstreakInTier
lastWinContestEndlastWinContestEnd
hasActiveLeaguehasActiveLeague
cohortSizecohortSize
contestStartcontestStart
contestEndcontestEnd
ownerUsernameownerUsername
progressprogress
challengeTypechallengeType
skillIdskillId
unitIndexunitIndex
sectionIndexsectionIndex
levelIndexlevelIndex
isNewWordisNewWord
ttstts

Example output

An example from this actor’s documentation. Values are illustrative; this is not a fresh live result.

{
  "kind": "profile",
  "recordId": "200000042",
  "username": "sample_learner",
  "name": "Jordan Learner",
  "userId": 200000042,
  "profileUrl": "https://www.duolingo.com/profile/sample_learner",
  "bio": "Learning one language at a time.",
  "joinedAt": 1700000000,
  "streak": 42,
  "totalXp": 8500,
  "learningLanguage": "es",
  "fromLanguage": "en",
  "currentCourseId": "SAMPLE_ES_EN",
  "hasPlus": false,
  "location": "Sample City",
  "coursesCount": 2,
  "courses": [
    {
      "id": "SAMPLE_ES_EN",
      "title": "Spanish",
      "learningLanguage": "es",
      "fromLanguage": "en",
      "xp": 8000,
      "crowns": 45
    },
    {
      "id": "SAMPLE_FR_EN",
      "title": "French",
      "learningLanguage": "fr",
      "fromLanguage": "en",
      "xp": 500,
      "crowns": 3
    }
  ],
  "achievements": [
    {
      "name": "sample_badge",
      "tier": 2,
      "count": 15
    },
    {
      "name": "sample_streak_badge",
      "tier": 1,
      "count": 7
    }
  ],
  "achievementsCount": 2,
  "achievementsDetail": [
    {
      "name": "sample_badge",
      "tier": 2,
      "count": 15,
      "rewards": {},
      "tierCounts": [
        5,
        15,
        50
      ]
    },
    {
      "name": "sample_streak_badge",
      "tier": 1,
      "count": 7,
      "rewards": {},
      "tierCounts": [
        7,
        30,
        100
      ]
    }
  ],
  "motivation": "CAREER",
  "roles": []
}

Ways to use this data

  • Language-learning coaches and classrooms: track streaks, XP and course progress for a roster of learners over time.
  • Community and leaderboard tools: build league standing dashboards or streak leaderboards from public profiles.
  • Course research and content teams: compare learner counts, release maturity and language pairs across the full catalog.
  • Vocabulary and curriculum builders: extract word and translation lists from specific lessons for flashcards or study guides.
  • Product and market research: watch which language pairs are growing by learner count over time with incremental mode.

Before you run

Start with a small input and check the resulting records against your expected fields. Source content and actor options can change; the current Apify listing is the reference for availability and pricing.

Questions about this actor

What data does Duolingo Scraper return?

The documented output for Duolingo Scraper includes kind, recordid, username, name, userid, profileurl, streak and totalxp. Availability of each field depends on the source page and the selected mode.

How much does Duolingo Scraper cost?

It costs $2.50 per 1,000 results on the Apify Free plan and from $1.80 on paid plans, with smaller per-run or optional feature charges listed on Apify. The Apify listing shows the current rate for every plan.

Can I get only new or changed learners on a schedule?

Yes. Schedule the actor from the Schedules tab and turn on Incremental mode. Each run then returns only new, updated and reappeared records, and unchanged ones are not billed.

Why can't I search for a learner without knowing their username?

The source has no anonymous keyword search for people: profiles are read by exact username, or by pasting a profile link. Course-catalog mode does support a keyword search, since courses (not people) are the thing being searched there.

Why did my run fail instead of returning an empty dataset?

If the site refuses every request, or no results page could be loaded ("Could not load any results page (the requests failed), so nothing was collected. This is a connection problem, not an empty search."), the run stops with a clear message so "no matches" is never confused with "nothing could be read". Run it again in a few minutes. If only some usernames or links could be read, you still get what was found.

Can I use it with AI agents or MCP?

Yes. Call it from any Apify integration or MCP client, and use the connector field to push results into Notion, Linear or Airtable.

Ready to collect with Duolingo Scraper?

Run it on Apify with your own input and export the results as JSON, CSV or Excel.