X API OAuth 2.0 PKCE認証をClaude Codeで設定する完全ガイド

X API OAuth 2.0 PKCE認証をClaude Codeで設定する完全ガイド

執筆・編集:株式会社Nexa 編集部(下書きと機械検査はAI、公開前に人が確認)/約20分で読めます。公式ドキュメント・公式発表などの一次情報を確認したうえで執筆・更新しています。AIツールの仕様や料金は変わることがあるため、導入判断の前に各公式サイトの最新情報もご確認ください。記事の作り方と検査の方針

X APIはBearer Token(読み取り)とOAuth 2.0 PKCE(書き込み)の2種類を使い分けることで安全に自動化できる。

  • 要点1: Bearer Tokenは公開情報の読み取りのみ。投稿・フォローにはOAuth 2.0 PKCEが必要
  • 要点2: CLIツール用途ではNative App(Public Client)が最適。Client Secretが不要でシンプル
  • 要点3: macOS KeychainでAPIキーを管理することでコードへのハードコードを防げる

対象: Claude CodeでX APIを活用して業務自動化したいエンジニア・DX推進担当者

今日やること: Developer Portalでアプリを作成し、Bearer TokenとClient IDをKeychainに登録する

この記事の著者
株式会社Nexa 代表取締役川島 陸

一橋大学経済学部卒業後、フォーティエンスコンサルティング株式会社(旧 株式会社クニエ)にて法人向けAI導入支援等を経験。独立後、AI系メディア運営やDify/n8nの導入支援を経て、株式会社Nexaを創業。法人向けAI研修・AI導入支援・AI関連メディア運営を手掛ける。

X APIを使えば、Xの投稿作成・競合アカウントのリサーチ・タイムライン監視などの業務を自動化できます。しかし「認証設定でつまずいた」「Bearer Tokenは取れたが投稿できない」「OAuth 2.0 PKCEって何から始めればよいか分からない」という声は多くあります。

この記事では、あるメディア運営者がX APIを活用した投稿自動化を実現するまでの実装経験をもとに、Bearer TokenとOAuth 2.0 PKCEの使い分け、Native Appの選択理由、macOS Keychainによるセキュアな管理方法、そして実際に遭遇したトラブルと解決策を解説します。

本記事の認証方式・料金制度に関する記述は、X API公式ドキュメント(docs.x.com)を2026年10月7日に確認した内容に基づいています。

X APIの認証方式は2種類 — Bearer TokenとOAuth 2.0 PKCEの使い分け

X API v2では、用途に応じて2つの認証方式を使い分ける必要があります。この違いを理解せずに実装を始めると、後から大幅な修正が必要になります。

Bearer Token(App-only認証)とは — 読み取り専用の認証

Bearer Tokenは「アプリ自体」として認証する方式です。特定のユーザーとして操作するのではなく、アプリケーションとして公開情報を読み取ります。

設定は簡単で、Developer PortalからBearer Tokenを1回コピーするだけです。有効期限もなく、更新不要で使い続けられます。

Bearer Tokenで可能な操作:– 公開ツイートの検索(/2/tweets/search/recent)- ユーザープロフィールの取得(/2/users)- ツイートの詳細取得(/2/tweets/:id)

一方、「誰かの代わりにツイートする」「特定ユーザーのフォローリストを見る」といった操作は、そのユーザーの許可が必要なためBearer Tokenでは行えません。

OAuth 2.0 PKCE — 投稿・フォローなどユーザー操作に使う認証

OAuth 2.0 PKCEは「ユーザーの代わりに」操作する方式です。PKCEとはProof Key for Code Exchangeの略で、認可コードを安全に交換するための仕組みです。

認証フローはこうなります。まず認可URLをブラウザで開きます。ユーザーがXにログインして「アプリを認証」を押すと、コールバックURLにアクセストークンの元となるコードが返ってきます。そのコードをアクセストークンに交換して保存することで、以降はそのトークンで操作できるようになります。

OAuth 2.0 PKCEで可能な操作:– ツイートの投稿・削除(/2/tweets)- リツイート・いいね- フォロー・アンフォロー- DM送受信(dm.read / dm.writeスコープ追加が必要)- メディアアップロード(media.writeスコープ追加が必要)

2つの認証方式の比較表

項目 Bearer Token OAuth 2.0 PKCE
用途 公開情報の読み取り ユーザーとしての操作
設定の手間 少ない(コピーするだけ) やや多い(認証フロー実装が必要)
有効期限 なし アクセストークンは2時間(リフレッシュトークンで自動更新可)
レート制限 標準 エンドポイントごとに異なる(ユーザー単位。例: 一部エンドポイントは15分あたり最大900リクエスト)
必要なもの Bearer Token Client ID + 認証フロー実装

