<aside>
</aside>
公式にはアナウンスされていないけれど、API処理ができるようです。
requestsを投げると返ってきます。
API使用には認証が必要です。jwt_token という値を取得します。
<aside>
</aside>
Username から UserID取得します。
認証(jwt_token)は不要です。
import requests
username = **"USERNAME"**
def get_duolingo_id(username: str):
url = f"<https://www.duolingo.com/2017-06-30/users?username={username}>"
headers = {
"User-Agent": "Mozilla/5.0"
}
resp = requests.get(url, headers=headers)
resp.raise_for_status()
data = resp.json()
if "users" in data and len(data["users"]) > 0:
user_id = data["users"][0].get("id")
return user_id
else:
return None
user_id = get_duolingo_id(username)
if user_id:
print(f"Username: {username} → User ID: {user_id}")
else:
print("ユーザーが見つかりませんでした")
実行結果例
import requests
import time
user_id = **"USERID"**
jwt_token = **"JWT TOKEN"**
headers = {
"Authorization": f"Bearer {jwt_token}",
"User-Agent": "Mozilla/5.0",
}
# followers
followers_url = f"<https://www.duolingo.com/2017-06-30/friends/users/{user_id}/followers>"
followers_params = {"pageSize": 500, "viewerId": user_id, "_": str(int(time.time() * 1000))}
followers_data = requests.get(followers_url, headers=headers, params=followers_params).json()
# following
following_url = f"<https://www.duolingo.com/2017-06-30/friends/users/{user_id}/following>"
following_params = {"pageSize": 500, "viewerId": user_id, "_": str(int(time.time() * 1000))}
following_data = requests.get(following_url, headers=headers, params=following_params).json()
followers_list = {tmp_userlist["username"] for tmp_userlist in followers_data["followers"].get("users", [])}
following_list = {tmp_userlist["username"] for tmp_userlist in following_data["following"].get("users", [])}
# 相互フォローしていない人
not_following_back = following_list - followers_list
print("フォローしているがフォローされていないアカウント:")
for tmp_userlist in sorted(not_following_back):
print('<https://www.duolingo.com/profile/>' + tmp_userlist)
実行結果例
これでプロフィールのURLを開いてどんどんフォロー解除できますね。
userIDを指定してstreakを取得します。
認証(jwt_token)は不要です。
import requests
user_id = **"USERID"**
url = f"<https://www.duolingo.com/2017-06-30/users?username={user_id}>"
headers = {
"User-Agent": "Mozilla/5.0",
"Accept": "application/json"
}
res = requests.get(url, headers=headers)
res.raise_for_status()
data = res.json()
user_data = data["users"][0]
streak = user_data["streak"]
print(user_id, "の streak:", streak, "日")
実行結果例
こざえもん、635日がんばっている。えらい。
ちょっと処理時間かかるけど、ずらっと表示されます。
import requests
import time
user_id = **"USERID"**
jwt_token = **"JWT TOKEN"**
headers = {
"Authorization": f"Bearer {jwt_token}",
"User-Agent": "Mozilla/5.0",
}
def streak_check(user_id):
url = f"<https://www.duolingo.com/2017-06-30/users?username={user_id}>"
headers = {
"User-Agent": "Mozilla/5.0",
"Accept": "application/json"
}
res = requests.get(url, headers=headers)
res.raise_for_status()
data = res.json()
user_data = data["users"][0]
return user_data["streak"]
# following
following_url = f"<https://www.duolingo.com/2017-06-30/friends/users/{user_id}/following>"
following_params = {"pageSize": 500, "viewerId": user_id, "_": str(int(time.time() * 1000))}
following_data = requests.get(following_url, headers=headers, params=following_params).json()
following_list = {tmp_userlist["username"] for tmp_userlist in following_data["following"].get("users", [])}
print("各ユーザのStreak: ※0daysは先頭に★")
for tmpUserID in sorted(following_list):
streak = streak_check(tmpUserID)
if streak == 0:
print('★https://www.duolingo.com/profile/' + tmpUserID + ": " + str(streak) + "days")
else:
print('<https://www.duolingo.com/profile/>' + tmpUserID + ": " + str(streak) + "days")
実行結果例
streak 0days のお友達は、すまないけどフォロー解除させてもらいます。
pageSizeが500を超えると、duolingoからエラーが返ってくる..という情報があります。(まだ試していませんが)
なので、500名以上のデータを取得する場合は工夫が必要と思われます。
requestのパラメータにpageを追加して、ループ処理でpage数をインクリメントしていけば、全データ取得できるはずです。
*ここの処理は考え中です。
というか、まだ友達500も居ないので、困った頃に記載します。*
以上
<aside>
2025/9/23
2025/9/22
2025/8/23
2025/7/5
<aside>
NAME : こざえもん
SNS : X(@koz_sec)
JOB : IT Security