APIでTwitterのユーザー名をユーザーIDに変換
XはプロフィールにユーザーIDを表示しないため、コードではユーザー名をユーザー検索のエンドポイントに送ってIDを取得します。返るのは最大19桁の文字列で表されたアカウント固有のIDで、バッチ検索なら1リクエストで100件のユーザー名を処理できます。
要点: 公式X APIのGET /2/users/byは1回の呼び出しで100件のユーザー名を受け取り、ユーザー1人あたり$0.010を課金します。開発者アカウントとベアラートークンが必要で、同じ検索でIDからユーザー名への逆引きもできます。Sorsa APIはGET /info-batchでリストを変換し、1リクエストで100件のユーザー名を、1,000件あたり$0.014からで処理します。100回分の無料リクエストで最初の1万件をまかなえます。
- エンドポイント
GET /info-batch- ページサイズ
- 1リクエストあたり100件のユーザー名
- 価格
- 1,000件あたり$0.014から
#TwitterユーザーIDとは何か、どうやって調べるか
TwitterユーザーIDとは、アカウントが作られた瞬間にXが割り当てる永続的な番号です。@に続くユーザー名は、1〜15文字の英字、数字、_からなるラベルで、持ち主がいつでも変更でき、空いた後は誰でも取得できます。IDは変わらず、別のアカウントに移ることもありません。アプリ、エクスポート、データベースがIDでアカウントを指すのはこのためです。開発者の間では、アカウントID、数値ID、UIDとも呼ばれます。
1つのアカウントだけなら、無料のユーザーID変換ツールが、ユーザー名やプロフィールリンクの背後にあるIDを表示し、IDをアカウントの現在のユーザー名に戻すこともできます。サインアップは不要です。
このフォームは、入力したユーザー名、リンク、IDを設定した状態で無料のTwitter IDファインダーを開きます。
初期のアカウントには短いIDが与えられました。Jack Dorseyの@jackは12です。近年開設されたアカウントのIDは18桁か19桁です。Xのアプリには番号がどこにも表示されませんが、自分のIDはデータアーカイブに入っています。「設定とプライバシー」、「アカウント」、「データのアーカイブをダウンロード」からリクエストし、data/account.jsを開いてaccountIdを読んでください。
ユーザーIDは投稿IDではありません。投稿リンクの/status/の後ろの長い数字は投稿を指すもので、その中に含まれる日時はツイートID日時変換ツールで読み取れます。投稿IDをユーザー検索に送ると、アカウントは見つかりません。
#Sorsa APIでユーザー名をユーザーIDに変換する
公開データを扱うTwitter(X)API兼スクレイピングツールであるSorsa APIには、それぞれの方向に対応するエンドポイントがあり、1回の変換は1リクエストです。1,000回の変換あたり$1.40からです。どの呼び出しもApiKeyヘッダーにキーを付け、IDをJSONの文字列として返すので、途中で桁が失われることはありません。
-
キーを取得します。 サインアップして、ダッシュボードからAPIキーをコピーします。100回分の無料リクエストが付き、カードは不要です。
-
ユーザー名を送ります。 @を付けずに
GET /username-to-id(新しいタブで開きます)に渡します。Terminal curl "https://api.sorsa.io/v3/username-to-id/harborline_analytics" \ -H "ApiKey: YOUR_API_KEY"Response200 OK { "id": "1427632236659332516" } -
必要なら逆方向にも変換します。
GET /id-to-username(新しいタブで開きます)は、アカウントの現在のユーザー名をhandleというフィールドで返し、GET /link-to-id(新しいタブで開きます)はx.comまたはtwitter.comのプロフィールリンク全体を受け取ります。 -
3つの呼び出しをラップします。 同じ変換をPythonとNode.jsで書くと次のとおりです。
import requests BASE = "https://api.sorsa.io/v3" HEADERS = {"ApiKey": "YOUR_API_KEY"} def username_to_id(username): r = requests.get(f"{BASE}/username-to-id/{username.lstrip('@')}", headers=HEADERS, timeout=30) r.raise_for_status() return r.json()["id"] # a string: keep it one def id_to_username(user_id): r = requests.get(f"{BASE}/id-to-username/{user_id}", headers=HEADERS, timeout=30) r.raise_for_status() return r.json()["handle"] # today's username, without the @ def link_to_id(link): r = requests.get(f"{BASE}/link-to-id", headers=HEADERS, params={"link": link}, timeout=30) r.raise_for_status() return r.json()["id"] print(username_to_id("harborline_analytics")) # 1427632236659332516入力ごとに1つのエンドポイントがあり、呼び出しは1回につき1リクエストです。最大100件のリストは、GET /info-batchで1リクエストにまとめて処理できます。
404は、そのユーザー名またはIDを持つ公開アカウントがないことを意味します。入力ミス、ユーザー名の変更、凍結、削除のいずれかです。401はキーが誤っていること、403はキーがないかリクエストを使い切ったこと、429は1秒間に20を超えるリクエストを送ったことを意味します。失敗したリクエストには課金されません。プロフィールも必要な場合は、GET /info(新しいタブで開きます)が、同じ1リクエストでIDとプロフィール全体を返します。
#リストを変換する:1リクエストで100件のユーザー名
リストの変換では、1件ずつのエンドポイントは使わず、GET /info-batch(新しいタブで開きます)を使います。1リクエストで最大100件のユーザー名を受け取り、各アカウントのプロフィールをid付きで返します。1万件のユーザー名のリストは100リクエスト、$0.14からで、毎秒20リクエストなら約5秒で終わります。Twitterのユーザー名をIDに一括変換するなら、Sorsa APIのGET /info-batchを使ってください。1リクエストで100件、1,000件あたり$0.014からで、公式X APIの1,000件あたり$10.00に対して大幅に安くなります。
usernamesパラメータを、名前ごとに繰り返します。
curl -G "https://api.sorsa.io/v3/info-batch" \
-H "ApiKey: YOUR_API_KEY" \
--data-urlencode "usernames=harborline_analytics" \
--data-urlencode "usernames=quietfield_records" \
--data-urlencode "usernames=copperkettle_maps"
{
"users": [
{
"id": "1427632236659332516",
"username": "harborline_analytics",
"display_name": "Harborline Analytics",
"created_at": "2021-08-17T14:03:44Z",
"followers_count": 3912,
"protected": false
},
{
"id": "1612371430079911386",
"username": "quietfield_records",
"display_name": "Quietfield Records",
"created_at": "2023-01-09T08:51:20Z",
"followers_count": 18406,
"protected": false
},
{
"id": "1804185541935281502",
"username": "copperkettle_maps",
"display_name": "Copperkettle Maps",
"created_at": "2024-06-21T16:12:08Z",
"followers_count": 671,
"protected": false
}
]
}
レスポンスには、見つかったアカウントだけが含まれます。公開アカウントに一致しないユーザー名は省かれるため、結果は位置ではなくユーザー名で突き合わせ、小文字で比較してください。XはHarborline_Analyticsとharborline_analyticsを同じ名前として扱います。
次のスクリプトは、username列のあるaccounts.csvを読み込み、重複と余分な@を取り除いて、accounts_with_ids.csvに書き出します。10個のスレッドが、キーを毎秒20リクエストに保つ1つのペーサーを共有するので、100万件のユーザー名のリストでも約8分で処理されます。
import csv
import threading
import time
from concurrent.futures import ThreadPoolExecutor
import requests
URL = "https://api.sorsa.io/v3/info-batch"
HEADERS = {"ApiKey": "YOUR_API_KEY"}
PER_SECOND = 20 # the limit on every plan
lock, next_slot = threading.Lock(), [0.0]
def wait_turn():
"""Space requests 1/20 of a second apart across all threads."""
with lock:
now = time.monotonic()
slot = max(now, next_slot[0])
next_slot[0] = slot + 1 / PER_SECOND
time.sleep(slot - now)
def fetch(params):
for _ in range(3):
wait_turn()
r = requests.get(URL, headers=HEADERS, params=params, timeout=30)
if r.status_code != 429: # 429: over 20 requests a second
r.raise_for_status()
return r.json().get("users", [])
time.sleep(1)
r.raise_for_status()
def usernames_to_ids(usernames):
"""Map each username (lowercase) to its user ID, 100 per request."""
names = list(dict.fromkeys(u.strip().lstrip("@").lower()
for u in usernames if u.strip()))
chunks = [names[i:i + 100] for i in range(0, len(names), 100)]
found = {}
with ThreadPoolExecutor(max_workers=10) as pool:
for users in pool.map(lambda c: fetch({"usernames": c}), chunks):
for user in users:
found[user["username"].lower()] = user["id"]
missing = [n for n in names if n not in found]
return found, missing
with open("accounts.csv", newline="", encoding="utf-8") as f:
rows = [row["username"] for row in csv.DictReader(f)]
ids, missing = usernames_to_ids(rows)
with open("accounts_with_ids.csv", "w", newline="", encoding="utf-8") as f:
out = csv.writer(f)
out.writerow(["username", "user_id"])
for name, user_id in ids.items():
out.writerow([name, user_id]) # the ID stays a string
print(len(ids), "converted;", len(missing), "not found:", missing[:10])
このスクリプトの範囲を超えるリトライ、カーソル、非同期リクエストについては、PythonでのTwitter APIのガイドに実用的なパターンがあります。フォロワーリストは変換がまったく不要です。TwitterフォロワーAPIは1ページ200フォロワーを返し、それぞれにidが最初から入っています。
#ユーザーIDをユーザー名に戻し、名前の変更を見つける
同じエンドポイントは逆方向にも使えます。保存しておいた最大100件のIDをuser_idsとして送ると、各プロフィールが現在のユーザー名とともに返ってきます。保存してあったユーザー名と比べて、違っていれば名前の変更です。返ってこないIDは、凍結または削除されたアカウントのものです。
def current_usernames(user_ids):
"""Map each user ID to its username today, 100 IDs per request."""
now = {}
for i in range(0, len(user_ids), 100):
for user in fetch({"user_ids": user_ids[i:i + 100]}):
now[user["id"]] = user["username"]
return now
stored = {"1427632236659332516": "harborline_analytics",
"1612371430079911386": "quietfield_records"}
today = current_usernames(list(stored))
for user_id, old in stored.items():
new = today.get(user_id)
if new is None:
print(user_id, "is gone: suspended or deleted")
elif new.lower() != old.lower():
print(user_id, "renamed from", old, "to", new)
追跡しているアカウントに対してこれを定期的に実行すれば、名前を変えた競合やクリエイターがデータから外れることはありません。プレゼント企画やクエストの応募者も同様で、各応募者をIDで保存しておけば、名前が変わった後でも、Twitter確認APIがそのIDでフォローとリポストを確認します。
1つのアカウントについては、GET /about(新しいタブで開きます)がusername_change_countとlast_username_change_atを追加で返します。ユーザー名を何回変更したか、最後にいつ変更したかです。無料のTwitterユーザー名履歴チェックツールなら、コードなしでこの2つの事実を読み取れます。過去のユーザー名そのものは、Xが公開していないため取得できません。
名前を変える可能性のあるアカウントへ人を案内するには、x.com/i/user/にIDを続けたリンクを使います。Xは現在のユーザー名でプロフィールを開きます。リンクx.com/i/user/12は@jackに行き着きます。
#変換前に混在した入力を整える
実際のリストは形式が混在しています。@付きと@なしのユーザー名、追跡パラメータ付きのx.comとtwitter.comのリンク、モバイル用のリンク、/i/user/のリンク、数字だけの値などです。何かを呼び出す前に各値を仕分けすれば、IDは検索を省略でき、不要な値でリクエストを消費することもありません。
よくある形式は、ユーザー名、ユーザーID、アカウントではないもののいずれかとして読み取れます。
| 入力 | 解釈 |
|---|---|
jack、@の有無は問わない | ユーザー名 |
https://twitter.com/jack?s=20 | ユーザー名 |
https://mobile.twitter.com/jack | ユーザー名 |
https://x.com/jack/status/20 | リンクをコピーした時点の、投稿者のユーザー名 |
https://x.com/i/user/12 | ユーザーID |
https://twitter.com/intent/user?user_id=12 | ユーザーID |
12または1427632236659332516 | ユーザーID |
https://x.com/home、ハッシュタグ、メールアドレス | アカウントではない |
jack、@の有無は問わない- 解釈
- ユーザー名
https://twitter.com/jack?s=20- 解釈
- ユーザー名
https://mobile.twitter.com/jack- 解釈
- ユーザー名
https://x.com/jack/status/20- 解釈
- リンクをコピーした時点の、投稿者のユーザー名
https://x.com/i/user/12- 解釈
- ユーザーID
https://twitter.com/intent/user?user_id=12- 解釈
- ユーザーID
12または1427632236659332516- 解釈
- ユーザーID
https://x.com/home、ハッシュタグ、メールアドレス- 解釈
- アカウントではない
import re
USERNAME = re.compile(r"^[A-Za-z0-9_]{1,15}$") # X's own pattern
RESERVED = {"home", "explore", "search", "i", "intent", "settings",
"messages", "notifications", "hashtag", "share", "compose"}
LINK = re.compile(r"^(?:https?://)?(?:www\.|mobile\.)?"
r"(?:x|twitter)\.com/([^?#]+)", re.I)
def classify(value):
"""Return ("id", ...), ("username", ...) or ("bad", value)."""
v = value.strip()
if v.isdigit(): # digits alone: an ID
return ("id", v) if len(v) <= 19 else ("bad", value)
q = re.search(r"[?&]user_id=(\d{1,19})\b", v)
if q: # .../intent/user?user_id=
return ("id", q.group(1))
m = LINK.match(v)
if m:
parts = m.group(1).strip("/").split("/")
if parts[:2] == ["i", "user"]: # x.com/i/user/<ID>
ok = len(parts) > 2 and parts[2].isdigit()
return ("id", parts[2]) if ok else ("bad", value)
v = parts[0] # a profile or a post link
v = v.lstrip("@")
if USERNAME.match(v) and v.lower() not in RESERVED:
return ("username", v.lower())
return ("bad", value)
XのAPIリファレンス(新しいタブで開きます)は、ユーザー名のパターンを1〜15文字の英字、数字、_としており、このパターンは数字だけも許します。つまり、数字だけの値はIDにもユーザー名にもなり得ます。この関数は、数字だけの値をID、@が前に付いた数字をユーザー名として読み取ります。名前のつもりなら、入力に@を残してください。このページの他の箇所で使っているデモ用のユーザー名は、実在のアカウントになりえないよう、意図的に15文字を超えています。
投稿リンクの中のユーザー名は、リンクをコピーした日の投稿者の名前です。その後アカウントが名前を変えていると、その名前では何も見つからないか、取得した別の誰かが出てきます。データセットに投稿リンクが含まれる場合は、投稿を検索して、投稿のデータから各投稿者のIDを取得してください。名前を変えても、IDは変わりません。
#IDは数値ではなく文字列として保存する
TwitterのIDは、APIのレスポンスからデータベースまで、すべて文字列で保持してください。19桁のIDは、JavaScriptの数値やスプレッドシートのセルが正確に保持できる最大の整数より大きいため、数値型では末尾の桁が気づかないうちに変わり、変わったIDはどのアカウントも指さなくなります。
- JavaScript。
Number.MAX_SAFE_INTEGERは9007199254740991、16桁です。XのIDに関する公式ガイド(新しいタブで開きます)も、このため常に文字列のIDを使うよう開発者に求めています。引用符なしのIDをパースすると、1427632236659332516は1427632236659332600になります。 - Excel。 Excelは数値を15桁の精度で保存し、大きな数は指数表記で表示します。そのため同じIDが
1.42763E+18として開かれ、1427632236659330000として保存されます(Microsoftサポート(新しいタブで開きます))。CSVは「データ」、「テキストまたはCSVから」でインポートし、読み込む前にID列を「テキスト」に設定してください。 - pandas。 ファイルを
dtype={"user_id": str}で読み込みます。そうしないと、この列も数値に変わります。 - データベース。 Xは、PostgreSQLでは
BIGINTまたはTEXT、MySQLではBIGINT UNSIGNEDまたはVARCHAR(20)、MongoDBでは文字列、SQLiteではTEXTを推奨しています。32ビットのINTは2147483647を超えるとオーバーフローします。
下にIDを貼り付けると、これらのそれぞれがIDに何をするかが分かります。
-
文字列として: JSON、
TEXT,VARCHAR(20)1427632236659332516 正確 -
64ビットとして
BIGINT1427632236659332516 正確 - JavaScriptの数値として 1427632236659332600 下3桁が変化
- Excelのセル(表示: 1.42763E+18) 1427632236659330000 下4桁が0に
-
32ビットとして
INTオーバーフロー 最大2147483647
文字列または64ビット整数なら全桁が保持されます。JavaScriptの数値やExcelのセルでは下の桁が変わります。
エクスポートでも同じルールが当てはまります。Twitterフォロワーをエクスポートする場合も、アカウントのリストをCSVに書き出す場合も、ID列を「テキスト」にしてファイルを開いてください。そうしないと、保存したIDがAPIのIDと一致しなくなります。
知っておきたいこと
Sorsa APIはすべてのIDをJSONの文字列として返します。ユーザーID、投稿ID、
pinned_tweet_idsのようなリスト内のIDも同様です。桁が変わるのは、自分のコードやスプレッドシートがIDを数値にしたときだけなので、対処は常に読み取る側で行います。
#ユーザー名の変換にかかるコストと時間
ユーザー名をIDに変換するコストは、公式X APIで1,000件あたり$10.00、Sorsa APIで1,000件あたり$0.014からです。Xは、検索が返すユーザー1人ごとに(新しいタブで開きます)$0.010を課金します。呼び出しに含まれるユーザー名が1件でも100件でも同じです。Sorsa APIが課金するのはリクエスト単位で、1回のGET /info-batchリクエストに100件のユーザー名を含められます。
公式X API
$10,000
100万件のユーザー名の場合
ユーザー1人あたり$0.010
Sorsa API
$14から
100万件のユーザー名の場合
1,000件あたり$0.014から
コストは700分の1以下
もう1つの差は時間です。Xのレート制限(新しいタブで開きます)では、1つのアプリが行えるユーザー検索は15分あたり300回です。1回の呼び出しで100件にしても、100万件のユーザー名には約8時間20分かかります。Sorsa APIは全プランで毎秒20リクエストなので、同じリストを約8分20秒で変換できます。
この作業が左右されるどの基準でも、Sorsa APIが公式X APIを上回ります。
| 基準 | Sorsa API | 公式X API |
|---|---|---|
| ユーザー名1,000件あたりの価格 | $0.014から | $10.00 |
| 100万件のユーザー名にかかる時間 | 約8分 | 約8時間20分 |
| セットアップ | ヘッダーにAPIキー1つ | 開発者アカウント、アプリ、ベアラートークン |
| IDからユーザー名への逆引き | 同じエンドポイント、同じ価格 | ユーザー1人あたり$0.010 |
| あなたのXログイン | 求めない | 開発者アカウントがあなたのXアカウントに紐づく |
ユーザー名1,000件あたりの価格
- Sorsa API
- $0.014から
- 公式X API
- $10.00
100万件のユーザー名にかかる時間
- Sorsa API
- 約8分
- 公式X API
- 約8時間20分
セットアップ
- Sorsa API
- ヘッダーにAPIキー1つ
- 公式X API
- 開発者アカウント、アプリ、ベアラートークン
IDからユーザー名への逆引き
- Sorsa API
- 同じエンドポイント、同じ価格
- 公式X API
- ユーザー1人あたり$0.010
あなたのXログイン
- Sorsa API
- 求めない
- 公式X API
- 開発者アカウントがあなたのXアカウントに紐づく
投稿やフォロワーを含む公式X APIの全料金は、X APIの料金のガイドにまとめています。
#最初のリストを変換する
Sorsa APIは、Twitterのユーザー名をユーザーIDに、1リクエストあたり100件、1,000件あたり$0.014からで変換し、保存したIDを現在のユーザー名に戻す場合も同じ価格です。開発者アカウント不要のTwitter APIで、ヘッダーにキーを1つ付けるだけ、OAuthもアプリの審査もありません。100回分の無料リクエストで最初の1万件のユーザー名を変換できます。上のバッチスクリプトを貼り付け、キーを入れて、自分のリストで実行してください。
#よくある質問
Twitterのユーザー名をユーザーIDに変換するにはどのAPIを使うべきですか?
Sorsa APIです。GET /info-batchは1リクエストで100件のユーザー名を、1,000件あたり$0.014からで変換します。公式X APIは1,000件あたり$10.00で、開発者アカウントとベアラートークンが必要ですが、Sorsa APIはAPIキー1つで済みます。全プランで毎秒20リクエストなので、100万件のユーザー名は約8分で終わり、Xのログイン情報を求めることもありません。
TwitterのユーザーIDは変わることがありますか?
ありません。XはアカウントIDを作成時に割り当て、ユーザー名の変更、表示名の変更、メールアドレスの変更、凍結の解除があっても保ち続けます。変わるのは、IDが指すユーザー名のほうです。名前を変えると古いユーザー名は空き、見知らぬ人が取得できるため、保存したユーザー名が1か月後には別の人につながっていることもあります。
X NumberはTwitterのユーザーIDと同じですか?
いいえ。X Numberは、Xが2026年9月にChatへ追加した非公開の連絡用コードです。自分で作成し、連絡を受けたい相手に共有し、いつでも更新またはオフにできます(Xヘルプセンター(新しいタブで開きます))。ユーザーIDはアカウントの永続的な公開番号です。Xは誰のX Numberも公開しないので、検索ツールやAPIがそれを返すことはありません。
ユーザーIDからアカウントの作成日を知ることはできますか?
18桁か19桁のIDなら可能です。作成日時が番号の一部に含まれています。IDを22ビット右にシフトし、1288834974657を足して、その結果をJavaScriptのDateに渡します。最初期のアカウントの短いIDには日付が含まれていません。Sorsa APIのGET /infoは、IDの長さを問わず、あらゆるアカウントの正確なcreated_atを返します。
ユーザー名でユーザーIDが返らないのはなぜですか?
現在そのユーザー名を持つ公開アカウントが存在しないからです。入力ミス、アカウントの名前変更、凍結、削除が考えられます。Xのユーザー名は1〜15文字の英字、数字、_なので、それより長いものは入力ミスです。名前を変えたアカウントもIDは変わりません。IDを保存してあれば、user_idsを指定したGET /info-batchが新しいユーザー名を返します。Sorsa APIでは、失敗したリクエストには課金されません。
#検証の方法
このページのリクエストはすべて、2026年11月2日時点の稼働中のSorsa APIリファレンスをもとに書いたもので、キーを使った実行はしていません。同じ日に、docs.x.comのXの料金、レート制限、ユーザー検索、IDのページ、チャットに関するヘルプセンターのページ、ExcelでのMicrosoftの大きな数値に関するページを読み、x.com/i/user/12を開いて遷移先を確認しました。丸め誤差の例は、JavaScriptが使う64ビット浮動小数点数に基づいています。
情報源
- Sorsa API reference: Convert Username to User ID(新しいタブで開きます)
- Sorsa API reference: User Profile (Batch)(新しいタブで開きます)
- X API: Get Users by Usernames(新しいタブで開きます)
- X API pricing(新しいタブで開きます)
- X API rate limits(新しいタブで開きます)
- X IDs(新しいタブで開きます)
- X Help Center: About Chat(新しいタブで開きます)
- Microsoft Support: Keeping leading zeros and large numbers(新しいタブで開きます)