実務的には、競合アカウントのリサーチや検索はBearer Token、ツイート投稿や下書き作成はOAuth 2.0 PKCEと使い分けるのが基本です。

Developer Portalでアプリを作成する手順

Developer Portalへのアクセスと登録(2026年版)

2026年現在、X APIのDeveloper Portalは console.x.com で提供されています。以前の developer.twitter.com は統合済みです。

登録時の注意点として、2026年2月よりX APIは従量課金制(pay-per-use)に移行しました。Developer Consoleでクレジットを事前購入し、APIを呼び出した分だけ差し引かれる仕組みで、月額の最低利用料はありません。ただし、アプリの作成自体は無料で行えます。詳細はX API公式の料金ページで確認できます。

重要な選択肢:Native AppとWeb Appの違い

アプリ作成時に「Type of App」を選択する画面が表示されます。ここでの選択がその後の設定に大きく影響するため、慎重に選んでください。

アプリ種別 クライアントタイプ 必要な認証情報 用途
Native App Public Client Client IDのみ CLIツール、デスクトップアプリ
Web App / Automated App or Bot Confidential Client Client ID + Client Secret Webサービス、サーバーサイドアプリ

Claude CodeなどのCLIツールとして使う場合は、Native Appを選択してください。

実際の実装経験では、最初にWeb Appを選択してしまい、Client SecretとClient IDの両方が必要になって設定が複雑化しました。さらに、Web Appで取得したトークンはPublic ClientのNative App用とは互換性がなく、アプリを作り直す必要が生じました。

Native Appを選ぶと、PKCEのみでセキュリティが担保されるため、Client Secretの管理が不要でシンプルです。

User authentication settingsの設定(Callback URI設定)

アプリ作成後、「User authentication settings」でOAuth 2.0を有効化します。以下の設定が必要です。

