本文へスキップ

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をアカウントの現在のユーザー名に戻すこともできます。サインアップは不要です。

Twitter IDファインダー

無料、1回に1アカウント、登録不要。例: ユーザー名jackのユーザーIDは12です。

このフォームは、入力したユーザー名、リンク、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の文字列として返すので、途中で桁が失われることはありません。

  1. キーを取得します。 サインアップして、ダッシュボードからAPIキーをコピーします。100回分の無料リクエストが付き、カードは不要です。

  2. ユーザー名を送ります。 @を付けずに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"
    }
    
  3. 必要なら逆方向にも変換します。 GET /id-to-username(新しいタブで開きます)は、アカウントの現在のユーザー名をhandleというフィールドで返し、GET /link-to-id(新しいタブで開きます)はx.comまたはtwitter.comのプロフィールリンク全体を受け取ります。

  4. 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
    
    手元にあるもの Sorsa APIエンドポイント 返るもの ユーザー名 harborline_analytics GET /username-to-id 各1リクエスト id 1427632236659332516 プロフィールリンク x.com/harborline_analytics GET /link-to-id 各1リクエスト id 1427632236659332516 ユーザーID 1427632236659332516 GET /id-to-username 各1リクエスト ハンドル harborline_analytics 最大100件のユーザー名 または最大100件のユーザーID GET /info-batch 1リクエスト100件 id + username とプロフィール全体
    入力ごとに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パラメータを、名前ごとに繰り返します。

Terminal
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"
Response200 OK
{
  "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分で処理されます。

Python
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は、凍結または削除されたアカウントのものです。

Python
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、ハッシュタグ、メールアドレス

    解釈
    アカウントではない
Python
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ビットとして BIGINT 1427632236659332516 正確
  • 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秒で変換できます。

同じ100万件のユーザー名で、公式X APIは約8時間20分、$10,000。Sorsa APIは約8分20秒、$14からです。

この作業が左右されるどの基準でも、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チーム 読み取り専用のX(Twitter)APIとスクレイピングツール、Sorsa APIを開発・運営しています。2022年から本番稼働し(旧TweetScout API)、累計60億リクエスト以上を処理しています。 Sorsa APIについて