本記事には筆者が開発に関わっているサービス(GetXAPI)の紹介を含みます。公式X APIとの料金・手順の比較はすべて執筆時点の公開情報に基づいています。
X(旧Twitter)のデータをPythonで取得したい。ツイートを検索したい、特定アカウントの投稿を集めたい、フォロワーを分析したい。よくあるニーズですが、2026年の今、「どのAPIをどう使うか」の前提が数年前とは大きく変わっています。
この記事では、現在のX APIの状況を整理した上で、開発者アカウントなしでツイート検索・取得を動かすまでを、コピペで動くPythonコードと一緒に解説します。検索演算子、ページネーション、エラーハンドリング、コスト試算まで一通りカバーします。
2026年のX API事情を3分で整理する
まず前提の整理です。「Twitter APIは無料で使える」という古い記事がまだ大量に検索に出てきますが、現在の状況は違います。
公式X APIの現状(執筆時点の公開ドキュメントより):
- 現在の公式料金ページには、一般向けの無料枠(Freeプラン)の記載がありません。プリペイドの従量課金と、個別見積もりのEnterpriseの2本立てです
- 読み取りは「返されたリソース単位」で課金されます。標準のPost読み取りは1件$0.005。つまり100件返ってくる検索リクエスト1回で$0.50です
- 書き込みはリクエスト単位で、標準の投稿作成は1回$0.015です
- 従量課金には月300万件のPost読み取り上限があり、それを超える規模はEnterprise契約になります
- 利用開始には開発者アカウントの作成、アプリの作成、認証情報の生成、支払い方法の登録が必要です
「返されたリソース単位」という課金モデルは見落としやすいポイントです。リクエスト数ではなく、レスポンスに含まれるツイート数で課金されるため、検索やタイムラインのような複数件返るエンドポイントでは、想定よりコストが速く積み上がります。1,000ツイートの読み取りで$5、10万ツイートで$500です。
選択肢は実質3つ
| 選択肢 | 初期費用 | 1,000ツイートあたり | 主なハードル |
|---|---|---|---|
| 公式X API | プリペイド購入 | $5(標準Post読み取り) | 開発者アカウント+アプリ設定、カード登録 |
| サードパーティAPI(本記事) | $0(無料クレジット) | 約$0.05 | 登録のみ |
| OSSスクレイパー(snscrape等) | $0 | $0 | ゲストエンドポイント廃止後はほぼ動作せず、保守が絶えない |
snscrapeやtwikitのようなオープンソースのスクレイパーは、かつては定番でしたが、Xの仕様変更のたびに壊れる状態が続いています。ログイン型のものは実アカウントを使うため、アカウント制限のリスクも自分で抱えることになります。
本記事ではサードパーティAPIのGetXAPIを使います。筆者が開発に関わっているサービスなのでその点は割り引いて読んでいただきたいのですが、「Bearerキー1つで、コール単位の従量課金」というモデルはこの用途に素直にはまります。1コール$0.001で約20ツイートが返るため、1,000ツイートあたり約$0.05。登録時に$0.10の無料クレジット(約100コール、約2,000ツイート分)が付くので、この記事のコードは全部無料枠内で試せます。クレジットカードの登録は不要です。
セットアップ:30秒でAPIキーを取得する
- getxapi.com/signupでGoogleアカウントまたはメールで登録します
- ダッシュボードに表示されるBearerキーをコピーします
- 環境変数に入れます
export GETXAPI_KEY="コピーしたキー"
これで終わりです。開発者アカウントの作成もアプリの登録もOAuthの設定もありません。以降のすべてのリクエストは、この1つのキーをAuthorizationヘッダーに載せるだけです。
キーの管理について1点だけ。コードに直書きせず、必ず環境変数か秘密情報マネージャーから読んでください。公開リポジトリにキーをコミットしてしまった場合は、ダッシュボードから再発行(旧キーの無効化)ができます。
最初のリクエスト:ツイートを検索する
まずは動作確認を兼ねて、キーワードでツイートを検索します。依存はrequestsだけです。
pip install requests
import os
import requests
API_KEY = os.environ["GETXAPI_KEY"]
BASE_URL = "https://api.getxapi.com"
resp = requests.get(
f"{BASE_URL}/twitter/tweet/advanced_search",
params={"q": "生成AI lang:ja", "product": "Latest"},
headers={"Authorization": f"Bearer {API_KEY}"},
timeout=30,
)
resp.raise_for_status()
data = resp.json()
for tweet in data.get("tweets", [])[:5]:
print(tweet["text"][:80])
productパラメータは2種類あります。
-
Latest: 新着順。リアルタイム監視やアラート向き -
Top: エンゲージメント順。話題のツイートのダイジェスト向き
省略した場合はLatestです。
検索演算子:欲しいツイートだけを取る
qパラメータには、Xの高度な検索と同じ演算子がそのまま使えます。クライアント側でフィルタするのではなく、クエリの時点で絞り込むのがコスト面でも正解です。
| 演算子 | 意味 | 例 |
|---|---|---|
from:ユーザー名 |
特定アカウントの投稿 | from:nasa |
to:ユーザー名 |
特定アカウント宛の返信 | to:nasa |
min_faves:N |
いいねN件以上 | 決算 min_faves:100 |
min_retweets:N |
リポストN件以上 | 地震 min_retweets:50 |
since: / until:
|
日付範囲 | since:2026-01-01 until:2026-06-30 |
lang:ja |
日本語の投稿のみ | LLM lang:ja |
filter:verified |
認証済みアカウントのみ | セキュリティ filter:verified |
-filter:retweets |
リポストを除外 | 新製品 -filter:retweets |
組み合わせの例です。「日本語で、リポストを除き、いいね50件以上の生成AI関連ツイート」なら:
query = "生成AI lang:ja -filter:retweets min_faves:50"
ページネーション:1,000件を確実に集める
1コールで返るのは約20ツイートです。まとまった件数が必要な場合は、レスポンスに含まれるnext_cursorを次のリクエストに渡して繰り返します。
import os
import time
import requests
API_KEY = os.environ["GETXAPI_KEY"]
BASE_URL = "https://api.getxapi.com"
HEADERS = {"Authorization": f"Bearer {API_KEY}"}
def search_tweets(query: str, max_tweets: int = 1000) -> list[dict]:
"""クエリに一致するツイートを、カーソルを辿って集める。"""
tweets: list[dict] = []
cursor = None
while len(tweets) < max_tweets:
params = {"q": query, "product": "Latest"}
if cursor:
params["cursor"] = cursor
resp = requests.get(
f"{BASE_URL}/twitter/tweet/advanced_search",
params=params,
headers=HEADERS,
timeout=30,
)
if resp.status_code == 429:
# レスポンスのretry_afterに従う。なければ短い指数バックオフ
wait = int(resp.headers.get("retry-after", 0)) or 2
time.sleep(wait)
continue
resp.raise_for_status()
payload = resp.json()
batch = payload.get("tweets", [])
if not batch:
break
tweets.extend(batch)
cursor = payload.get("next_cursor")
if not cursor:
break
return tweets[:max_tweets]
if __name__ == "__main__":
results = search_tweets("生成AI lang:ja -filter:retweets", max_tweets=1000)
print(f"{len(results)}件取得")
1,000ツイートはおよそ50コール、コストにして約$0.05です。無料クレジットの範囲内に収まります。
ユーザー情報とフォロワーも同じキーで取れる
検索以外もエンドポイントを差し替えるだけです。
# プロフィール取得
resp = requests.get(
f"{BASE_URL}/twitter/user/info",
params={"userName": "nasa"},
headers=HEADERS,
timeout=30,
)
profile = resp.json()["data"]
print(profile["name"], profile["followers"])
# フォロワー一覧(こちらもカーソルでページネーション)
resp = requests.get(
f"{BASE_URL}/twitter/user/followers",
params={"userName": "nasa"},
headers=HEADERS,
timeout=30,
)
followers = resp.json()
ツイート詳細、リプライ、タイムライン、DM、リスト、トレンドなど、読み書き合わせて71のエンドポイントが同じBearerキーで使えます。全エンドポイントと単価は料金ページにまとまっています。
実践:収集したツイートをCSVに保存する
分析パイプラインの入り口としてよくある「検索してCSVに落とす」までを1本にまとめます。
import csv
import os
import time
import requests
API_KEY = os.environ["GETXAPI_KEY"]
BASE_URL = "https://api.getxapi.com"
HEADERS = {"Authorization": f"Bearer {API_KEY}"}
def collect_to_csv(query: str, path: str, max_tweets: int = 500) -> None:
tweets = []
cursor = None
while len(tweets) < max_tweets:
params = {"q": query, "product": "Latest"}
if cursor:
params["cursor"] = cursor
resp = requests.get(
f"{BASE_URL}/twitter/tweet/advanced_search",
params=params,
headers=HEADERS,
timeout=30,
)
if resp.status_code == 429:
time.sleep(int(resp.headers.get("retry-after", 0)) or 2)
continue
resp.raise_for_status()
payload = resp.json()
batch = payload.get("tweets", [])
if not batch:
break
tweets.extend(batch)
cursor = payload.get("next_cursor")
if not cursor:
break
with open(path, "w", newline="", encoding="utf-8") as f:
writer = csv.writer(f)
writer.writerow(["id", "created_at", "author", "text", "likes", "retweets"])
for t in tweets[:max_tweets]:
author = t.get("author") or {}
writer.writerow([
t.get("id"),
t.get("createdAt"),
author.get("userName"),
(t.get("text") or "").replace("\n", " "),
t.get("likeCount"),
t.get("retweetCount"),
])
print(f"{min(len(tweets), max_tweets)}件を{path}に保存しました")
if __name__ == "__main__":
collect_to_csv("決算 lang:ja min_faves:10", "tweets.csv", max_tweets=500)
このままpandasで読み込めば、頻度分析やセンチメント分析にそのまま進めます。
エラーハンドリング:実運用で当たる4つ
長時間動かすコードでは、次の4つを想定しておけば大半のトラブルは吸収できます。
| ステータス | 意味 | 対処 |
|---|---|---|
| 401 | キーの誤り・欠落・失効 | ダッシュボードでキーを確認して再設定 |
| 402 | クレジット残高切れ | 自動課金はされず、チャージするまで402が返るだけ |
| 429 | レート制限(サービス側のキャパシティ制御、またはX側のアカウント上限) |
retry_afterに従って待ってからリトライ |
| 5xx | 一時的なサーバーエラー | 短いバックオフでリトライ |
残高が切れても勝手に課金されない(402が返るだけ)という挙動は、試験運用では安心材料です。
レート制限について補足すると、エンドポイント別のクォータ(公式APIの「15分あたりN回」のような時間窓)はありません。ただし持続的な高並列では全体的なスロットリングとして429が返ることがあるので、上のコードのようにリトライパスは必ず用意してください。
コストの考え方:リクエスト単価とツイート単価を区別する
APIのコスト比較で混乱しがちなのが、「1リクエストいくら」と「ツイート1件いくら」の混同です。
- GetXAPI: 1コール$0.001で約20ツイート。ツイート単価は約$0.00005
- 公式X API: 返されたPost1件$0.005。ツイート単価がそのまま$0.005
同じ「1,000ツイートの読み取り」で並べると、約$0.05と$5になります。月10万ツイートを読むダッシュボードなら約$5と$500です。自分のボリュームで試算したい場合はコスト計算ツールが便利です。
なお、公式APIが適している場面もあります。エンドユーザーが自分のXアカウントでログインして操作を認可するOAuthのユーザー委任フローが必要なアプリや、プラットフォーム直結であることがコンプライアンス上必須のケースでは、公式API一択です。今回のような「公開データの収集・分析」がユースケースなら、サードパーティAPIの方が導入も運用も軽くなります。
まとめ
- 2026年の公式X APIは従量課金制で、一般向け無料枠の記載はなく、読み取りは返されたリソース単位($0.005/Post)で課金される
- 開発者アカウントなしでツイートを取得するなら、サードパーティAPIが現実的な選択肢
- GetXAPIなら登録30秒、Bearerキー1つ、1,000ツイート約$0.05。$0.10の無料クレジットで本記事のコードはすべて試せる
- 検索演算子で取得段階から絞り込み、カーソルでページネーション、429は
retry_afterでリトライ
関連リンク:
質問やつまずいた点があればコメントでどうぞ。