設定項目 値
App permissions Read and write
Type of App Native App
Callback URI / Redirect URL http://127.0.0.1:3891/callback
Website URL 任意(例: https://example.com)

Callback URIの登録は必須です。 ここに http://127.0.0.1:3891/callback を登録しておかないと、認証フローの最終ステップで「このURLは登録されていません」というエラーが返ります。この設定を忘れて認証が完了しないというトラブルは非常によく起こります。

ポイントCallback URIは、認証後にブラウザがリダイレクトされるURLです。ローカルで実行する場合は http://127.0.0.1:3891/callback のようにlocalhostのポートを指定します。Developer Portalに事前登録していない場合、X側が「不正なリダイレクト先」として認証を拒否します。

Bearer TokenとClient IDの取得

設定が完了したら、「Keys and tokens」タブから以下の2つを取得します。

  1. Bearer Token: 「Authentication Tokens」の「Bearer Token」横の「Generate」をクリック
  2. Client ID: 「OAuth 2.0 Client ID and Client Secret」セクションに表示されている「Client ID」をコピー

Native Appの場合、Client Secretは表示されません(不要です)。

macOS KeychainにAPIキーをセキュアに保存する

なぜKeychainを使うのか — APIキー漏洩のリスクを避ける

APIキーをスクリプトに直接書き込む(ハードコード)と、コードをGitHubにプッシュした際に漏洩するリスクがあります。また、環境変数に設定する方法もありますが、シェルの履歴に残ったり、envコマンドで誰でも見られたりする問題があります。

macOS Keychainは、OSレベルでAES-256-GCM暗号化によって保護された認証情報の保管庫です。Macにログインした時点で自動的にアンロックされ、許可されたアプリケーションのみがアクセスできます。

APIキーの管理にKeychainを使うことで、コードへの直接記述を避けつつ、コマンドラインからも安全に値を取得できます。

securityコマンドでKeychainに登録する

macOSの security コマンドを使って、Keychainへの登録・取得が行えます。

登録:

# Bearer Tokenを登録
security add-generic-password -a "$USER" -s "X_BEARER_TOKEN" -w "ここにBearer Token"

# Client IDを登録
security add-generic-password -a "$USER" -s "X_CLIENT_ID" -w "ここにClient ID"

取得:

security find-generic-password -s "X_BEARER_TOKEN" -w

削除(更新時):

security delete-generic-password -s "X_BEARER_TOKEN"
# 削除後に再登録
security add-generic-password -a "$USER" -s "X_BEARER_TOKEN" -w "新しいBearer Token"

スクリプトからKeychainの値を取得する

Pythonスクリプト内でKeychainの値を取得するには、subprocessモジュールで security コマンドを呼び出します。

import subprocess

def get_keychain_value(service_name: str) -> str:
    """macOS KeychainからAPIキーを取得する"""
    result = subprocess.run(
        ["security", "find-generic-password", "-s", service_name, "-w"],
        capture_output=True, text=True
    )
    if result.returncode != 0:
        raise ValueError(f"{service_name}がKeychainに登録されていません")
    return result.stdout.strip()

# 使用例
bearer_token = get_keychain_value("X_BEARER_TOKEN")
client_id = get_keychain_value("X_CLIENT_ID")

このパターンを使えば、コード内にAPIキーを直接書かずに済みます。(Claude Code Skillsの設計パターンについてはこちら →)


AI導入に関するお困りごとは、株式会社NexaのAI顧問がサポートします。「何から始めればいいか分からない」という段階からご相談いただけます。

AI顧問の無料相談はこちら →


OAuth 2.0 PKCEの認証フローを実装する

認証フローの全体像(PKCE方式の仕組み)

OAuth 2.0 PKCEの認証は、以下の流れで行います。

1. code_verifier(ランダム文字列)を生成
2. code_verifier をSHA-256ハッシュ化して code_challenge を作成
3. code_challenge を含む認可URLをブラウザで開く
4. ユーザーがXでログインして「アプリを認証」を押す
5. コールバックURLに認可コード(code)が返ってくる
6. code + code_verifier をX APIに送ってアクセストークンに交換
7. アクセストークンをファイルに保存
8. 期限切れ(2時間後)になったらリフレッシュトークンで自動更新

PKCEの仕組みのポイントは「code_verifier」と「code_challenge」の関係です。code_verifierはランダム生成された秘密の値で、code_challengeはその変換値(ハッシュ)です。認可URLにはcode_challengeのみを含め、トークン交換時にcode_verifierを送ります。これにより、認可コードが傍受されても、code_verifierを知らない第三者はトークンを取得できません。仕組みの詳細はX API公式のOAuth 2.0ドキュメントで確認できます。

Pythonでの実装例 — ローカルコールバックサーバーを立てる

import hashlib, base64, os, secrets, urllib.parse
from http.server import HTTPServer, BaseHTTPRequestHandler
import webbrowser

CLIENT_ID = get_keychain_value("X_CLIENT_ID")
CALLBACK_URL = "http://127.0.0.1:3891/callback"
SCOPE = "tweet.read tweet.write users.read offline.access media.write"

def generate_pkce():
    """code_verifier と code_challenge を生成する"""
    code_verifier = base64.urlsafe_b64encode(secrets.token_bytes(32)).rstrip(b'=').decode()
    code_challenge = base64.urlsafe_b64encode(
        hashlib.sha256(code_verifier.encode()).digest()
    ).rstrip(b'=').decode()
    return code_verifier, code_challenge

def get_auth_url(code_challenge: str, state: str) -> str:
    """認可URLを生成する"""
    params = {
        "response_type": "code",
        "client_id": CLIENT_ID,
        "redirect_uri": CALLBACK_URL,
        "scope": SCOPE,
        "state": state,
        "code_challenge": code_challenge,
        "code_challenge_method": "S256",
    }
    return "https://x.com/i/oauth2/authorize?" + urllib.parse.urlencode(params)

注意点: favicon問題への対処

コールバックサーバーを実装する際、http.server.BaseHTTPRequestHandler を使って「1リクエストだけ受け付けて終了」という実装にすると、ブラウザが送るfavicon.ico取得リクエストが本来のコールバックより先に処理されてしまい、認証が失敗することがあります。

対処法は「認可コード(codeパラメータ)が含まれるリクエストが来るまでループで待ち続ける」実装にすることです。

received_code = None

class CallbackHandler(BaseHTTPRequestHandler):
    def do_GET(self):
        global received_code
        parsed = urllib.parse.urlparse(self.path)
        params = urllib.parse.parse_qs(parsed.query)
        # codeが含まれていないリクエスト(favicon等)は無視する
        if "code" in params:
            received_code = params["code"][0]
        self.send_response(200)
        self.end_headers()
        self.wfile.write(b"Authenticated. You can close this tab.")

    def log_message(self, format, *args):
        pass  # ログを抑制

# サーバーをcodeが取得できるまで動かし続ける
server = HTTPServer(("127.0.0.1", 3891), CallbackHandler)
while received_code is None:
    server.handle_request()

トークンの自動リフレッシュ実装

offline.access スコープを含めて認証すると、リフレッシュトークンが発行されます。アクセストークンの有効期限(2時間)が切れた際に、ユーザーを再認証させることなく自動的にトークンを更新できます。

import json, time

TOKEN_FILE = "~/.x_tokens.json"

def load_tokens() -> dict:
    with open(os.path.expanduser(TOKEN_FILE)) as f:
        return json.load(f)

def save_tokens(tokens: dict):
    with open(os.path.expanduser(TOKEN_FILE), "w") as f:
        json.dump(tokens, f)

def get_valid_access_token() -> str:
    """有効なアクセストークンを返す(期限切れなら自動リフレッシュ)"""
    tokens = load_tokens()
    # 有効期限の10分前にリフレッシュする
    if tokens["expires_at"] - time.time() < 600:
        tokens = refresh_access_token(tokens["refresh_token"])
        save_tokens(tokens)
    return tokens["access_token"]

(X APIを使った競合アカウント分析の実装例はこちら →)

よくあるエラーと解決策 — 実際に遭遇したトラブル事例

401エラーが続く — OAuth 1.0aからOAuth 2.0 PKCEへの切り替えで解決

あるメディア運用者が最初にOAuth 1.0aで実装を試みたところ、API KeyとAccess Tokenを正しく設定しているにも関わらず401エラーが続きました。Access Tokenを再生成しても、Keychain登録をやり直しても解消しませんでした。

原因は、X APIの現在の推奨認証方式がOAuth 2.0 PKCEであることを見落としていたことです。Developer ConsoleでUser authentication settingsを確認すると、OAuth 2.0が有効化されておらず、OAuth 1.0a専用の設定になっていました。

解決策: OAuth 2.0を有効化し、Native Appとして認証フローを実装し直すことで、401エラーが完全に解消しました。

補足 OAuth 1.0aも現在も利用できますが、Consumer KeyとAccess Tokenの4つのキー(Consumer Key / Consumer Secret / Access Token / Access Token Secret)を管理する必要があります。OAuth 2.0 PKCEは X_BEARER_TOKEN と X_CLIENT_ID の2つだけで管理できるため、よりシンプルです。

Native AppとWeb Appの選択ミス — CLIツールにはNative Appが最適

最初にWeb App(Confidential Client)を選択してアプリを作成してしまうと、Client IDに加えてClient Secretも必要になります。CLIツールとしてKeychainで管理する場合は問題ありませんが、トークン発行の仕組みが変わるため、既にNative Appで取得したトークンとの互換性がありません。

解決策: Developer Portalで「User authentication settings → Edit」からアプリタイプをNative Appに変更するか、アプリを作り直してください。Native Appに変更するとClient Secretは表示されなくなります(不要です)。

コールバックURIの未登録で認証が完了しない

認証フローを実行してブラウザでXの認証画面を開き、「アプリを認証」を押した後に「コールバックURIが登録されていません」というエラーが返る場合、Developer Portalのコールバック設定が抜けています。

解決策: Developer Portal → App → User authentication settings → Callback URI に http://127.0.0.1:3891/callback を追加してください。

v1.1 media/uploadの廃止 — v2エンドポイントへの移行

2025年6月9日にX APIのv1.1 media/upload エンドポイントが廃止されました(当初は2025年3月31日に廃止予定でしたが延長されました)。画像付き投稿が突然403エラーになる場合、このエンドポイントの廃止が原因です。

解決策: /2/media/upload(v2エンドポイント)に移行してください。また、v2のメディアアップロードには media.write スコープが必要なため、トークンを再取得する必要があります(スコープ変更後に再認証が必要です)。

1つのアプリで複数Xアカウントを管理する

OAuth 2.0の仕組み — Client IDは「アプリ」、トークンは「ユーザー認証」

「X Developer Portalで作ったアプリのアカウント以外は使えないのでは?」と思われることがありますが、それは誤解です。

OAuth 2.0の仕組みでは、Client ID(アプリのID)と、ユーザーが「このアプリを使うことを許可する」という認可(アクセストークン)は分離しています。1つのClient IDに対して、何人のユーザーでも認可フローを経てトークンを発行できます。

Client ID(アプリ): 1つ
  ├── @account_A のアクセストークン → tokens/account_a.json
  ├── @account_B のアクセストークン → tokens/account_b.json
  └── @account_C のアクセストークン → tokens/account_c.json

アカウントごとにトークンファイルを分けて管理する

複数アカウントのトークンを管理する実装パターンとして、アカウント名をキーにしたファイル管理が有効です。

import json, os

TOKEN_DIR = os.path.expanduser("~/.x_tokens/")

def get_token_file(account_name: str) -> str:
    return os.path.join(TOKEN_DIR, f"{account_name}.json")

def switch_account(account_name: str) -> dict:
    """アカウントを切り替えてそのトークン情報を返す"""
    token_file = get_token_file(account_name)
    if not os.path.exists(token_file):
        raise ValueError(f"{account_name}のトークンが見つかりません。先にauthを実行してください")
    with open(token_file) as f:
        return json.load(f)

各アカウントの初回認証時に、ブラウザでそのアカウントにログインした状態で認可フローを実行することで、そのアカウントのトークンが tokens/アカウント名.json に保存されます。(Claude CodeでツイートをXに下書き投稿する方法はこちら →)

\ AI活用の「次の一手」を一緒に考えませんか /

AI顧問の無料相談はこちら

よくある質問

Q. Bearer TokenとOAuth 2.0 PKCEはどう使い分ければよいですか?

ユーザーの操作を代替する必要があるかどうかで判断します。ツイート検索・ユーザープロフィール取得など「公開情報の読み取り」だけであればBearer Tokenで十分です。ツイートの投稿・いいね・フォローなど「アカウントとして何かを行う」操作にはOAuth 2.0 PKCEが必要です。

Q. Native AppとWeb App(Confidential Client)はどちらを選ぶべきですか?

Claude CodeなどのCLIツールやデスクトップアプリ用途であれば、Native App(Public Client)を選択してください。Web AppはClient Secretの管理が必要になり、CLIツールでは不要な複雑さが加わります。Webサービスやサーバーサイドアプリとして動作する場合はWeb Appが適しています。

Q. 複数のXアカウントを1つのAPIアプリで管理できますか?

できます。Client ID(アプリ)は1つのまま、OAuth 2.0の認可フローで「どのアカウントとして認証するか」を分けられます。各アカウントの認証時に、そのアカウントでXにログインした状態でブラウザを操作することで、アカウントごとに別々のアクセストークンが発行されます。

Q. アクセストークンの有効期限が切れたらどうなりますか?

offline.access スコープを含めて認証していれば、リフレッシュトークンが発行されます。アクセストークン(有効期限2時間)が切れた際に、リフレッシュトークンを使って自動的に新しいアクセストークンを取得できます。再認証(ブラウザでのログイン)は不要です。

Q. X APIの無料プランでできることに制限はありますか?

2026年2月より従量課金制(pay-per-use)に移行し、無料プランは基本的に廃止されました(Xが指定する一部アプリのみ例外)。クレジットを事前購入し、使った分だけ課金される仕組みで、月額の最低利用料はありません。ツイート投稿(POST /2/tweets)も読み取り系APIも同じ課金の対象です。

まとめ

X APIの認証設定は、最初の選択を誤ると後から修正コストが高くなります。本記事の要点を振り返ります。

  • 認証方式の使い分け: 読み取りはBearer Token、書き込みはOAuth 2.0 PKCE
  • アプリタイプの選択: CLIツールにはNative App(Client Secretが不要でシンプル)
  • Callback URI登録: http://127.0.0.1:3891/callback をDeveloper Portalに必ず設定
  • セキュアなキー管理: macOS KeychainでBearer TokenとClient IDを管理
  • 複数アカウント管理: 1つのClient IDで複数アカウントのトークンを管理できる

次のアクションとして、まずDeveloper Portalで新規アプリを作成し(Native Appを選択)、Bearer TokenとClient IDをKeychainに登録するところから始めてみてください。


法人向けAI導入・活用の月額伴走サービス

AI導入の疑問を、週1回のMTGで相談できる「AI顧問」

株式会社Nexaでは、ChatGPT・Claude・Claude CodeなどのAI導入に関する質問や、社内活用・業務自動化の進め方を週1回相談できる 月額7万円(毎月3社限定で月額5万円)のAI顧問サービス を提供しています。

「自社では何から始めるべきか」「この業務はAI化できるか」「どのツールを選ぶべきか」を、無料相談で整理します。

AI顧問の無料相談はこちら →





AI導入を検討中の方へ

AIの力で、ビジネスを次のステージへ

まずはお気軽にご相談ください。貴社に最適なAI活用プランをご提案します。