How to fetch your Duolingo streak using an unconfirmed API on duolingo.com:
function getDuolingoStreak(username) {
const res = await fetch(
`https://www.duolingo.com/2017-06-30/users?username=${username}&fields=streak,streakData%7BcurrentStreak,previousStreak%7D%7D`
);
const data = await res.json();
const userData = data.users[0];
// I didn't know which of these fields matter, so I just get the max of them.
const streak = Math.max(
userData?.streak ?? 0,
userData?.streakData?.currentStreak?.length ?? 0,
userData?.streakData?.previousStreak?.length ?? 0
);
return streak;
}
365
This card is live ;)
That’s my current max streak.
I can then render this data into a card like that. I put one of these cards in the /misc/ section.
Let’s look at the API itself. www.duolingo.com/2017-06-30
seems to be the API prefix, which is a bit weird. What is 2017-06-30
? What happened on that date? Maybe the Duolingo team used a date-based versioning at the time?
In any case, big thanks to the Duolingo team for keeping this apparently-6-year-old public API alive and accessible.
The query parameters for the /users
endpoint are interesting. It’s similar to GraphQL where you can specify exactly which fields you want in the response. You can even specify specific sub-fields within objects, using some kind of a DSL in the fields
parameter.
The fields
parameter in the above query, when decoded, is fields=streak,streakDate{currentStreak,previousStreak}}
. Fields are comma-delimited and objects are enclosed in braces. This could actually be GraphQL! There is an extra closing brace at the end which seems necessary for the request to be successful.